From 9f5142738b1011d9ca20be4cde6bd9c9c104875c Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Sun, 6 Sep 2026 01:13:42 -0500 Subject: [PATCH] More physics work. New assets. --- .gitattributes | 1 + .gitignore | 2 + CHANGELOG | 9 +- CMakeLists.txt | 19 + INSTALL | 9 + LICENSES | 3 + assets/Dragon.mtl | 34 + assets/Dragon.obj | 1339 + assets/Dragon.svg | 79 + assets/DragonModel.glb | 3 + assets/DragonModel.mtl | 34 + assets/DragonModel.obj | 3155 +++ assets/DragonPrint-champagne.stl | 3 + assets/DragonPrint-dark.stl | 3 + assets/DragonPrint-frame.stl | 3 + assets/DragonPrint-glass.stl | 3 + assets/DragonPrint.mtl | 45 + assets/DragonPrint.obj | 9538 +++++++ assets/DragonRelief.glb | 3 + assets/Plaque-champagne.stl | 3 + assets/Plaque-dark.stl | 3 + assets/Plaque-frame.stl | 3 + assets/Plaque-glass.stl | 3 + assets/Plaque-plate.stl | 3 + assets/Plaque.mtl | 56 + assets/Plaque.obj | 7040 ++++++ assets/SingeText.glb | 3 + assets/SingeText.mtl | 12 + assets/SingeText.obj | 1735 ++ assets/SingeText.svg | 8 + assets/SingeTextPrint-face.stl | 3 + assets/SingeTextPrint-plate.stl | 3 + assets/SingeTextPrint-walls.stl | 3 + assets/SingeTextPrint.mtl | 34 + assets/SingeTextPrint.obj | 2617 ++ cmake/Superbuild.cmake | 7 +- cmake/hostTools.cmake | 66 + cmake/shaderHeader.cmake | 38 + docs/Manual.adoc | 114 +- src/model.c | 134 +- src/physics.h | 1 + src/physicsJolt.cpp | 15 + src/scene.c | 242 +- src/scene.h | 7 + src/shaders/build.sh | 61 - src/shaders/scene.hlsl | 56 +- src/shaders/sceneShaders.h | 4584 ---- src/singe.c | 84 +- testScripts/Models/AnimatedMorphCube.glb | 3 + testScripts/Models/DragonModel.glb | 3 + testScripts/Models/SingeText.glb | 3 + testScripts/README.md | 9 +- testScripts/crate.png | 3 + testScripts/games.dat | 54 + testScripts/scene12.singe | 70 + testScripts/scene13.singe | 52 + testScripts/scene14.singe | 48 + testScripts/scene15.singe | 48 + testScripts/scene16.singe | 54 + thirdparty/SDL_shadercross/CMakeLists.txt | 549 + thirdparty/SDL_shadercross/LICENSE.txt | 18 + .../cmake/CPackProjectConfig.cmake.in | 37 + .../cmake/FindDirectXShaderCompiler.cmake | 60 + .../cmake/GetGitRevisionDescription.cmake | 284 + .../cmake/GetGitRevisionDescription.cmake.in | 43 + .../cmake/PrivateSdlFunctions.cmake | 363 + .../cmake/SDL3_shadercrossConfig.cmake.in | 76 + .../cmake/sdl3-shadercross.pc.in | 13 + thirdparty/SDL_shadercross/cmake/sdlcpu.cmake | 158 + .../SDL_shadercross/cmake/sdlmanpages.cmake | 68 + .../SDL_shadercross/cmake/sdlplatform.cmake | 106 + .../SDL3_shadercross/SDL_shadercross.h | 356 + .../SDL_shadercross/src/SDL_shadercross.c | 2699 ++ .../SDL_shadercross/src/SDL_shadercross.sym | 19 + thirdparty/SDL_shadercross/src/cli.c | 824 + thirdparty/SDL_shadercross/src/version.rc | 38 + thirdparty/SPIRV-Cross/CMakeLists.txt | 659 + thirdparty/SPIRV-Cross/GLSL.std.450.h | 114 + thirdparty/SPIRV-Cross/LICENSE | 202 + .../SPIRV-Cross/LICENSES/Apache-2.0.txt | 208 + thirdparty/SPIRV-Cross/LICENSES/CC-BY-4.0.txt | 324 + .../LICENSES/LicenseRef-KhronosFreeUse.txt | 23 + thirdparty/SPIRV-Cross/LICENSES/MIT.txt | 19 + .../NonSemanticShaderDebugInfo100.h | 171 + thirdparty/SPIRV-Cross/README.md | 558 + thirdparty/SPIRV-Cross/cmake/gitversion.in.h | 9 + .../include/spirv_cross/barrier.hpp | 80 + .../include/spirv_cross/external_interface.h | 127 + .../SPIRV-Cross/include/spirv_cross/image.hpp | 63 + .../spirv_cross/internal_interface.hpp | 604 + .../include/spirv_cross/sampler.hpp | 106 + .../include/spirv_cross/thread_group.hpp | 114 + thirdparty/SPIRV-Cross/main.cpp | 2099 ++ .../pkg-config/spirv-cross-c-shared.pc.in | 16 + .../pkg-config/spirv-cross-c.pc.in | 15 + thirdparty/SPIRV-Cross/spirv.h | 5602 +++++ thirdparty/SPIRV-Cross/spirv.hpp | 5667 +++++ thirdparty/SPIRV-Cross/spirv_cfg.cpp | 525 + thirdparty/SPIRV-Cross/spirv_cfg.hpp | 168 + thirdparty/SPIRV-Cross/spirv_common.hpp | 2096 ++ thirdparty/SPIRV-Cross/spirv_cpp.cpp | 553 + thirdparty/SPIRV-Cross/spirv_cpp.hpp | 93 + thirdparty/SPIRV-Cross/spirv_cross.cpp | 6061 +++++ thirdparty/SPIRV-Cross/spirv_cross.hpp | 1264 + thirdparty/SPIRV-Cross/spirv_cross_c.cpp | 2984 +++ thirdparty/SPIRV-Cross/spirv_cross_c.h | 1130 + .../SPIRV-Cross/spirv_cross_containers.hpp | 756 + .../spirv_cross_error_handling.hpp | 99 + .../SPIRV-Cross/spirv_cross_parsed_ir.cpp | 1149 + .../SPIRV-Cross/spirv_cross_parsed_ir.hpp | 273 + thirdparty/SPIRV-Cross/spirv_cross_util.cpp | 77 + thirdparty/SPIRV-Cross/spirv_cross_util.hpp | 37 + thirdparty/SPIRV-Cross/spirv_glsl.cpp | 20865 +++++++++++++++ thirdparty/SPIRV-Cross/spirv_glsl.hpp | 1132 + thirdparty/SPIRV-Cross/spirv_hlsl.cpp | 7320 ++++++ thirdparty/SPIRV-Cross/spirv_hlsl.hpp | 425 + thirdparty/SPIRV-Cross/spirv_msl.cpp | 20918 ++++++++++++++++ thirdparty/SPIRV-Cross/spirv_msl.hpp | 1440 ++ thirdparty/SPIRV-Cross/spirv_parser.cpp | 1610 ++ thirdparty/SPIRV-Cross/spirv_parser.hpp | 103 + thirdparty/SPIRV-Cross/spirv_reflect.cpp | 721 + thirdparty/SPIRV-Cross/spirv_reflect.hpp | 92 + util/dragonModel.py | 320 + util/dragonObj.py | 122 + util/objToGlb.py | 165 + util/objWriter.py | 183 + util/outline.py | 191 + util/plaqueModel.py | 75 + util/previewObj.py | 149 + util/textModel.py | 176 + util/traceDragon.py | 224 + util/traceText.py | 59 + 132 files changed, 122928 insertions(+), 4688 deletions(-) create mode 100644 assets/Dragon.mtl create mode 100644 assets/Dragon.obj create mode 100644 assets/Dragon.svg create mode 100644 assets/DragonModel.glb create mode 100644 assets/DragonModel.mtl create mode 100644 assets/DragonModel.obj create mode 100644 assets/DragonPrint-champagne.stl create mode 100644 assets/DragonPrint-dark.stl create mode 100644 assets/DragonPrint-frame.stl create mode 100644 assets/DragonPrint-glass.stl create mode 100644 assets/DragonPrint.mtl create mode 100644 assets/DragonPrint.obj create mode 100644 assets/DragonRelief.glb create mode 100644 assets/Plaque-champagne.stl create mode 100644 assets/Plaque-dark.stl create mode 100644 assets/Plaque-frame.stl create mode 100644 assets/Plaque-glass.stl create mode 100644 assets/Plaque-plate.stl create mode 100644 assets/Plaque.mtl create mode 100644 assets/Plaque.obj create mode 100644 assets/SingeText.glb create mode 100644 assets/SingeText.mtl create mode 100644 assets/SingeText.obj create mode 100644 assets/SingeText.svg create mode 100644 assets/SingeTextPrint-face.stl create mode 100644 assets/SingeTextPrint-plate.stl create mode 100644 assets/SingeTextPrint-walls.stl create mode 100644 assets/SingeTextPrint.mtl create mode 100644 assets/SingeTextPrint.obj create mode 100644 cmake/hostTools.cmake create mode 100644 cmake/shaderHeader.cmake delete mode 100755 src/shaders/build.sh delete mode 100644 src/shaders/sceneShaders.h create mode 100644 testScripts/Models/AnimatedMorphCube.glb create mode 100644 testScripts/Models/DragonModel.glb create mode 100644 testScripts/Models/SingeText.glb create mode 100644 testScripts/crate.png create mode 100644 testScripts/scene12.singe create mode 100644 testScripts/scene13.singe create mode 100644 testScripts/scene14.singe create mode 100644 testScripts/scene15.singe create mode 100644 testScripts/scene16.singe create mode 100644 thirdparty/SDL_shadercross/CMakeLists.txt create mode 100644 thirdparty/SDL_shadercross/LICENSE.txt create mode 100644 thirdparty/SDL_shadercross/cmake/CPackProjectConfig.cmake.in create mode 100644 thirdparty/SDL_shadercross/cmake/FindDirectXShaderCompiler.cmake create mode 100644 thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake create mode 100644 thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake.in create mode 100644 thirdparty/SDL_shadercross/cmake/PrivateSdlFunctions.cmake create mode 100644 thirdparty/SDL_shadercross/cmake/SDL3_shadercrossConfig.cmake.in create mode 100644 thirdparty/SDL_shadercross/cmake/sdl3-shadercross.pc.in create mode 100644 thirdparty/SDL_shadercross/cmake/sdlcpu.cmake create mode 100644 thirdparty/SDL_shadercross/cmake/sdlmanpages.cmake create mode 100644 thirdparty/SDL_shadercross/cmake/sdlplatform.cmake create mode 100644 thirdparty/SDL_shadercross/include/SDL3_shadercross/SDL_shadercross.h create mode 100644 thirdparty/SDL_shadercross/src/SDL_shadercross.c create mode 100644 thirdparty/SDL_shadercross/src/SDL_shadercross.sym create mode 100644 thirdparty/SDL_shadercross/src/cli.c create mode 100644 thirdparty/SDL_shadercross/src/version.rc create mode 100644 thirdparty/SPIRV-Cross/CMakeLists.txt create mode 100644 thirdparty/SPIRV-Cross/GLSL.std.450.h create mode 100644 thirdparty/SPIRV-Cross/LICENSE create mode 100644 thirdparty/SPIRV-Cross/LICENSES/Apache-2.0.txt create mode 100644 thirdparty/SPIRV-Cross/LICENSES/CC-BY-4.0.txt create mode 100644 thirdparty/SPIRV-Cross/LICENSES/LicenseRef-KhronosFreeUse.txt create mode 100644 thirdparty/SPIRV-Cross/LICENSES/MIT.txt create mode 100644 thirdparty/SPIRV-Cross/NonSemanticShaderDebugInfo100.h create mode 100644 thirdparty/SPIRV-Cross/README.md create mode 100644 thirdparty/SPIRV-Cross/cmake/gitversion.in.h create mode 100644 thirdparty/SPIRV-Cross/include/spirv_cross/barrier.hpp create mode 100644 thirdparty/SPIRV-Cross/include/spirv_cross/external_interface.h create mode 100644 thirdparty/SPIRV-Cross/include/spirv_cross/image.hpp create mode 100644 thirdparty/SPIRV-Cross/include/spirv_cross/internal_interface.hpp create mode 100644 thirdparty/SPIRV-Cross/include/spirv_cross/sampler.hpp create mode 100644 thirdparty/SPIRV-Cross/include/spirv_cross/thread_group.hpp create mode 100644 thirdparty/SPIRV-Cross/main.cpp create mode 100644 thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c-shared.pc.in create mode 100644 thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c.pc.in create mode 100644 thirdparty/SPIRV-Cross/spirv.h create mode 100644 thirdparty/SPIRV-Cross/spirv.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cfg.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cfg.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_common.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cpp.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cpp.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_c.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_c.h create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_containers.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_error_handling.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_util.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_cross_util.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_glsl.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_glsl.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_hlsl.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_hlsl.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_msl.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_msl.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_parser.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_parser.hpp create mode 100644 thirdparty/SPIRV-Cross/spirv_reflect.cpp create mode 100644 thirdparty/SPIRV-Cross/spirv_reflect.hpp create mode 100644 util/dragonModel.py create mode 100644 util/dragonObj.py create mode 100644 util/objToGlb.py create mode 100644 util/objWriter.py create mode 100644 util/outline.py create mode 100644 util/plaqueModel.py create mode 100644 util/previewObj.py create mode 100644 util/textModel.py create mode 100644 util/traceDragon.py create mode 100644 util/traceText.py diff --git a/.gitattributes b/.gitattributes index 80046a82c..e1f831381 100644 --- a/.gitattributes +++ b/.gitattributes @@ -87,3 +87,4 @@ menu.dat !filter !diff !merge text # Model # *.glb filter=lfs diff=lfs merge=lfs -text +*.stl filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 8f40fc011..170ab7fc5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ docs/Manual.html docs/Manual.pdf docs/.asciidoctor/ .claude/settings.local.json +**/__pycache__/ +/util/perl/ # Extracted by the binary when it is run from here; never source. /Singe/ diff --git a/CHANGELOG b/CHANGELOG index abfb7aae9..25a042d91 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -25,7 +25,10 @@ API Changes velocities, forces and impulses; triggers; hinge, ball and slider joints with limits; a raycast; onCollision and onTrigger callbacks. The world steps at a fixed 60 Hz between animation and rendering and - pauses with the game. New calls: body*, joint*, physics*, and the + pauses with the game. physicsSet2D keeps bodies in a plane for 2D + games, which need no GPU (nodes now exist on every machine, so a + Raspberry Pi 3 gets 2D physics without 3D). New calls: body*, + joint*, physics*, and the BODY_*, SHAPE_* and JOINT_* constants; see the Physics chapter. Needs SSE4.1 on x86 (2008 and later); the engine's first C++ (one wrapper file and the library), statically linked, no runtime to install. @@ -35,8 +38,8 @@ API Changes colour, textures from sprites, the disc or a loaded video, metallic and roughness, up to eight lights each able to cast shadows (cube maps for point lights), any node as the camera, and glTF 2.0 - models (.glb, self-contained) with node animation and skinning, placed - any number of times. Everything is a node in one tree. New calls: + models (.glb, self-contained) with node animation, skinning and morph + targets, placed any number of times. Everything is a node in one tree. New calls: scene*, node*, mesh*, material*, light*, camera*, model*, animation*, and the LIGHT_* constants; see the 3D Scenes chapter of the manual. Needs a GPU with Vulkan, Direct3D 12 or Metal (Raspberry Pi 4 or diff --git a/CMakeLists.txt b/CMakeLists.txt index cc8cd8193..d6e3019d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -624,6 +624,25 @@ foreach(imageLib libjpeg.a libpng16.a) endif() endforeach() +# The scene shaders: src/shaders/scene.hlsl compiled at build time into a header of SPIR-V, DXIL +# and MSL blobs by SDL_shadercross, which the superbuild builds for the host (cmake/hostTools.cmake) +# and names in SINGE_SHADERCROSS; configuring this project alone finds one on the PATH. +if(NOT SINGE_SHADERCROSS) + find_program(SINGE_SHADERCROSS shadercross) + if(NOT SINGE_SHADERCROSS) + message(FATAL_ERROR "SDL_shadercross is needed to compile the scene shaders; build through the superbuild (cmake --preset ...) or set SINGE_SHADERCROSS.") + endif() +endif() +set(shaderHeader ${CMAKE_BINARY_DIR}/generated/shaders/sceneShaders.h) +add_custom_command( + OUTPUT ${shaderHeader} + COMMAND ${CMAKE_COMMAND} -DSHADERCROSS=${SINGE_SHADERCROSS} -DSOURCE=${CMAKE_SOURCE_DIR}/src/shaders/scene.hlsl -DOUTPUT=${shaderHeader} -P ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake + DEPENDS ${CMAKE_SOURCE_DIR}/src/shaders/scene.hlsl ${CMAKE_SOURCE_DIR}/cmake/shaderHeader.cmake + COMMENT "Compiling the scene shaders" +) +target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${shaderHeader}) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_BINARY_DIR}/generated) + # Jolt Physics comes with the compile definitions it was built with (SIMD level, layer bits) through # its exported target; the one C++ file in the tree (physicsJolt.cpp) needs them. Its C++ runtime is # zig's own libc++, linked statically, or the host's libstdc++ under gcc; macOS already lists -lc++. diff --git a/INSTALL b/INSTALL index ff1512559..911d8ac5f 100644 --- a/INSTALL +++ b/INSTALL @@ -96,6 +96,15 @@ builds the Apple silicon and Intel presets (macos-aarch64, macos-x86_64) and joins them with llvm-lipo into Singe-v3.00-Macos-universal. Either architecture alone is ./build-all.sh macos aarch64 or x86_64. +The 3D scene's shaders are HLSL, compiled when Singe is built. The +superbuild builds SDL_shadercross and SPIRV-Cross for the host machine +once (into .builddir/toolchains/host, shared by every preset) and fetches +Microsoft's prebuilt DirectX Shader Compiler, which SDL_shadercross +drives, into .builddir/toolchains/dxc. That prebuilt package exists for +x86_64 Linux hosts; on another host, unpack a DirectX Shader Compiler +release (lib/libdxcompiler.so, lib/libdxil.so, include/dxc/dxcapi.h) at +that path yourself, or build it from source there. + The Raspberry Pi build (64-bit Raspberry Pi OS, glibc 2.31 or newer) also uses zig. The platform headers and libraries it links against are Debian bookworm arm64 packages listed in cmake/zig/piPackages.cmake, diff --git a/LICENSES b/LICENSES index be65a137f..0848d0d61 100644 --- a/LICENSES +++ b/LICENSES @@ -6,6 +6,7 @@ these tools, Singe would not exist. arg_parser BSD-2-Clause http://savannah.nongnu.org/projects/arg-parser binaryheap.lua MIT http://tieske.github.io/binaryheap.lua cgltf MIT https://github.com/jkuhlmann/cgltf +DirectXShaderCompiler NCSA https://github.com/microsoft/DirectXShaderCompiler (prebuilt, downloaded at build time; host tool only) copas MIT https://lunarmodules.github.io/copas ffmpeg LGPL-2.1 https://ffmpeg.org freetype FTL https://freetype.org @@ -27,9 +28,11 @@ openssl Apache-2.0 https://www.openssl.org opus BSD-3-Clause https://opus-codec.org opusfile BSD-3-Clause https://opus-codec.org SDL3 Zlib https://www.libsdl.org +SDL_shadercross Zlib https://github.com/libsdl-org/SDL_shadercross (host tool only) SDL3_image Zlib https://www.libsdl.org SDL3_mixer Zlib https://www.libsdl.org SDL3_ttf Zlib https://www.libsdl.org +SPIRV-Cross Apache-2.0 https://github.com/KhronosGroup/SPIRV-Cross (host tool only) sqlite Public-Domain https://sqlite.org timerwheel.lua MIT https://tieske.github.io/timerwheel.lua uthash BSD-1-Clause https://troydhanson.github.io/uthash diff --git a/assets/Dragon.mtl b/assets/Dragon.mtl new file mode 100644 index 000000000..0eacd58c4 --- /dev/null +++ b/assets/Dragon.mtl @@ -0,0 +1,34 @@ +# Materials for Dragon.obj +newmtl dark +Ka 0.060 0.062 0.066 +Kd 0.300 0.310 0.330 +Ks 0.700 0.700 0.700 +Ns 180.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.35 + +newmtl champagne +Ka 0.144 0.128 0.108 +Kd 0.720 0.640 0.540 +Ks 0.850 0.800 0.700 +Ns 120.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.40 + +newmtl glass +Ka 0.176 0.184 0.180 +Kd 0.880 0.920 0.900 +Ks 0.950 0.950 0.950 +Ns 300.0 +d 0.55 +Ni 1.50 +illum 4 +Pm 0.00 +Pr 0.15 + diff --git a/assets/Dragon.obj b/assets/Dragon.obj new file mode 100644 index 000000000..69b7f2f0e --- /dev/null +++ b/assets/Dragon.obj @@ -0,0 +1,1339 @@ +# Singe dragon, extruded from the tiles of Dragon.jpeg by util/dragonObj.py +# Units: 0.01 per image pixel; 34 tiles; Y up, front faces toward +Z, resting on Y = 0 +mtllib Dragon.mtl +v -0.85000 7.13840 0.00000 +v 0.02790 6.26060 0.00000 +v 1.40680 6.21220 0.00000 +v -0.85000 7.13840 0.07000 +v 0.02790 6.26060 0.07000 +v 1.40680 6.21220 0.07000 +v -0.72302 7.05386 0.10000 +v 0.04076 6.29017 0.10000 +v 1.24041 6.24806 0.10000 +v -3.34790 6.35520 0.00000 +v -2.33020 5.07750 0.00000 +v -1.17830 4.56320 0.00000 +v -0.64280 5.01890 0.00000 +v -3.34790 6.35520 0.07000 +v -2.33020 5.07750 0.07000 +v -1.17830 4.56320 0.07000 +v -0.64280 5.01890 0.07000 +v -3.24072 6.26880 0.10000 +v -2.31129 5.10191 0.10000 +v -1.18334 4.59830 0.10000 +v -0.69697 5.01220 0.10000 +v 2.17370 5.96880 0.00000 +v 2.50350 6.22000 0.00000 +v 1.87540 6.22000 0.00000 +v 2.17370 5.96880 0.07000 +v 2.50350 6.22000 0.07000 +v 1.87540 6.22000 0.07000 +v 2.17464 6.00723 0.10000 +v 2.41460 6.19000 0.10000 +v 1.95760 6.19000 0.10000 +v 2.04140 5.58350 0.00000 +v 1.48390 5.01340 0.00000 +v 2.49750 5.22880 0.00000 +v 2.04140 5.58350 0.07000 +v 1.48390 5.01340 0.07000 +v 2.49750 5.22880 0.07000 +v 2.04412 5.54338 0.10000 +v 1.57473 5.06337 0.10000 +v 2.42815 5.24473 0.10000 +v 0.67430 5.14050 0.00000 +v 0.35930 4.67380 0.00000 +v 0.68030 3.96970 0.00000 +v 1.92250 2.27100 0.00000 +v 1.94530 3.33230 0.00000 +v 0.67430 5.14050 0.07000 +v 0.35930 4.67380 0.07000 +v 0.68030 3.96970 0.07000 +v 1.92250 2.27100 0.07000 +v 1.94530 3.33230 0.07000 +v 0.67480 5.08762 0.10000 +v 0.39357 4.67095 0.10000 +v 0.70633 3.98493 0.10000 +v 1.89441 2.36024 0.10000 +v 1.91510 3.32310 0.10000 +v -2.22390 0.69780 0.00000 +v -0.97620 1.99300 0.00000 +v -1.55650 3.09790 0.00000 +v -2.22390 0.69780 0.07000 +v -0.97620 1.99300 0.07000 +v -1.55650 3.09790 0.07000 +v -2.16322 0.80403 0.10000 +v -1.01283 1.99822 0.10000 +v -1.54787 3.01695 0.10000 +v 1.95750 1.44360 0.00000 +v 2.45110 0.90300 0.00000 +v 2.90950 2.25450 0.00000 +v 1.95750 1.44360 0.07000 +v 2.45110 0.90300 0.07000 +v 2.90950 2.25450 0.07000 +v 2.00059 1.44090 0.10000 +v 2.43901 0.96074 0.10000 +v 2.84615 2.16113 0.10000 +v -1.51280 0.11360 0.00000 +v -3.48900 1.46210 0.00000 +v -3.16650 0.16090 0.00000 +v -1.51280 0.11360 0.07000 +v -3.48900 1.46210 0.07000 +v -3.16650 0.16090 0.07000 +v -1.61426 0.14651 0.10000 +v -3.44097 1.39300 0.10000 +v -3.14286 0.19024 0.10000 +v 2.89000 0.18030 0.00000 +v 3.53350 0.97590 0.00000 +v 2.45710 0.90570 0.00000 +v 2.89000 0.18030 0.07000 +v 3.53350 0.97590 0.07000 +v 2.45710 0.90570 0.07000 +v 2.89372 0.23261 0.10000 +v 3.46710 0.94151 0.10000 +v 2.50800 0.87896 0.10000 +v 2.97380 0.08820 0.00000 +v 2.55590 0.69000 0.00000 +v 1.89060 0.69000 0.00000 +v 2.97380 0.08820 0.07000 +v 2.55590 0.69000 0.07000 +v 1.89060 0.69000 0.07000 +v 2.87553 0.17711 0.10000 +v 2.54021 0.66000 0.10000 +v 2.00637 0.66000 0.10000 +v 1.40490 6.63320 0.00000 +v 0.15500 7.40240 0.00000 +v 0.63260 6.70180 0.00000 +v 1.40490 6.63320 0.07000 +v 0.15500 7.40240 0.07000 +v 0.63260 6.70180 0.07000 +v 1.28081 6.67434 0.10000 +v 0.25892 7.30322 0.10000 +v 0.64939 6.73043 0.10000 +v 3.32850 5.52760 0.00000 +v 3.52140 5.91340 0.00000 +v 3.21270 6.35630 0.00000 +v 3.17420 5.78000 0.00000 +v 3.32850 5.52760 0.07000 +v 3.52140 5.91340 0.07000 +v 3.21270 6.35630 0.07000 +v 3.17420 5.78000 0.07000 +v 3.32587 5.58942 0.10000 +v 3.48659 5.91087 0.10000 +v 3.23694 6.26906 0.10000 +v 3.20477 5.78751 0.10000 +v 1.81500 6.25020 0.00000 +v 1.36960 6.20970 0.00000 +v 2.24650 5.61750 0.00000 +v 3.18500 5.80730 0.00000 +v 3.18500 6.12660 0.00000 +v 1.81500 6.25020 0.07000 +v 1.36960 6.20970 0.07000 +v 2.24650 5.61750 0.07000 +v 3.18500 5.80730 0.07000 +v 3.18500 6.12660 0.07000 +v 1.81501 6.22008 0.10000 +v 1.45616 6.18745 0.10000 +v 2.25287 5.64940 0.10000 +v 3.15500 5.83184 0.10000 +v 3.15500 6.09918 0.10000 +v 0.07120 4.35890 0.00000 +v 1.30060 6.17980 0.00000 +v 0.18720 5.64620 0.00000 +v 0.07120 4.35890 0.07000 +v 1.30060 6.17980 0.07000 +v 0.18720 5.64620 0.07000 +v 0.11154 4.47226 0.10000 +v 1.21388 6.10497 0.10000 +v 0.21555 5.62652 0.10000 +v 3.29410 5.31780 0.00000 +v 2.04850 5.59590 0.00000 +v 3.02810 4.79770 0.00000 +v 3.29410 5.31780 0.07000 +v 2.04850 5.59590 0.07000 +v 3.02810 4.79770 0.07000 +v 3.24975 5.29696 0.10000 +v 2.16588 5.53895 0.10000 +v 3.01829 4.84439 0.10000 +v 1.77750 3.63490 0.00000 +v 2.36480 2.64170 0.00000 +v 2.78390 3.34390 0.00000 +v 1.47720 4.90530 0.00000 +v 1.77750 3.63490 0.07000 +v 2.36480 2.64170 0.07000 +v 2.78390 3.34390 0.07000 +v 1.47720 4.90530 0.07000 +v 1.80565 3.64624 0.10000 +v 2.36492 2.70044 0.10000 +v 2.74722 3.34098 0.10000 +v 1.53556 4.78882 0.10000 +v 2.74490 3.35780 0.00000 +v 2.33820 2.62830 0.00000 +v 2.68710 2.09930 0.00000 +v 2.92820 2.31500 0.00000 +v 2.74490 3.35780 0.07000 +v 2.33820 2.62830 0.07000 +v 2.68710 2.09930 0.07000 +v 2.92820 2.31500 0.07000 +v 2.72998 3.26942 0.10000 +v 2.37328 2.62961 0.10000 +v 2.69300 2.14484 0.10000 +v 2.89577 2.32624 0.10000 +v 2.68730 2.09100 0.00000 +v 1.97310 3.26600 0.00000 +v 1.99610 1.52660 0.00000 +v 2.68730 2.09100 0.07000 +v 1.97310 3.26600 0.07000 +v 1.99610 1.52660 0.07000 +v 2.64810 2.09773 0.10000 +v 2.00455 3.15650 0.10000 +v 2.02528 1.58915 0.10000 +v -4.23430 2.88360 0.00000 +v -4.35570 1.38990 0.00000 +v -3.79710 2.10640 0.00000 +v -4.23430 2.88360 0.07000 +v -4.35570 1.38990 0.07000 +v -3.79710 2.10640 0.07000 +v -4.21235 2.78338 0.10000 +v -4.31767 1.48747 0.10000 +v -3.83304 2.10910 0.10000 +v 0.92450 0.79230 0.00000 +v 2.56980 0.69430 0.00000 +v 0.94630 2.50300 0.00000 +v 0.92450 0.79230 0.07000 +v 2.56980 0.69430 0.07000 +v 0.94630 2.50300 0.07000 +v 0.95486 0.82054 0.10000 +v 2.49871 0.72859 0.10000 +v 0.97532 2.42576 0.10000 +v -4.34800 1.31620 0.00000 +v -3.27470 0.25420 0.00000 +v -3.71540 2.15230 0.00000 +v -4.34800 1.31620 0.07000 +v -3.27470 0.25420 0.07000 +v -3.71540 2.15230 0.07000 +v -4.30823 1.31905 0.10000 +v -3.32741 0.34855 0.10000 +v -3.73013 2.08311 0.10000 +v 4.35770 0.12090 0.00000 +v 3.58310 0.99230 0.00000 +v 2.95590 0.19410 0.00000 +v 4.35770 0.12090 0.07000 +v 3.58310 0.99230 0.07000 +v 2.95590 0.19410 0.07000 +v 4.28760 0.15460 0.10000 +v 3.58452 0.94555 0.10000 +v 3.01522 0.22104 0.10000 +v 0.23300 0.10780 0.00000 +v -0.49920 0.97510 0.00000 +v -1.55810 0.15280 0.00000 +v 0.23300 0.10780 0.07000 +v -0.49920 0.97510 0.07000 +v -1.55810 0.15280 0.07000 +v 0.16700 0.13947 0.10000 +v -0.50355 0.93374 0.10000 +v -1.47329 0.18068 0.10000 +v 2.37220 6.26650 0.00000 +v 1.92740 6.54950 0.00000 +v 0.75300 6.59700 0.00000 +v 1.45590 6.21950 0.00000 +v 2.37220 6.26650 0.07000 +v 1.92740 6.54950 0.07000 +v 0.75300 6.59700 0.07000 +v 1.45590 6.21950 0.07000 +v 2.27679 6.29165 0.10000 +v 1.91811 6.51985 0.10000 +v 0.88203 6.56176 0.10000 +v 1.46272 6.24989 0.10000 +v 0.73630 5.17900 0.00000 +v 0.92890 4.91420 0.00000 +v 1.44610 5.05430 0.00000 +v 2.01640 5.64610 0.00000 +v 1.35430 6.10610 0.00000 +v 0.73630 5.17900 0.07000 +v 0.92890 4.91420 0.07000 +v 1.44610 5.05430 0.07000 +v 2.01640 5.64610 0.07000 +v 1.35430 6.10610 0.07000 +v 0.77285 5.17975 0.10000 +v 0.94100 4.94856 0.10000 +v 1.43025 5.08109 0.10000 +v 1.97036 5.64156 0.10000 +v 1.36230 6.06401 0.10000 +v -2.23140 3.70200 0.00000 +v -1.27090 4.52060 0.00000 +v -2.62000 5.12710 0.00000 +v -3.55740 5.46270 0.00000 +v -2.23140 3.70200 0.07000 +v -1.27090 4.52060 0.07000 +v -2.62000 5.12710 0.07000 +v -3.55740 5.46270 0.07000 +v -2.22661 3.74550 0.10000 +v -1.32644 4.51268 0.10000 +v -2.63122 5.09925 0.10000 +v -3.47312 5.40066 0.10000 +v 0.96360 4.82790 0.00000 +v 1.60430 3.91270 0.00000 +v 1.34280 4.98480 0.00000 +v 0.96360 4.82790 0.07000 +v 1.60430 3.91270 0.07000 +v 1.34280 4.98480 0.07000 +v 1.00962 4.81447 0.10000 +v 1.53733 4.06068 0.10000 +v 1.32194 4.94370 0.10000 +v 0.43030 3.14100 0.00000 +v -0.62440 4.93070 0.00000 +v -2.17380 3.64850 0.00000 +v -0.17160 3.21050 0.00000 +v 0.43030 3.14100 0.07000 +v -0.62440 4.93070 0.07000 +v -2.17380 3.64850 0.07000 +v -0.17160 3.21050 0.07000 +v 0.37384 3.17772 0.10000 +v -0.63238 4.88515 0.10000 +v -2.10723 3.66465 0.10000 +v -0.16666 3.24013 0.10000 +v -4.12680 2.91050 0.00000 +v -3.71260 2.17180 0.00000 +v -3.11790 2.83140 0.00000 +v -2.91480 3.84710 0.00000 +v -4.12680 2.91050 0.07000 +v -3.71260 2.17180 0.07000 +v -3.11790 2.83140 0.07000 +v -2.91480 3.84710 0.07000 +v -4.08797 2.90259 0.10000 +v -3.70688 2.22294 0.10000 +v -3.14570 2.84537 0.10000 +v -2.95995 3.77429 0.10000 +v 0.81590 3.69070 0.00000 +v 0.54830 3.06250 0.00000 +v 1.87920 1.55060 0.00000 +v 1.91580 2.17190 0.00000 +v 0.81590 3.69070 0.07000 +v 0.54830 3.06250 0.07000 +v 1.87920 1.55060 0.07000 +v 1.91580 2.17190 0.07000 +v 0.82271 3.63014 0.10000 +v 0.58331 3.06813 0.10000 +v 1.85354 1.62515 0.10000 +v 1.88522 2.16298 0.10000 +v -1.53380 3.20620 0.00000 +v -0.98000 2.13170 0.00000 +v 0.37130 3.04000 0.00000 +v -1.53380 3.20620 0.07000 +v -0.98000 2.13170 0.07000 +v 0.37130 3.04000 0.07000 +v -1.48221 3.17159 0.10000 +v -0.96877 2.17540 0.10000 +v 0.28405 3.01750 0.10000 +v -1.64230 3.11290 0.00000 +v -2.54070 2.27140 0.00000 +v -2.84090 1.14310 0.00000 +v -2.25050 0.73440 0.00000 +v -1.64230 3.11290 0.07000 +v -2.54070 2.27140 0.07000 +v -2.84090 1.14310 0.07000 +v -2.25050 0.73440 0.07000 +v -1.69684 3.02071 0.10000 +v -2.51392 2.25538 0.10000 +v -2.80649 1.15576 0.10000 +v -2.26888 0.78361 0.10000 +v 0.83520 2.59470 0.00000 +v 0.51260 2.96690 0.00000 +v 0.05410 2.72420 0.00000 +v -0.90340 2.05080 0.00000 +v 0.81410 0.78140 0.00000 +v 0.83520 2.59470 0.07000 +v 0.51260 2.96690 0.07000 +v 0.05410 2.72420 0.07000 +v -0.90340 2.05080 0.07000 +v 0.81410 0.78140 0.07000 +v 0.80507 2.58366 0.10000 +v 0.50555 2.92923 0.10000 +v 0.06981 2.69857 0.10000 +v -0.85211 2.05020 0.10000 +v 0.78478 0.84037 0.10000 +v -1.54470 0.24930 0.00000 +v -0.58470 1.00780 0.00000 +v -2.16090 0.72330 0.00000 +v -1.54470 0.24930 0.07000 +v -0.58470 1.00780 0.07000 +v -2.16090 0.72330 0.07000 +v -1.54495 0.28734 0.10000 +v -0.69743 0.95697 0.10000 +v -2.08895 0.70580 0.10000 +vt 0.40439 0.95306 +vt 0.50314 0.83586 +vt 0.65825 0.82940 +vt 0.40439 0.95306 +vt 0.50314 0.83586 +vt 0.65825 0.82940 +vt 0.41867 0.94177 +vt 0.50458 0.83981 +vt 0.63953 0.83419 +vt 0.12341 0.84849 +vt 0.23789 0.67790 +vt 0.36746 0.60924 +vt 0.42769 0.67008 +vt 0.12341 0.84849 +vt 0.23789 0.67790 +vt 0.36746 0.60924 +vt 0.42769 0.67008 +vt 0.13546 0.83696 +vt 0.24001 0.68116 +vt 0.36689 0.61393 +vt 0.42160 0.66919 +vt 0.74451 0.79690 +vt 0.78161 0.83044 +vt 0.71096 0.83044 +vt 0.74451 0.79690 +vt 0.78161 0.83044 +vt 0.71096 0.83044 +vt 0.74462 0.80203 +vt 0.77161 0.82644 +vt 0.72020 0.82644 +vt 0.72963 0.74546 +vt 0.66692 0.66935 +vt 0.78093 0.69810 +vt 0.72963 0.74546 +vt 0.66692 0.66935 +vt 0.78093 0.69810 +vt 0.72994 0.74010 +vt 0.67713 0.67602 +vt 0.77313 0.70023 +vt 0.57585 0.68632 +vt 0.54042 0.62401 +vt 0.57652 0.53000 +vt 0.71625 0.30320 +vt 0.71882 0.44490 +vt 0.57585 0.68632 +vt 0.54042 0.62401 +vt 0.57652 0.53000 +vt 0.71625 0.30320 +vt 0.71882 0.44490 +vt 0.57591 0.67925 +vt 0.54427 0.62362 +vt 0.57945 0.53203 +vt 0.71309 0.31512 +vt 0.71542 0.44367 +vt 0.24984 0.09316 +vt 0.39019 0.26609 +vt 0.32492 0.41360 +vt 0.24984 0.09316 +vt 0.39019 0.26609 +vt 0.32492 0.41360 +vt 0.25667 0.10735 +vt 0.38607 0.26679 +vt 0.32589 0.40280 +vt 0.72019 0.19274 +vt 0.77571 0.12056 +vt 0.82728 0.30100 +vt 0.72019 0.19274 +vt 0.77571 0.12056 +vt 0.82728 0.30100 +vt 0.72504 0.19238 +vt 0.77435 0.12827 +vt 0.82015 0.28854 +vt 0.32983 0.01517 +vt 0.10754 0.19521 +vt 0.14381 0.02148 +vt 0.32983 0.01517 +vt 0.10754 0.19521 +vt 0.14381 0.02148 +vt 0.31842 0.01956 +vt 0.11294 0.18598 +vt 0.14647 0.02540 +vt 0.82508 0.02407 +vt 0.89747 0.13029 +vt 0.77639 0.12092 +vt 0.82508 0.02407 +vt 0.89747 0.13029 +vt 0.77639 0.12092 +vt 0.82550 0.03106 +vt 0.89000 0.12570 +vt 0.78211 0.11735 +vt 0.83451 0.01178 +vt 0.78750 0.09212 +vt 0.71267 0.09212 +vt 0.83451 0.01178 +vt 0.78750 0.09212 +vt 0.71267 0.09212 +vt 0.82346 0.02365 +vt 0.78574 0.08812 +vt 0.72569 0.08812 +vt 0.65803 0.88561 +vt 0.51744 0.98830 +vt 0.57116 0.89477 +vt 0.65803 0.88561 +vt 0.51744 0.98830 +vt 0.57116 0.89477 +vt 0.64407 0.89110 +vt 0.52912 0.97506 +vt 0.57305 0.89859 +vt 0.87441 0.73800 +vt 0.89611 0.78951 +vt 0.86138 0.84864 +vt 0.85705 0.77170 +vt 0.87441 0.73800 +vt 0.89611 0.78951 +vt 0.86138 0.84864 +vt 0.85705 0.77170 +vt 0.87411 0.74625 +vt 0.89219 0.78917 +vt 0.86411 0.83699 +vt 0.86049 0.77270 +vt 0.70416 0.83447 +vt 0.65406 0.82907 +vt 0.75270 0.75000 +vt 0.85827 0.77534 +vt 0.85827 0.81797 +vt 0.70416 0.83447 +vt 0.65406 0.82907 +vt 0.75270 0.75000 +vt 0.85827 0.77534 +vt 0.85827 0.81797 +vt 0.70416 0.83045 +vt 0.66380 0.82609 +vt 0.75342 0.75426 +vt 0.85489 0.77862 +vt 0.85489 0.81431 +vt 0.50801 0.58196 +vt 0.64630 0.82507 +vt 0.52106 0.75383 +vt 0.50801 0.58196 +vt 0.64630 0.82507 +vt 0.52106 0.75383 +vt 0.51255 0.59710 +vt 0.63654 0.81508 +vt 0.52425 0.75120 +vt 0.87054 0.70999 +vt 0.73043 0.74712 +vt 0.84062 0.64055 +vt 0.87054 0.70999 +vt 0.73043 0.74712 +vt 0.84062 0.64055 +vt 0.86555 0.70720 +vt 0.74363 0.73951 +vt 0.83951 0.64678 +vt 0.69994 0.48530 +vt 0.76601 0.35270 +vt 0.81315 0.44645 +vt 0.66616 0.65491 +vt 0.69994 0.48530 +vt 0.76601 0.35270 +vt 0.81315 0.44645 +vt 0.66616 0.65491 +vt 0.70311 0.48681 +vt 0.76602 0.36054 +vt 0.80902 0.44606 +vt 0.67273 0.63936 +vt 0.80876 0.44830 +vt 0.76301 0.35091 +vt 0.80226 0.28028 +vt 0.82938 0.30908 +vt 0.80876 0.44830 +vt 0.76301 0.35091 +vt 0.80226 0.28028 +vt 0.82938 0.30908 +vt 0.80708 0.43650 +vt 0.76696 0.35108 +vt 0.80293 0.28636 +vt 0.82573 0.31058 +vt 0.80228 0.27917 +vt 0.72195 0.43605 +vt 0.72453 0.20382 +vt 0.80228 0.27917 +vt 0.72195 0.43605 +vt 0.72453 0.20382 +vt 0.79787 0.28007 +vt 0.72548 0.42143 +vt 0.72782 0.21217 +vt 0.02370 0.38499 +vt 0.01004 0.18557 +vt 0.07288 0.28123 +vt 0.02370 0.38499 +vt 0.01004 0.18557 +vt 0.07288 0.28123 +vt 0.02617 0.37161 +vt 0.01432 0.19859 +vt 0.06884 0.28159 +vt 0.60399 0.10578 +vt 0.78907 0.09270 +vt 0.60645 0.33418 +vt 0.60399 0.10578 +vt 0.78907 0.09270 +vt 0.60645 0.33418 +vt 0.60741 0.10955 +vt 0.78107 0.09727 +vt 0.60971 0.32387 +vt 0.01091 0.17573 +vt 0.13164 0.03394 +vt 0.08207 0.28736 +vt 0.01091 0.17573 +vt 0.13164 0.03394 +vt 0.08207 0.28736 +vt 0.01539 0.17611 +vt 0.12571 0.04654 +vt 0.08041 0.27812 +vt 0.99018 0.01614 +vt 0.90305 0.13248 +vt 0.83250 0.02591 +vt 0.99018 0.01614 +vt 0.90305 0.13248 +vt 0.83250 0.02591 +vt 0.98230 0.02064 +vt 0.90321 0.12624 +vt 0.83917 0.02951 +vt 0.52621 0.01439 +vt 0.44385 0.13019 +vt 0.32474 0.02040 +vt 0.52621 0.01439 +vt 0.44385 0.13019 +vt 0.32474 0.02040 +vt 0.51879 0.01862 +vt 0.44336 0.12467 +vt 0.33428 0.02412 +vt 0.76684 0.83665 +vt 0.71681 0.87443 +vt 0.58470 0.88077 +vt 0.66377 0.83037 +vt 0.76684 0.83665 +vt 0.71681 0.87443 +vt 0.58470 0.88077 +vt 0.66377 0.83037 +vt 0.75611 0.84001 +vt 0.71576 0.87047 +vt 0.59922 0.87607 +vt 0.66454 0.83443 +vt 0.58282 0.69146 +vt 0.60449 0.65610 +vt 0.66267 0.67481 +vt 0.72682 0.75382 +vt 0.65234 0.81523 +vt 0.58282 0.69146 +vt 0.60449 0.65610 +vt 0.66267 0.67481 +vt 0.72682 0.75382 +vt 0.65234 0.81523 +vt 0.58694 0.69156 +vt 0.60585 0.66069 +vt 0.66088 0.67838 +vt 0.72164 0.75321 +vt 0.65324 0.80961 +vt 0.24900 0.49426 +vt 0.35704 0.60355 +vt 0.20529 0.68453 +vt 0.09984 0.72933 +vt 0.24900 0.49426 +vt 0.35704 0.60355 +vt 0.20529 0.68453 +vt 0.09984 0.72933 +vt 0.24954 0.50007 +vt 0.35079 0.60249 +vt 0.20402 0.68081 +vt 0.10932 0.72105 +vt 0.60839 0.64458 +vt 0.68046 0.52239 +vt 0.65105 0.66553 +vt 0.60839 0.64458 +vt 0.68046 0.52239 +vt 0.65105 0.66553 +vt 0.61357 0.64279 +vt 0.67293 0.54215 +vt 0.64870 0.66004 +vt 0.54840 0.41936 +vt 0.42976 0.65830 +vt 0.25548 0.48712 +vt 0.48070 0.42864 +vt 0.54840 0.41936 +vt 0.42976 0.65830 +vt 0.25548 0.48712 +vt 0.48070 0.42864 +vt 0.54205 0.42426 +vt 0.42887 0.65222 +vt 0.26297 0.48927 +vt 0.48125 0.43259 +vt 0.03579 0.38858 +vt 0.08238 0.28996 +vt 0.14928 0.37802 +vt 0.17213 0.51363 +vt 0.03579 0.38858 +vt 0.08238 0.28996 +vt 0.14928 0.37802 +vt 0.17213 0.51363 +vt 0.04016 0.38753 +vt 0.08303 0.29679 +vt 0.14615 0.37989 +vt 0.16705 0.50391 +vt 0.59178 0.49275 +vt 0.56168 0.40888 +vt 0.71138 0.20702 +vt 0.71550 0.28997 +vt 0.59178 0.49275 +vt 0.56168 0.40888 +vt 0.71138 0.20702 +vt 0.71550 0.28997 +vt 0.59254 0.48467 +vt 0.56561 0.40963 +vt 0.70850 0.21698 +vt 0.71206 0.28878 +vt 0.32747 0.42806 +vt 0.38976 0.28461 +vt 0.54177 0.40587 +vt 0.32747 0.42806 +vt 0.38976 0.28461 +vt 0.54177 0.40587 +vt 0.33327 0.42344 +vt 0.39103 0.29044 +vt 0.53195 0.40287 +vt 0.31526 0.41561 +vt 0.21421 0.30326 +vt 0.18044 0.15262 +vt 0.24685 0.09805 +vt 0.31526 0.41561 +vt 0.21421 0.30326 +vt 0.18044 0.15262 +vt 0.24685 0.09805 +vt 0.30913 0.40330 +vt 0.21722 0.30112 +vt 0.18431 0.15431 +vt 0.24478 0.10462 +vt 0.59395 0.34642 +vt 0.55766 0.39611 +vt 0.50609 0.36371 +vt 0.39838 0.27381 +vt 0.59157 0.10433 +vt 0.59395 0.34642 +vt 0.55766 0.39611 +vt 0.50609 0.36371 +vt 0.39838 0.27381 +vt 0.59157 0.10433 +vt 0.59056 0.34495 +vt 0.55687 0.39108 +vt 0.50785 0.36029 +vt 0.40415 0.27372 +vt 0.58828 0.11220 +vt 0.32624 0.03328 +vt 0.43423 0.13455 +vt 0.25693 0.09657 +vt 0.32624 0.03328 +vt 0.43423 0.13455 +vt 0.25693 0.09657 +vt 0.32622 0.03836 +vt 0.42155 0.12777 +vt 0.26502 0.09423 +vn 0.0000 0.0000 1.0000 +vn -0.5000 -0.5000 0.7071 +vn -0.7071 -0.7071 0.0000 +vn -0.0248 -0.7066 0.7071 +vn -0.0351 -0.9994 0.0000 +vn 0.2685 0.6542 0.7071 +vn 0.3797 0.9251 0.0000 +vn 0.0000 0.0000 -1.0000 +vn -0.5531 -0.4405 0.7071 +vn -0.7822 -0.6230 0.0000 +vn -0.2883 -0.6457 0.7071 +vn -0.4077 -0.9131 0.0000 +vn 0.4583 -0.5385 0.7071 +vn 0.6481 -0.7616 0.0000 +vn 0.3132 0.6340 0.7071 +vn 0.4429 0.8966 0.0000 +vn 0.4284 -0.5625 0.7071 +vn 0.6059 -0.7955 0.0000 +vn 0.0000 0.7071 0.7071 +vn 0.0000 1.0000 0.0000 +vn -0.4555 -0.5409 0.7071 +vn -0.6441 -0.7649 0.0000 +vn -0.5056 0.4944 0.7071 +vn -0.7150 0.6992 0.0000 +vn 0.1470 -0.6917 0.7071 +vn 0.2079 -0.9782 0.0000 +vn 0.4341 0.5582 0.7071 +vn 0.6139 0.7894 0.0000 +vn -0.5861 0.3956 0.7071 +vn -0.8289 0.5594 0.0000 +vn -0.6434 -0.2933 0.7071 +vn -0.9099 -0.4148 0.0000 +vn -0.5708 -0.4174 0.7071 +vn -0.8072 -0.5903 0.0000 +vn 0.7070 -0.0152 0.7071 +vn 0.9998 -0.0215 0.0000 +vn 0.5785 0.4066 0.7071 +vn 0.8181 0.5751 0.0000 +vn 0.5092 -0.4906 0.7071 +vn 0.7202 -0.6938 0.0000 +vn 0.6260 0.3288 0.7071 +vn 0.8853 0.4650 0.0000 +vn -0.6812 0.1894 0.7071 +vn -0.9634 0.2679 0.0000 +vn -0.5222 -0.4768 0.7071 +vn -0.7385 -0.6743 0.0000 +vn 0.6696 -0.2271 0.7071 +vn 0.9470 -0.3212 0.0000 +vn -0.4585 0.5383 0.7071 +vn -0.6484 0.7613 0.0000 +vn 0.3985 0.5840 0.7072 +vn 0.5636 0.8260 0.0000 +vn -0.6863 -0.1701 0.7071 +vn -0.9706 -0.2406 0.0000 +vn -0.0202 -0.7068 0.7071 +vn -0.0286 -0.9996 0.0000 +vn 0.5498 -0.4447 0.7071 +vn 0.7775 -0.6289 0.0000 +vn -0.0460 0.7057 0.7071 +vn -0.0651 0.9979 0.0000 +vn -0.6072 -0.3623 0.7071 +vn -0.8587 -0.5125 0.0000 +vn 0.5808 0.4033 0.7071 +vn 0.8214 0.5704 0.0000 +vn -0.3434 -0.6181 0.7071 +vn -0.4857 -0.8741 0.0000 +vn 0.3706 0.6022 0.7071 +vn 0.5241 0.8516 0.0000 +vn -0.5843 -0.3983 0.7071 +vn -0.8263 -0.5633 0.0000 +vn -0.0626 -0.7043 0.7071 +vn -0.0885 -0.9961 0.0000 +vn 0.6324 -0.3162 0.7071 +vn 0.8944 -0.4472 0.0000 +vn 0.5801 0.4043 0.7071 +vn 0.8204 0.5718 0.0000 +vn -0.7055 0.0471 0.7071 +vn -0.9978 0.0667 0.0000 +vn -0.6033 -0.3688 0.7071 +vn -0.8532 -0.5216 0.0000 +vn -0.0640 0.7042 0.7071 +vn -0.0906 0.9959 0.0000 +vn -0.3957 -0.5860 0.7071 +vn -0.5597 -0.8287 0.0000 +vn 0.1402 -0.6931 0.7071 +vn 0.1982 -0.9802 0.0000 +vn 0.7071 0.0000 0.7071 +vn 1.0000 0.0000 0.0000 +vn 0.0635 0.7042 0.7071 +vn 0.0899 0.9960 0.0000 +vn 0.5860 -0.3957 0.7071 +vn 0.8288 -0.5596 0.0000 +vn -0.3056 0.6377 0.7071 +vn -0.4322 0.9018 0.0000 +vn -0.7042 0.0635 0.7071 +vn -0.9960 0.0897 0.0000 +vn 0.1541 0.6901 0.7072 +vn 0.2179 0.9760 0.0000 +vn -0.4467 -0.5482 0.7071 +vn -0.6317 -0.7752 0.0000 +vn 0.6296 -0.3220 0.7070 +vn 0.8903 -0.4553 0.0000 +vn -0.6086 -0.3599 0.7071 +vn -0.8608 -0.5090 0.0000 +vn 0.6072 -0.3624 0.7071 +vn 0.8587 -0.5125 0.0000 +vn 0.5423 0.4538 0.7071 +vn 0.7669 0.6418 0.0000 +vn -0.6881 -0.1627 0.7071 +vn -0.9732 -0.2300 0.0000 +vn -0.6176 0.3443 0.7071 +vn -0.8734 0.4869 0.0000 +vn -0.5903 -0.3893 0.7071 +vn -0.8348 -0.5506 0.0000 +vn 0.4714 -0.5270 0.7071 +vn 0.6668 -0.7453 0.0000 +vn 0.6965 0.1224 0.7070 +vn 0.9849 0.1731 0.0000 +vn 0.6042 0.3673 0.7071 +vn 0.8545 0.5194 0.0000 +vn -0.7070 -0.0093 0.7071 +vn -0.9999 -0.0132 0.0000 +vn 0.4472 -0.5477 0.7071 +vn 0.6325 -0.7746 0.0000 +vn -0.7048 0.0573 0.7071 +vn -0.9967 0.0810 0.0000 +vn 0.5576 -0.4348 0.7071 +vn 0.7886 -0.6148 0.0000 +vn 0.6163 0.3467 0.7071 +vn 0.8716 0.4903 0.0000 +vn -0.0420 -0.7059 0.7071 +vn -0.0595 -0.9982 0.0000 +vn 0.5262 0.4723 0.7071 +vn 0.7442 0.6680 0.0000 +vn -0.7071 0.0090 0.7071 +vn -0.9999 0.0127 0.0000 +vn -0.4974 -0.5027 0.7071 +vn -0.7034 -0.7108 0.0000 +vn 0.6888 0.1599 0.7071 +vn 0.9741 0.2262 0.0000 +vn -0.5639 0.4267 0.7071 +vn -0.7975 0.6034 0.0000 +vn 0.5285 0.4698 0.7071 +vn 0.7474 0.6644 0.0000 +vn -0.5560 0.4369 0.7071 +vn -0.7863 0.6178 0.0000 +vn -0.0369 -0.7062 0.7071 +vn -0.0521 -0.9986 0.0000 +vn 0.5403 0.4561 0.7071 +vn 0.7641 0.6451 0.0000 +vn -0.4337 0.5585 0.7071 +vn -0.6133 0.7898 0.0000 +vn -0.0178 -0.7069 0.7071 +vn -0.0251 -0.9997 0.0000 +vn 0.3796 0.5966 0.7071 +vn 0.5368 0.8437 0.0000 +vn 0.0286 0.7065 0.7071 +vn 0.0404 0.9992 0.0000 +vn -0.3346 -0.6229 0.7071 +vn -0.4731 -0.8810 0.0000 +vn 0.0362 -0.7061 0.7071 +vn 0.0512 -0.9987 0.0000 +vn -0.5719 -0.4159 0.7071 +vn -0.8087 -0.5882 0.0000 +vn 0.1849 -0.6825 0.7071 +vn 0.2615 -0.9652 0.0000 +vn 0.5091 -0.4906 0.7071 +vn 0.7201 -0.6939 0.0000 +vn 0.4035 0.5807 0.7071 +vn 0.5706 0.8212 0.0000 +vn -0.5884 0.3922 0.7071 +vn -0.8321 0.5547 0.0000 +vn 0.4587 -0.5382 0.7071 +vn 0.6486 -0.7611 0.0000 +vn 0.2899 0.6449 0.7071 +vn 0.4100 0.9121 0.0000 +vn 0.2383 0.6657 0.7071 +vn 0.3371 0.9415 0.0000 +vn -0.5649 -0.4254 0.7071 +vn -0.7988 -0.6016 0.0000 +vn -0.5793 -0.4055 0.7071 +vn -0.8192 -0.5735 0.0000 +vn 0.6870 0.1676 0.7071 +vn 0.9715 0.2370 0.0000 +vn -0.2703 0.6533 0.7071 +vn -0.3823 0.9240 0.0000 +vn 0.6092 0.3590 0.7071 +vn 0.8615 0.5077 0.0000 +vn -0.4508 0.5447 0.7071 +vn -0.6375 0.7704 0.0000 +vn -0.1511 -0.6907 0.7071 +vn -0.2137 -0.9769 0.0000 +vn -0.0811 -0.7024 0.7071 +vn -0.1147 -0.9934 0.0000 +vn -0.6168 -0.3458 0.7071 +vn -0.8722 -0.4891 0.0000 +vn 0.5252 -0.4735 0.7071 +vn 0.7427 -0.6696 0.0000 +vn 0.6934 -0.1387 0.7071 +vn 0.9806 -0.1961 0.0000 +vn -0.4323 0.5595 0.7071 +vn -0.6115 0.7913 0.0000 +vn -0.6505 0.2771 0.7071 +vn -0.9200 0.3919 0.0000 +vn -0.5308 -0.4672 0.7071 +vn -0.7506 -0.6607 0.0000 +vn 0.7059 -0.0416 0.7071 +vn 0.9983 -0.0588 0.0000 +vn 0.5727 0.4147 0.7071 +vn 0.8099 0.5865 0.0000 +vn -0.6285 -0.3239 0.7071 +vn -0.8889 -0.4581 0.0000 +vn 0.3945 -0.5868 0.7071 +vn 0.5579 -0.8299 0.0000 +vn 0.0615 0.7045 0.7071 +vn 0.0869 0.9962 0.0000 +vn -0.4834 0.5161 0.7071 +vn -0.6836 0.7298 0.0000 +vn -0.6834 0.1818 0.7071 +vn -0.9664 0.2571 0.0000 +vn -0.4025 -0.5814 0.7071 +vn -0.5692 -0.8222 0.0000 +vn 0.6851 -0.1752 0.7071 +vn 0.9688 -0.2477 0.0000 +vn 0.5343 0.4631 0.7071 +vn 0.7557 0.6550 0.0000 +vn -0.3308 0.6250 0.7071 +vn -0.4678 0.8838 0.0000 +vn -0.4068 0.5784 0.7071 +vn -0.5753 0.8180 0.0000 +vn -0.4203 -0.5687 0.7071 +vn -0.5944 -0.8042 0.0000 +vn 0.7070 -0.0082 0.7071 +vn 0.9999 -0.0116 0.0000 +vn 0.4383 -0.5548 0.7071 +vn 0.6199 -0.7846 0.0000 +vn -0.1256 0.6959 0.7071 +vn -0.1776 0.9841 0.0000 +vn -0.4311 -0.5605 0.7071 +vn -0.6097 -0.7926 0.0000 +o tile01_dark +usemtl dark +f 7/7/1 8/8/1 9/9/1 +f 4/4/2 5/5/2 8/8/2 7/7/2 +f 1/1/3 2/2/3 5/5/3 4/4/3 +f 5/5/4 6/6/4 9/9/4 8/8/4 +f 2/2/5 3/3/5 6/6/5 5/5/5 +f 6/6/6 4/4/6 7/7/6 9/9/6 +f 3/3/7 1/1/7 4/4/7 6/6/7 +f 3/3/8 2/2/8 1/1/8 +o tile02_dark +usemtl dark +f 18/18/1 19/19/1 20/20/1 21/21/1 +f 14/14/9 15/15/9 19/19/9 18/18/9 +f 10/10/10 11/11/10 15/15/10 14/14/10 +f 15/15/11 16/16/11 20/20/11 19/19/11 +f 11/11/12 12/12/12 16/16/12 15/15/12 +f 16/16/13 17/17/13 21/21/13 20/20/13 +f 12/12/14 13/13/14 17/17/14 16/16/14 +f 17/17/15 14/14/15 18/18/15 21/21/15 +f 13/13/16 10/10/16 14/14/16 17/17/16 +f 13/13/8 12/12/8 11/11/8 10/10/8 +o tile03_dark +usemtl dark +f 28/28/1 29/29/1 30/30/1 +f 25/25/17 26/26/17 29/29/17 28/28/17 +f 22/22/18 23/23/18 26/26/18 25/25/18 +f 26/26/19 27/27/19 30/30/19 29/29/19 +f 23/23/20 24/24/20 27/27/20 26/26/20 +f 27/27/21 25/25/21 28/28/21 30/30/21 +f 24/24/22 22/22/22 25/25/22 27/27/22 +f 24/24/8 23/23/8 22/22/8 +o tile04_dark +usemtl dark +f 37/37/1 38/38/1 39/39/1 +f 34/34/23 35/35/23 38/38/23 37/37/23 +f 31/31/24 32/32/24 35/35/24 34/34/24 +f 35/35/25 36/36/25 39/39/25 38/38/25 +f 32/32/26 33/33/26 36/36/26 35/35/26 +f 36/36/27 34/34/27 37/37/27 39/39/27 +f 33/33/28 31/31/28 34/34/28 36/36/28 +f 33/33/8 32/32/8 31/31/8 +o tile05_dark +usemtl dark +f 50/50/1 51/51/1 52/52/1 53/53/1 54/54/1 +f 45/45/29 46/46/29 51/51/29 50/50/29 +f 40/40/30 41/41/30 46/46/30 45/45/30 +f 46/46/31 47/47/31 52/52/31 51/51/31 +f 41/41/32 42/42/32 47/47/32 46/46/32 +f 47/47/33 48/48/33 53/53/33 52/52/33 +f 42/42/34 43/43/34 48/48/34 47/47/34 +f 48/48/35 49/49/35 54/54/35 53/53/35 +f 43/43/36 44/44/36 49/49/36 48/48/36 +f 49/49/37 45/45/37 50/50/37 54/54/37 +f 44/44/38 40/40/38 45/45/38 49/49/38 +f 44/44/8 43/43/8 42/42/8 41/41/8 40/40/8 +o tile06_dark +usemtl dark +f 61/61/1 62/62/1 63/63/1 +f 58/58/39 59/59/39 62/62/39 61/61/39 +f 55/55/40 56/56/40 59/59/40 58/58/40 +f 59/59/41 60/60/41 63/63/41 62/62/41 +f 56/56/42 57/57/42 60/60/42 59/59/42 +f 60/60/43 58/58/43 61/61/43 63/63/43 +f 57/57/44 55/55/44 58/58/44 60/60/44 +f 57/57/8 56/56/8 55/55/8 +o tile07_dark +usemtl dark +f 70/70/1 71/71/1 72/72/1 +f 67/67/45 68/68/45 71/71/45 70/70/45 +f 64/64/46 65/65/46 68/68/46 67/67/46 +f 68/68/47 69/69/47 72/72/47 71/71/47 +f 65/65/48 66/66/48 69/69/48 68/68/48 +f 69/69/49 67/67/49 70/70/49 72/72/49 +f 66/66/50 64/64/50 67/67/50 69/69/50 +f 66/66/8 65/65/8 64/64/8 +o tile08_dark +usemtl dark +f 79/79/1 80/80/1 81/81/1 +f 76/76/51 77/77/51 80/80/51 79/79/51 +f 73/73/52 74/74/52 77/77/52 76/76/52 +f 77/77/53 78/78/53 81/81/53 80/80/53 +f 74/74/54 75/75/54 78/78/54 77/77/54 +f 78/78/55 76/76/55 79/79/55 81/81/55 +f 75/75/56 73/73/56 76/76/56 78/78/56 +f 75/75/8 74/74/8 73/73/8 +o tile09_dark +usemtl dark +f 88/88/1 89/89/1 90/90/1 +f 85/85/57 86/86/57 89/89/57 88/88/57 +f 82/82/58 83/83/58 86/86/58 85/85/58 +f 86/86/59 87/87/59 90/90/59 89/89/59 +f 83/83/60 84/84/60 87/87/60 86/86/60 +f 87/87/61 85/85/61 88/88/61 90/90/61 +f 84/84/62 82/82/62 85/85/62 87/87/62 +f 84/84/8 83/83/8 82/82/8 +o tile10_dark +usemtl dark +f 97/97/1 98/98/1 99/99/1 +f 94/94/63 95/95/63 98/98/63 97/97/63 +f 91/91/64 92/92/64 95/95/64 94/94/64 +f 95/95/19 96/96/19 99/99/19 98/98/19 +f 92/92/20 93/93/20 96/96/20 95/95/20 +f 96/96/65 94/94/65 97/97/65 99/99/65 +f 93/93/66 91/91/66 94/94/66 96/96/66 +f 93/93/8 92/92/8 91/91/8 +o tile11_champagne +usemtl champagne +f 106/106/1 107/107/1 108/108/1 +f 103/103/67 104/104/67 107/107/67 106/106/67 +f 100/100/68 101/101/68 104/104/68 103/103/68 +f 104/104/69 105/105/69 108/108/69 107/107/69 +f 101/101/70 102/102/70 105/105/70 104/104/70 +f 105/105/71 103/103/71 106/106/71 108/108/71 +f 102/102/72 100/100/72 103/103/72 105/105/72 +f 102/102/8 101/101/8 100/100/8 +o tile12_champagne +usemtl champagne +f 117/117/1 118/118/1 119/119/1 120/120/1 +f 113/113/73 114/114/73 118/118/73 117/117/73 +f 109/109/74 110/110/74 114/114/74 113/113/74 +f 114/114/75 115/115/75 119/119/75 118/118/75 +f 110/110/76 111/111/76 115/115/76 114/114/76 +f 115/115/77 116/116/77 120/120/77 119/119/77 +f 111/111/78 112/112/78 116/116/78 115/115/78 +f 116/116/79 113/113/79 117/117/79 120/120/79 +f 112/112/80 109/109/80 113/113/80 116/116/80 +f 112/112/8 111/111/8 110/110/8 109/109/8 +o tile13_champagne +usemtl champagne +f 131/131/1 132/132/1 133/133/1 134/134/1 135/135/1 +f 126/126/81 127/127/81 132/132/81 131/131/81 +f 121/121/82 122/122/82 127/127/82 126/126/82 +f 127/127/83 128/128/83 133/133/83 132/132/83 +f 122/122/84 123/123/84 128/128/84 127/127/84 +f 128/128/85 129/129/85 134/134/85 133/133/85 +f 123/123/86 124/124/86 129/129/86 128/128/86 +f 129/129/87 130/130/87 135/135/87 134/134/87 +f 124/124/88 125/125/88 130/130/88 129/129/88 +f 130/130/89 126/126/89 131/131/89 135/135/89 +f 125/125/90 121/121/90 126/126/90 130/130/90 +f 125/125/8 124/124/8 123/123/8 122/122/8 121/121/8 +o tile14_champagne +usemtl champagne +f 142/142/1 143/143/1 144/144/1 +f 139/139/91 140/140/91 143/143/91 142/142/91 +f 136/136/92 137/137/92 140/140/92 139/139/92 +f 140/140/93 141/141/93 144/144/93 143/143/93 +f 137/137/94 138/138/94 141/141/94 140/140/94 +f 141/141/95 139/139/95 142/142/95 144/144/95 +f 138/138/96 136/136/96 139/139/96 141/141/96 +f 138/138/8 137/137/8 136/136/8 +o tile15_champagne +usemtl champagne +f 151/151/1 152/152/1 153/153/1 +f 148/148/97 149/149/97 152/152/97 151/151/97 +f 145/145/98 146/146/98 149/149/98 148/148/98 +f 149/149/99 150/150/99 153/153/99 152/152/99 +f 146/146/100 147/147/100 150/150/100 149/149/100 +f 150/150/101 148/148/101 151/151/101 153/153/101 +f 147/147/102 145/145/102 148/148/102 150/150/102 +f 147/147/8 146/146/8 145/145/8 +o tile16_champagne +usemtl champagne +f 162/162/1 163/163/1 164/164/1 165/165/1 +f 158/158/103 159/159/103 163/163/103 162/162/103 +f 154/154/104 155/155/104 159/159/104 158/158/104 +f 159/159/105 160/160/105 164/164/105 163/163/105 +f 155/155/106 156/156/106 160/160/106 159/159/106 +f 160/160/107 161/161/107 165/165/107 164/164/107 +f 156/156/108 157/157/108 161/161/108 160/160/108 +f 161/161/109 158/158/109 162/162/109 165/165/109 +f 157/157/110 154/154/110 158/158/110 161/161/110 +f 157/157/8 156/156/8 155/155/8 154/154/8 +o tile17_champagne +usemtl champagne +f 174/174/1 175/175/1 176/176/1 177/177/1 +f 170/170/111 171/171/111 175/175/111 174/174/111 +f 166/166/112 167/167/112 171/171/112 170/170/112 +f 171/171/113 172/172/113 176/176/113 175/175/113 +f 167/167/114 168/168/114 172/172/114 171/171/114 +f 172/172/115 173/173/115 177/177/115 176/176/115 +f 168/168/116 169/169/116 173/173/116 172/172/116 +f 173/173/117 170/170/117 174/174/117 177/177/117 +f 169/169/118 166/166/118 170/170/118 173/173/118 +f 169/169/8 168/168/8 167/167/8 166/166/8 +o tile18_champagne +usemtl champagne +f 184/184/1 185/185/1 186/186/1 +f 181/181/119 182/182/119 185/185/119 184/184/119 +f 178/178/120 179/179/120 182/182/120 181/181/120 +f 182/182/121 183/183/121 186/186/121 185/185/121 +f 179/179/122 180/180/122 183/183/122 182/182/122 +f 183/183/123 181/181/123 184/184/123 186/186/123 +f 180/180/124 178/178/124 181/181/124 183/183/124 +f 180/180/8 179/179/8 178/178/8 +o tile19_champagne +usemtl champagne +f 193/193/1 194/194/1 195/195/1 +f 190/190/125 191/191/125 194/194/125 193/193/125 +f 187/187/126 188/188/126 191/191/126 190/190/126 +f 191/191/127 192/192/127 195/195/127 194/194/127 +f 188/188/128 189/189/128 192/192/128 191/191/128 +f 192/192/129 190/190/129 193/193/129 195/195/129 +f 189/189/130 187/187/130 190/190/130 192/192/130 +f 189/189/8 188/188/8 187/187/8 +o tile20_champagne +usemtl champagne +f 202/202/1 203/203/1 204/204/1 +f 199/199/131 200/200/131 203/203/131 202/202/131 +f 196/196/132 197/197/132 200/200/132 199/199/132 +f 200/200/133 201/201/133 204/204/133 203/203/133 +f 197/197/134 198/198/134 201/201/134 200/200/134 +f 201/201/135 199/199/135 202/202/135 204/204/135 +f 198/198/136 196/196/136 199/199/136 201/201/136 +f 198/198/8 197/197/8 196/196/8 +o tile21_champagne +usemtl champagne +f 211/211/1 212/212/1 213/213/1 +f 208/208/137 209/209/137 212/212/137 211/211/137 +f 205/205/138 206/206/138 209/209/138 208/208/138 +f 209/209/139 210/210/139 213/213/139 212/212/139 +f 206/206/140 207/207/140 210/210/140 209/209/140 +f 210/210/141 208/208/141 211/211/141 213/213/141 +f 207/207/142 205/205/142 208/208/142 210/210/142 +f 207/207/8 206/206/8 205/205/8 +o tile22_champagne +usemtl champagne +f 220/220/1 221/221/1 222/222/1 +f 217/217/143 218/218/143 221/221/143 220/220/143 +f 214/214/144 215/215/144 218/218/144 217/217/144 +f 218/218/145 219/219/145 222/222/145 221/221/145 +f 215/215/146 216/216/146 219/219/146 218/218/146 +f 219/219/147 217/217/147 220/220/147 222/222/147 +f 216/216/148 214/214/148 217/217/148 219/219/148 +f 216/216/8 215/215/8 214/214/8 +o tile23_champagne +usemtl champagne +f 229/229/1 230/230/1 231/231/1 +f 226/226/149 227/227/149 230/230/149 229/229/149 +f 223/223/150 224/224/150 227/227/150 226/226/150 +f 227/227/151 228/228/151 231/231/151 230/230/151 +f 224/224/152 225/225/152 228/228/152 227/227/152 +f 228/228/153 226/226/153 229/229/153 231/231/153 +f 225/225/154 223/223/154 226/226/154 228/228/154 +f 225/225/8 224/224/8 223/223/8 +o tile24_glass +usemtl glass +f 240/240/1 241/241/1 242/242/1 243/243/1 +f 236/236/155 237/237/155 241/241/155 240/240/155 +f 232/232/156 233/233/156 237/237/156 236/236/156 +f 237/237/157 238/238/157 242/242/157 241/241/157 +f 233/233/158 234/234/158 238/238/158 237/237/158 +f 238/238/159 239/239/159 243/243/159 242/242/159 +f 234/234/160 235/235/160 239/239/160 238/238/160 +f 239/239/161 236/236/161 240/240/161 243/243/161 +f 235/235/162 232/232/162 236/236/162 239/239/162 +f 235/235/8 234/234/8 233/233/8 232/232/8 +o tile25_glass +usemtl glass +f 254/254/1 255/255/1 256/256/1 257/257/1 258/258/1 +f 249/249/163 250/250/163 255/255/163 254/254/163 +f 244/244/164 245/245/164 250/250/164 249/249/164 +f 250/250/165 251/251/165 256/256/165 255/255/165 +f 245/245/166 246/246/166 251/251/166 250/250/166 +f 251/251/167 252/252/167 257/257/167 256/256/167 +f 246/246/168 247/247/168 252/252/168 251/251/168 +f 252/252/169 253/253/169 258/258/169 257/257/169 +f 247/247/170 248/248/170 253/253/170 252/252/170 +f 253/253/171 249/249/171 254/254/171 258/258/171 +f 248/248/172 244/244/172 249/249/172 253/253/172 +f 248/248/8 247/247/8 246/246/8 245/245/8 244/244/8 +o tile26_glass +usemtl glass +f 267/267/1 268/268/1 269/269/1 270/270/1 +f 263/263/173 264/264/173 268/268/173 267/267/173 +f 259/259/174 260/260/174 264/264/174 263/263/174 +f 264/264/175 265/265/175 269/269/175 268/268/175 +f 260/260/176 261/261/176 265/265/176 264/264/176 +f 265/265/177 266/266/177 270/270/177 269/269/177 +f 261/261/178 262/262/178 266/266/178 265/265/178 +f 266/266/179 263/263/179 267/267/179 270/270/179 +f 262/262/180 259/259/180 263/263/180 266/266/180 +f 262/262/8 261/261/8 260/260/8 259/259/8 +o tile27_glass +usemtl glass +f 277/277/1 278/278/1 279/279/1 +f 274/274/181 275/275/181 278/278/181 277/277/181 +f 271/271/182 272/272/182 275/275/182 274/274/182 +f 275/275/183 276/276/183 279/279/183 278/278/183 +f 272/272/184 273/273/184 276/276/184 275/275/184 +f 276/276/185 274/274/185 277/277/185 279/279/185 +f 273/273/186 271/271/186 274/274/186 276/276/186 +f 273/273/8 272/272/8 271/271/8 +o tile28_glass +usemtl glass +f 288/288/1 289/289/1 290/290/1 291/291/1 +f 284/284/187 285/285/187 289/289/187 288/288/187 +f 280/280/188 281/281/188 285/285/188 284/284/188 +f 285/285/189 286/286/189 290/290/189 289/289/189 +f 281/281/190 282/282/190 286/286/190 285/285/190 +f 286/286/191 287/287/191 291/291/191 290/290/191 +f 282/282/192 283/283/192 287/287/192 286/286/192 +f 287/287/193 284/284/193 288/288/193 291/291/193 +f 283/283/194 280/280/194 284/284/194 287/287/194 +f 283/283/8 282/282/8 281/281/8 280/280/8 +o tile29_glass +usemtl glass +f 300/300/1 301/301/1 302/302/1 303/303/1 +f 296/296/195 297/297/195 301/301/195 300/300/195 +f 292/292/196 293/293/196 297/297/196 296/296/196 +f 297/297/197 298/298/197 302/302/197 301/301/197 +f 293/293/198 294/294/198 298/298/198 297/297/198 +f 298/298/199 299/299/199 303/303/199 302/302/199 +f 294/294/200 295/295/200 299/299/200 298/298/200 +f 299/299/201 296/296/201 300/300/201 303/303/201 +f 295/295/202 292/292/202 296/296/202 299/299/202 +f 295/295/8 294/294/8 293/293/8 292/292/8 +o tile30_glass +usemtl glass +f 312/312/1 313/313/1 314/314/1 315/315/1 +f 308/308/203 309/309/203 313/313/203 312/312/203 +f 304/304/204 305/305/204 309/309/204 308/308/204 +f 309/309/205 310/310/205 314/314/205 313/313/205 +f 305/305/206 306/306/206 310/310/206 309/309/206 +f 310/310/207 311/311/207 315/315/207 314/314/207 +f 306/306/208 307/307/208 311/311/208 310/310/208 +f 311/311/209 308/308/209 312/312/209 315/315/209 +f 307/307/210 304/304/210 308/308/210 311/311/210 +f 307/307/8 306/306/8 305/305/8 304/304/8 +o tile31_glass +usemtl glass +f 322/322/1 323/323/1 324/324/1 +f 319/319/211 320/320/211 323/323/211 322/322/211 +f 316/316/212 317/317/212 320/320/212 319/319/212 +f 320/320/213 321/321/213 324/324/213 323/323/213 +f 317/317/214 318/318/214 321/321/214 320/320/214 +f 321/321/215 319/319/215 322/322/215 324/324/215 +f 318/318/216 316/316/216 319/319/216 321/321/216 +f 318/318/8 317/317/8 316/316/8 +o tile32_glass +usemtl glass +f 333/333/1 334/334/1 335/335/1 336/336/1 +f 329/329/217 330/330/217 334/334/217 333/333/217 +f 325/325/218 326/326/218 330/330/218 329/329/218 +f 330/330/219 331/331/219 335/335/219 334/334/219 +f 326/326/220 327/327/220 331/331/220 330/330/220 +f 331/331/221 332/332/221 336/336/221 335/335/221 +f 327/327/222 328/328/222 332/332/222 331/331/222 +f 332/332/223 329/329/223 333/333/223 336/336/223 +f 328/328/224 325/325/224 329/329/224 332/332/224 +f 328/328/8 327/327/8 326/326/8 325/325/8 +o tile33_glass +usemtl glass +f 347/347/1 348/348/1 349/349/1 350/350/1 351/351/1 +f 342/342/225 343/343/225 348/348/225 347/347/225 +f 337/337/226 338/338/226 343/343/226 342/342/226 +f 343/343/227 344/344/227 349/349/227 348/348/227 +f 338/338/228 339/339/228 344/344/228 343/343/228 +f 344/344/229 345/345/229 350/350/229 349/349/229 +f 339/339/230 340/340/230 345/345/230 344/344/230 +f 345/345/231 346/346/231 351/351/231 350/350/231 +f 340/340/232 341/341/232 346/346/232 345/345/232 +f 346/346/233 342/342/233 347/347/233 351/351/233 +f 341/341/234 337/337/234 342/342/234 346/346/234 +f 341/341/8 340/340/8 339/339/8 338/338/8 337/337/8 +o tile34_glass +usemtl glass +f 358/358/1 359/359/1 360/360/1 +f 355/355/235 356/356/235 359/359/235 358/358/235 +f 352/352/236 353/353/236 356/356/236 355/355/236 +f 356/356/237 357/357/237 360/360/237 359/359/237 +f 353/353/238 354/354/238 357/357/238 356/356/238 +f 357/357/239 355/355/239 358/358/239 360/360/239 +f 354/354/240 352/352/240 355/355/240 357/357/240 +f 354/354/8 353/353/8 352/352/8 diff --git a/assets/Dragon.svg b/assets/Dragon.svg new file mode 100644 index 000000000..5001d2d76 --- /dev/null +++ b/assets/Dragon.svg @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Singe dragon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/DragonModel.glb b/assets/DragonModel.glb new file mode 100644 index 000000000..e51ddc35b --- /dev/null +++ b/assets/DragonModel.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:459dfa78f2dacba3bea8d82352d97b3648847bccf936a9e85732e818a3759971 +size 131440 diff --git a/assets/DragonModel.mtl b/assets/DragonModel.mtl new file mode 100644 index 000000000..2094ddafa --- /dev/null +++ b/assets/DragonModel.mtl @@ -0,0 +1,34 @@ +# Materials for DragonModel.obj +newmtl dark +Ka 0.060 0.062 0.066 +Kd 0.300 0.310 0.330 +Ks 0.700 0.700 0.700 +Ns 180.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.35 + +newmtl champagne +Ka 0.144 0.128 0.108 +Kd 0.720 0.640 0.540 +Ks 0.850 0.800 0.700 +Ns 120.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.40 + +newmtl glass +Ka 0.176 0.184 0.180 +Kd 0.880 0.920 0.900 +Ks 0.950 0.950 0.950 +Ns 300.0 +d 0.55 +Ni 1.50 +illum 4 +Pm 0.00 +Pr 0.15 + diff --git a/assets/DragonModel.obj b/assets/DragonModel.obj new file mode 100644 index 000000000..251da2988 --- /dev/null +++ b/assets/DragonModel.obj @@ -0,0 +1,3155 @@ +# Singe dragon, solid model built by util/dragonModel.py from the tiles of Dragon.jpeg +# Units: 0.01 per image pixel; Y up, faces right (+X), resting on Y = 0, symmetric about Z = 0 +mtllib DragonModel.mtl +v 0.70500 6.60000 -0.38000 +v 0.74500 6.56000 -0.38000 +v 1.22500 6.31000 -0.38000 +v 1.37500 6.16000 -0.38000 +v 1.95500 5.78000 -0.38000 +v 2.06500 5.67000 -0.38000 +v 1.43500 5.02000 -0.38000 +v 1.47500 4.98000 -0.38000 +v 2.50500 5.19000 -0.38000 +v 3.02500 4.76000 -0.38000 +v 3.06500 4.80000 -0.38000 +v 3.32500 5.32000 -0.38000 +v 3.25500 5.39000 -0.38000 +v 3.20500 5.37000 -0.38000 +v 2.76500 5.47000 -0.38000 +v 2.65500 5.58000 -0.38000 +v 2.76500 5.69000 -0.38000 +v 3.09500 5.76000 -0.38000 +v 3.32500 5.49000 -0.38000 +v 3.36500 5.53000 -0.38000 +v 3.55500 5.92000 -0.38000 +v 3.20500 6.40000 -0.38000 +v 3.16500 6.29000 -0.38000 +v 3.05500 6.18000 -0.38000 +v 2.66500 6.21000 -0.38000 +v 2.41500 6.26000 -0.38000 +v 1.92500 6.59000 -0.38000 +v 0.74500 6.64000 -0.38000 +v 0.61590 6.60000 0.00000 +v 0.70730 6.50860 0.00000 +v 1.18730 6.25860 0.00000 +v 1.33504 6.11086 0.00000 +v 1.91504 5.73086 0.00000 +v 1.97660 5.66931 0.00000 +v 1.34660 5.01931 0.00000 +v 1.45440 4.91150 0.00000 +v 2.48807 5.12225 0.00000 +v 3.02902 4.67493 0.00000 +v 3.11678 4.76268 0.00000 +v 3.40166 5.33244 0.00000 +v 3.27017 5.46392 0.00000 +v 3.19982 5.43578 0.00000 +v 2.79669 5.52740 0.00000 +v 2.74410 5.58000 0.00000 +v 2.79634 5.63225 0.00000 +v 3.07137 5.69059 0.00000 +v 3.32129 5.39720 0.00000 +v 3.41701 5.49292 0.00000 +v 3.62824 5.92649 0.00000 +v 3.18622 6.53269 0.00000 +v 3.11057 6.32466 0.00000 +v 3.03094 6.24504 0.00000 +v 2.67362 6.27252 0.00000 +v 2.43973 6.31930 0.00000 +v 1.94544 6.65219 0.00000 +v 0.72002 6.70412 0.00000 +v 0.70500 6.60000 0.38000 +v 0.74500 6.56000 0.38000 +v 1.22500 6.31000 0.38000 +v 1.37500 6.16000 0.38000 +v 1.95500 5.78000 0.38000 +v 2.06500 5.67000 0.38000 +v 1.43500 5.02000 0.38000 +v 1.47500 4.98000 0.38000 +v 2.50500 5.19000 0.38000 +v 3.02500 4.76000 0.38000 +v 3.06500 4.80000 0.38000 +v 3.32500 5.32000 0.38000 +v 3.25500 5.39000 0.38000 +v 3.20500 5.37000 0.38000 +v 2.76500 5.47000 0.38000 +v 2.65500 5.58000 0.38000 +v 2.76500 5.69000 0.38000 +v 3.09500 5.76000 0.38000 +v 3.32500 5.49000 0.38000 +v 3.36500 5.53000 0.38000 +v 3.55500 5.92000 0.38000 +v 3.20500 6.40000 0.38000 +v 3.16500 6.29000 0.38000 +v 3.05500 6.18000 0.38000 +v 2.66500 6.21000 0.38000 +v 2.41500 6.26000 0.38000 +v 1.92500 6.59000 0.38000 +v 0.74500 6.64000 0.38000 +v 2.37220 6.26650 0.38000 +v 1.92740 6.54950 0.38000 +v 0.75300 6.59700 0.38000 +v 1.45590 6.21950 0.38000 +v 2.37220 6.26650 0.39000 +v 1.92740 6.54950 0.39000 +v 0.75300 6.59700 0.39000 +v 1.45590 6.21950 0.39000 +v 2.28932 6.28834 0.42000 +v 1.91811 6.51985 0.42000 +v 0.83569 6.57442 0.42000 +v 1.46272 6.24989 0.42000 +v 2.28932 6.28834 -0.42000 +v 1.91811 6.51985 -0.42000 +v 0.83569 6.57442 -0.42000 +v 1.46272 6.24989 -0.42000 +v 2.37220 6.26650 -0.39000 +v 1.92740 6.54950 -0.39000 +v 0.75300 6.59700 -0.39000 +v 1.45590 6.21950 -0.39000 +v 2.37220 6.26650 -0.38000 +v 1.92740 6.54950 -0.38000 +v 0.75300 6.59700 -0.38000 +v 1.45590 6.21950 -0.38000 +v 1.81500 6.25020 0.38000 +v 1.36960 6.20970 0.38000 +v 2.24650 5.61750 0.38000 +v 3.18500 5.80730 0.38000 +v 3.18500 6.12660 0.38000 +v 1.81500 6.25020 0.39000 +v 1.36960 6.20970 0.39000 +v 2.24650 5.61750 0.39000 +v 3.18500 5.80730 0.39000 +v 3.18500 6.12660 0.39000 +v 1.81501 6.22008 0.42000 +v 1.45261 6.18836 0.42000 +v 2.25287 5.64940 0.42000 +v 3.15500 5.83184 0.42000 +v 3.15500 6.09918 0.42000 +v 1.81501 6.22008 -0.42000 +v 1.45261 6.18836 -0.42000 +v 2.25287 5.64940 -0.42000 +v 3.15500 5.83184 -0.42000 +v 3.15500 6.09918 -0.42000 +v 1.81500 6.25020 -0.39000 +v 1.36960 6.20970 -0.39000 +v 2.24650 5.61750 -0.39000 +v 3.18500 5.80730 -0.39000 +v 3.18500 6.12660 -0.39000 +v 1.81500 6.25020 -0.38000 +v 1.36960 6.20970 -0.38000 +v 2.24650 5.61750 -0.38000 +v 3.18500 5.80730 -0.38000 +v 3.18500 6.12660 -0.38000 +v 3.32850 5.52760 0.38000 +v 3.52140 5.91340 0.38000 +v 3.21270 6.35630 0.38000 +v 3.17420 5.78000 0.38000 +v 3.32850 5.52760 0.39000 +v 3.52140 5.91340 0.39000 +v 3.21270 6.35630 0.39000 +v 3.17420 5.78000 0.39000 +v 3.32587 5.58942 0.42000 +v 3.48659 5.91087 0.42000 +v 3.23565 6.27371 0.42000 +v 3.20477 5.78751 0.42000 +v 3.32587 5.58942 -0.42000 +v 3.48659 5.91087 -0.42000 +v 3.23565 6.27371 -0.42000 +v 3.20477 5.78751 -0.42000 +v 3.32850 5.52760 -0.39000 +v 3.52140 5.91340 -0.39000 +v 3.21270 6.35630 -0.39000 +v 3.17420 5.78000 -0.39000 +v 3.32850 5.52760 -0.38000 +v 3.52140 5.91340 -0.38000 +v 3.21270 6.35630 -0.38000 +v 3.17420 5.78000 -0.38000 +v 2.17370 5.96880 0.38000 +v 2.50350 6.22000 0.38000 +v 1.87540 6.22000 0.38000 +v 2.17370 5.96880 0.39000 +v 2.50350 6.22000 0.39000 +v 1.87540 6.22000 0.39000 +v 2.17464 6.00723 0.42000 +v 2.42229 6.19259 0.42000 +v 1.95592 6.19061 0.42000 +v 2.17464 6.00723 -0.42000 +v 2.42229 6.19259 -0.42000 +v 1.95592 6.19061 -0.42000 +v 2.17370 5.96880 -0.39000 +v 2.50350 6.22000 -0.39000 +v 1.87540 6.22000 -0.39000 +v 2.17370 5.96880 -0.38000 +v 2.50350 6.22000 -0.38000 +v 1.87540 6.22000 -0.38000 +v 2.04140 5.58350 0.38000 +v 1.48390 5.01340 0.38000 +v 2.49750 5.22880 0.38000 +v 2.04140 5.58350 0.39000 +v 1.48390 5.01340 0.39000 +v 2.49750 5.22880 0.39000 +v 2.04412 5.54338 0.42000 +v 1.55900 5.05472 0.42000 +v 2.42815 5.24473 0.42000 +v 2.04412 5.54338 -0.42000 +v 1.55900 5.05472 -0.42000 +v 2.42815 5.24473 -0.42000 +v 2.04140 5.58350 -0.39000 +v 1.48390 5.01340 -0.39000 +v 2.49750 5.22880 -0.39000 +v 2.04140 5.58350 -0.38000 +v 1.48390 5.01340 -0.38000 +v 2.49750 5.22880 -0.38000 +v 3.29410 5.31780 0.38000 +v 2.04850 5.59590 0.38000 +v 3.02810 4.79770 0.38000 +v 3.29410 5.31780 0.39000 +v 2.04850 5.59590 0.39000 +v 3.02810 4.79770 0.39000 +v 3.24975 5.29696 0.42000 +v 2.12562 5.55849 0.42000 +v 3.01829 4.84439 0.42000 +v 3.24975 5.29696 -0.42000 +v 2.12562 5.55849 -0.42000 +v 3.01829 4.84439 -0.42000 +v 3.29410 5.31780 -0.39000 +v 2.04850 5.59590 -0.39000 +v 3.02810 4.79770 -0.39000 +v 3.29410 5.31780 -0.38000 +v 2.04850 5.59590 -0.38000 +v 3.02810 4.79770 -0.38000 +v 0.14500 5.65000 -0.32000 +v 0.01500 4.36000 -0.32000 +v 0.06500 4.32000 -0.32000 +v 0.29500 4.59000 -0.32000 +v 0.40500 4.48000 -0.32000 +v 0.64500 3.95000 -0.32000 +v 1.91500 2.24000 -0.32000 +v 1.95500 2.28000 -0.32000 +v 1.97500 2.86000 -0.32000 +v 2.08500 2.97000 -0.32000 +v 2.34500 2.63000 -0.32000 +v 2.64500 3.15000 -0.32000 +v 2.81500 3.35000 -0.32000 +v 1.42500 5.00000 -0.32000 +v 2.05500 5.65000 -0.32000 +v 1.42500 6.09000 -0.32000 +v 1.29500 6.22000 -0.32000 +v 0.09427 5.68512 0.00000 +v -0.04167 4.33619 0.00000 +v 0.07215 4.24512 0.00000 +v 0.29818 4.51046 0.00000 +v 0.35986 4.44877 0.00000 +v 0.59820 3.92245 0.00000 +v 1.90895 2.15758 0.00000 +v 2.00823 2.25687 0.00000 +v 2.02823 2.83687 0.00000 +v 2.07957 2.88820 0.00000 +v 2.35130 2.53286 0.00000 +v 2.68932 3.11877 0.00000 +v 2.88574 3.34984 0.00000 +v 1.49774 4.99746 0.00000 +v 2.13791 5.65796 0.00000 +v 1.45982 6.13155 0.00000 +v 1.30576 6.28560 0.00000 +v 0.14500 5.65000 0.32000 +v 0.01500 4.36000 0.32000 +v 0.06500 4.32000 0.32000 +v 0.29500 4.59000 0.32000 +v 0.40500 4.48000 0.32000 +v 0.64500 3.95000 0.32000 +v 1.91500 2.24000 0.32000 +v 1.95500 2.28000 0.32000 +v 1.97500 2.86000 0.32000 +v 2.08500 2.97000 0.32000 +v 2.34500 2.63000 0.32000 +v 2.64500 3.15000 0.32000 +v 2.81500 3.35000 0.32000 +v 1.42500 5.00000 0.32000 +v 2.05500 5.65000 0.32000 +v 1.42500 6.09000 0.32000 +v 1.29500 6.22000 0.32000 +v 0.07120 4.35890 0.32000 +v 1.30060 6.17980 0.32000 +v 0.18720 5.64620 0.32000 +v 0.07120 4.35890 0.33000 +v 1.30060 6.17980 0.33000 +v 0.18720 5.64620 0.33000 +v 0.09994 4.43965 0.36000 +v 1.23571 6.12380 0.36000 +v 0.21555 5.62652 0.36000 +v 0.09994 4.43965 -0.36000 +v 1.23571 6.12380 -0.36000 +v 0.21555 5.62652 -0.36000 +v 0.07120 4.35890 -0.33000 +v 1.30060 6.17980 -0.33000 +v 0.18720 5.64620 -0.33000 +v 0.07120 4.35890 -0.32000 +v 1.30060 6.17980 -0.32000 +v 0.18720 5.64620 -0.32000 +v 0.73630 5.17900 0.32000 +v 0.92890 4.91420 0.32000 +v 1.44610 5.05430 0.32000 +v 2.01640 5.64610 0.32000 +v 1.35430 6.10610 0.32000 +v 0.73630 5.17900 0.33000 +v 0.92890 4.91420 0.33000 +v 1.44610 5.05430 0.33000 +v 2.01640 5.64610 0.33000 +v 1.35430 6.10610 0.33000 +v 0.77285 5.17975 0.36000 +v 0.94100 4.94856 0.36000 +v 1.43025 5.08109 0.36000 +v 1.97036 5.64156 0.36000 +v 1.36230 6.06401 0.36000 +v 0.77285 5.17975 -0.36000 +v 0.94100 4.94856 -0.36000 +v 1.43025 5.08109 -0.36000 +v 1.97036 5.64156 -0.36000 +v 1.36230 6.06401 -0.36000 +v 0.73630 5.17900 -0.33000 +v 0.92890 4.91420 -0.33000 +v 1.44610 5.05430 -0.33000 +v 2.01640 5.64610 -0.33000 +v 1.35430 6.10610 -0.33000 +v 0.73630 5.17900 -0.32000 +v 0.92890 4.91420 -0.32000 +v 1.44610 5.05430 -0.32000 +v 2.01640 5.64610 -0.32000 +v 1.35430 6.10610 -0.32000 +v 0.96360 4.82790 0.32000 +v 1.60430 3.91270 0.32000 +v 1.34280 4.98480 0.32000 +v 0.96360 4.82790 0.33000 +v 1.60430 3.91270 0.33000 +v 1.34280 4.98480 0.33000 +v 1.00962 4.81447 0.36000 +v 1.56896 3.99079 0.36000 +v 1.32194 4.94370 0.36000 +v 1.00962 4.81447 -0.36000 +v 1.56896 3.99079 -0.36000 +v 1.32194 4.94370 -0.36000 +v 0.96360 4.82790 -0.33000 +v 1.60430 3.91270 -0.33000 +v 1.34280 4.98480 -0.33000 +v 0.96360 4.82790 -0.32000 +v 1.60430 3.91270 -0.32000 +v 1.34280 4.98480 -0.32000 +v 0.67430 5.14050 0.32000 +v 0.35930 4.67380 0.32000 +v 0.68030 3.96970 0.32000 +v 1.92250 2.27100 0.32000 +v 1.94530 3.33230 0.32000 +v 0.67430 5.14050 0.33000 +v 0.35930 4.67380 0.33000 +v 0.68030 3.96970 0.33000 +v 1.92250 2.27100 0.33000 +v 1.94530 3.33230 0.33000 +v 0.67480 5.08762 0.36000 +v 0.39357 4.67095 0.36000 +v 0.70633 3.98493 0.36000 +v 1.89676 2.35276 0.36000 +v 1.91510 3.32310 0.36000 +v 0.67480 5.08762 -0.36000 +v 0.39357 4.67095 -0.36000 +v 0.70633 3.98493 -0.36000 +v 1.89676 2.35276 -0.36000 +v 1.91510 3.32310 -0.36000 +v 0.67430 5.14050 -0.33000 +v 0.35930 4.67380 -0.33000 +v 0.68030 3.96970 -0.33000 +v 1.92250 2.27100 -0.33000 +v 1.94530 3.33230 -0.33000 +v 0.67430 5.14050 -0.32000 +v 0.35930 4.67380 -0.32000 +v 0.68030 3.96970 -0.32000 +v 1.92250 2.27100 -0.32000 +v 1.94530 3.33230 -0.32000 +v 1.77750 3.63490 0.32000 +v 2.36480 2.64170 0.32000 +v 2.78390 3.34390 0.32000 +v 1.47720 4.90530 0.32000 +v 1.77750 3.63490 0.33000 +v 2.36480 2.64170 0.33000 +v 2.78390 3.34390 0.33000 +v 1.47720 4.90530 0.33000 +v 1.80565 3.64624 0.36000 +v 2.36492 2.70044 0.36000 +v 2.74722 3.34098 0.36000 +v 1.51560 4.82867 0.36000 +v 1.80565 3.64624 -0.36000 +v 2.36492 2.70044 -0.36000 +v 2.74722 3.34098 -0.36000 +v 1.51560 4.82867 -0.36000 +v 1.77750 3.63490 -0.33000 +v 2.36480 2.64170 -0.33000 +v 2.78390 3.34390 -0.33000 +v 1.47720 4.90530 -0.33000 +v 1.77750 3.63490 -0.32000 +v 2.36480 2.64170 -0.32000 +v 2.78390 3.34390 -0.32000 +v 1.47720 4.90530 -0.32000 +v 0.53500 3.13000 -0.51000 +v 0.42500 3.02000 -0.51000 +v 0.36500 3.08000 -0.51000 +v -1.53500 3.25000 -0.51000 +v -1.57500 3.21000 -0.51000 +v -1.03500 2.15000 -0.51000 +v -0.89500 2.01000 -0.51000 +v 0.80500 0.75000 -0.51000 +v 0.86500 0.81000 -0.51000 +v 0.91500 0.76000 -0.51000 +v 2.56500 0.66000 -0.51000 +v 2.60500 0.70000 -0.51000 +v 2.45500 0.88000 -0.51000 +v 2.96500 2.32000 -0.51000 +v 2.77500 3.36000 -0.51000 +v 2.73500 3.40000 -0.51000 +v 2.68500 3.35000 -0.51000 +v 2.47500 2.95000 -0.51000 +v 2.33500 2.80000 -0.51000 +v 2.22500 2.91000 -0.51000 +v 2.01500 3.26000 -0.51000 +v 1.96500 3.31000 -0.51000 +v 1.92500 3.27000 -0.51000 +v 1.93500 2.51000 -0.51000 +v 1.82500 2.40000 -0.51000 +v 0.81500 3.74000 -0.51000 +v 0.46620 3.17787 0.00000 +v 0.42500 3.13667 0.00000 +v 0.40217 3.15950 0.00000 +v -1.56606 3.33561 0.00000 +v -1.67572 3.22596 0.00000 +v -1.10258 2.10091 0.00000 +v -0.94902 1.94735 0.00000 +v 0.81303 0.64136 0.00000 +v 0.86500 0.69333 0.00000 +v 0.87878 0.67954 0.00000 +v 2.59708 0.57540 0.00000 +v 2.71661 0.69494 0.00000 +v 2.54845 0.89673 0.00000 +v 3.05011 2.31319 0.00000 +v 2.85153 3.40014 0.00000 +v 2.73500 3.51667 0.00000 +v 2.61779 3.39946 0.00000 +v 2.40711 2.99818 0.00000 +v 2.33295 2.91872 0.00000 +v 2.29052 2.96115 0.00000 +v 2.08052 3.31115 0.00000 +v 1.96500 3.42667 0.00000 +v 1.84205 3.30372 0.00000 +v 1.85205 2.54372 0.00000 +v 1.83376 2.52544 0.00000 +v 0.79768 3.90004 0.00000 +v 0.53500 3.13000 0.51000 +v 0.42500 3.02000 0.51000 +v 0.36500 3.08000 0.51000 +v -1.53500 3.25000 0.51000 +v -1.57500 3.21000 0.51000 +v -1.03500 2.15000 0.51000 +v -0.89500 2.01000 0.51000 +v 0.80500 0.75000 0.51000 +v 0.86500 0.81000 0.51000 +v 0.91500 0.76000 0.51000 +v 2.56500 0.66000 0.51000 +v 2.60500 0.70000 0.51000 +v 2.45500 0.88000 0.51000 +v 2.96500 2.32000 0.51000 +v 2.77500 3.36000 0.51000 +v 2.73500 3.40000 0.51000 +v 2.68500 3.35000 0.51000 +v 2.47500 2.95000 0.51000 +v 2.33500 2.80000 0.51000 +v 2.22500 2.91000 0.51000 +v 2.01500 3.26000 0.51000 +v 1.96500 3.31000 0.51000 +v 1.92500 3.27000 0.51000 +v 1.93500 2.51000 0.51000 +v 1.82500 2.40000 0.51000 +v 0.81500 3.74000 0.51000 +v 0.81590 3.69070 0.51000 +v 0.54830 3.06250 0.51000 +v 1.87920 1.55060 0.51000 +v 1.91580 2.17190 0.51000 +v 0.81590 3.69070 0.52000 +v 0.54830 3.06250 0.52000 +v 1.87920 1.55060 0.52000 +v 1.91580 2.17190 0.52000 +v 0.82271 3.63014 0.55000 +v 0.58331 3.06813 0.55000 +v 1.85354 1.62515 0.55000 +v 1.88522 2.16298 0.55000 +v 0.82271 3.63014 -0.55000 +v 0.58331 3.06813 -0.55000 +v 1.85354 1.62515 -0.55000 +v 1.88522 2.16298 -0.55000 +v 0.81590 3.69070 -0.52000 +v 0.54830 3.06250 -0.52000 +v 1.87920 1.55060 -0.52000 +v 1.91580 2.17190 -0.52000 +v 0.81590 3.69070 -0.51000 +v 0.54830 3.06250 -0.51000 +v 1.87920 1.55060 -0.51000 +v 1.91580 2.17190 -0.51000 +v -1.53380 3.20620 0.51000 +v -0.98000 2.13170 0.51000 +v 0.37130 3.04000 0.51000 +v -1.53380 3.20620 0.52000 +v -0.98000 2.13170 0.52000 +v 0.37130 3.04000 0.52000 +v -1.48221 3.17159 0.55000 +v -0.96877 2.17540 0.55000 +v 0.28830 3.01860 0.55000 +v -1.48221 3.17159 -0.55000 +v -0.96877 2.17540 -0.55000 +v 0.28830 3.01860 -0.55000 +v -1.53380 3.20620 -0.52000 +v -0.98000 2.13170 -0.52000 +v 0.37130 3.04000 -0.52000 +v -1.53380 3.20620 -0.51000 +v -0.98000 2.13170 -0.51000 +v 0.37130 3.04000 -0.51000 +v 0.83520 2.59470 0.51000 +v 0.51260 2.96690 0.51000 +v 0.05410 2.72420 0.51000 +v -0.90340 2.05080 0.51000 +v 0.81410 0.78140 0.51000 +v 0.83520 2.59470 0.52000 +v 0.51260 2.96690 0.52000 +v 0.05410 2.72420 0.52000 +v -0.90340 2.05080 0.52000 +v 0.81410 0.78140 0.52000 +v 0.80507 2.58366 0.55000 +v 0.50555 2.92923 0.55000 +v 0.06981 2.69857 0.55000 +v -0.85211 2.05020 0.55000 +v 0.78478 0.84037 0.55000 +v 0.80507 2.58366 -0.55000 +v 0.50555 2.92923 -0.55000 +v 0.06981 2.69857 -0.55000 +v -0.85211 2.05020 -0.55000 +v 0.78478 0.84037 -0.55000 +v 0.83520 2.59470 -0.52000 +v 0.51260 2.96690 -0.52000 +v 0.05410 2.72420 -0.52000 +v -0.90340 2.05080 -0.52000 +v 0.81410 0.78140 -0.52000 +v 0.83520 2.59470 -0.51000 +v 0.51260 2.96690 -0.51000 +v 0.05410 2.72420 -0.51000 +v -0.90340 2.05080 -0.51000 +v 0.81410 0.78140 -0.51000 +v 2.74490 3.35780 0.51000 +v 2.33820 2.62830 0.51000 +v 2.68710 2.09930 0.51000 +v 2.92820 2.31500 0.51000 +v 2.74490 3.35780 0.52000 +v 2.33820 2.62830 0.52000 +v 2.68710 2.09930 0.52000 +v 2.92820 2.31500 0.52000 +v 2.73063 3.27328 0.55000 +v 2.37328 2.62961 0.55000 +v 2.69300 2.14484 0.55000 +v 2.89577 2.32624 0.55000 +v 2.73063 3.27328 -0.55000 +v 2.37328 2.62961 -0.55000 +v 2.69300 2.14484 -0.55000 +v 2.89577 2.32624 -0.55000 +v 2.74490 3.35780 -0.52000 +v 2.33820 2.62830 -0.52000 +v 2.68710 2.09930 -0.52000 +v 2.92820 2.31500 -0.52000 +v 2.74490 3.35780 -0.51000 +v 2.33820 2.62830 -0.51000 +v 2.68710 2.09930 -0.51000 +v 2.92820 2.31500 -0.51000 +v 2.68730 2.09100 0.51000 +v 1.97310 3.26600 0.51000 +v 1.99610 1.52660 0.51000 +v 2.68730 2.09100 0.52000 +v 1.97310 3.26600 0.52000 +v 1.99610 1.52660 0.52000 +v 2.64810 2.09773 0.55000 +v 1.99676 3.18362 0.55000 +v 2.02528 1.58915 0.55000 +v 2.64810 2.09773 -0.55000 +v 1.99676 3.18362 -0.55000 +v 2.02528 1.58915 -0.55000 +v 2.68730 2.09100 -0.52000 +v 1.97310 3.26600 -0.52000 +v 1.99610 1.52660 -0.52000 +v 2.68730 2.09100 -0.51000 +v 1.97310 3.26600 -0.51000 +v 1.99610 1.52660 -0.51000 +v 0.92450 0.79230 0.51000 +v 2.56980 0.69430 0.51000 +v 0.94630 2.50300 0.51000 +v 0.92450 0.79230 0.52000 +v 2.56980 0.69430 0.52000 +v 0.94630 2.50300 0.52000 +v 0.95486 0.82054 0.55000 +v 2.49871 0.72859 0.55000 +v 0.97532 2.42576 0.55000 +v 0.95486 0.82054 -0.55000 +v 2.49871 0.72859 -0.55000 +v 0.97532 2.42576 -0.55000 +v 0.92450 0.79230 -0.52000 +v 2.56980 0.69430 -0.52000 +v 0.94630 2.50300 -0.52000 +v 0.92450 0.79230 -0.51000 +v 2.56980 0.69430 -0.51000 +v 0.94630 2.50300 -0.51000 +v 1.95750 1.44360 0.51000 +v 2.45110 0.90300 0.51000 +v 2.90950 2.25450 0.51000 +v 1.95750 1.44360 0.52000 +v 2.45110 0.90300 0.52000 +v 2.90950 2.25450 0.52000 +v 2.00059 1.44090 0.55000 +v 2.43901 0.96074 0.55000 +v 2.86138 2.18357 0.55000 +v 2.00059 1.44090 -0.55000 +v 2.43901 0.96074 -0.55000 +v 2.86138 2.18357 -0.55000 +v 1.95750 1.44360 -0.52000 +v 2.45110 0.90300 -0.52000 +v 2.90950 2.25450 -0.52000 +v 1.95750 1.44360 -0.51000 +v 2.45110 0.90300 -0.51000 +v 2.90950 2.25450 -0.51000 +v -4.19500 2.90000 -0.22000 +v -4.23500 2.93000 -0.22000 +v -4.27500 2.89000 -0.22000 +v -4.41500 1.39000 -0.22000 +v -4.37500 1.35000 -0.22000 +v -4.39500 1.32000 -0.22000 +v -3.17500 0.13000 -0.22000 +v 0.22500 0.07000 -0.22000 +v 0.26500 0.11000 -0.22000 +v 0.23500 0.15000 -0.22000 +v -0.50500 1.02000 -0.22000 +v -0.53500 1.00000 -0.22000 +v -0.58500 1.05000 -0.22000 +v -2.16500 0.77000 -0.22000 +v -2.26500 0.67000 -0.22000 +v -3.41500 1.45000 -0.22000 +v -3.55500 1.59000 -0.22000 +v -3.68500 2.17000 -0.22000 +v -3.08500 2.84000 -0.22000 +v -2.86500 3.85000 -0.22000 +v -2.91500 3.89000 -0.22000 +v -4.19536 2.94902 0.00000 +v -4.23866 2.98149 0.00000 +v -4.31252 2.90763 0.00000 +v -4.45553 1.37538 0.00000 +v -4.42519 1.34503 0.00000 +v -4.44541 1.31469 0.00000 +v -3.19116 0.09128 0.00000 +v 0.24087 0.03071 0.00000 +v 0.31649 0.10634 0.00000 +v 0.26548 0.17436 0.00000 +v -0.49777 1.07169 0.00000 +v -0.53003 1.05019 0.00000 +v -0.57179 1.09195 0.00000 +v -2.18390 0.80626 0.00000 +v -2.26978 0.72037 0.00000 +v -3.39004 1.48020 0.00000 +v -3.51942 1.60957 0.00000 +v -3.64255 2.15894 0.00000 +v -3.04908 2.82165 0.00000 +v -2.82174 3.86534 0.00000 +v -2.91459 3.93962 0.00000 +v -4.19500 2.90000 0.22000 +v -4.23500 2.93000 0.22000 +v -4.27500 2.89000 0.22000 +v -4.41500 1.39000 0.22000 +v -4.37500 1.35000 0.22000 +v -4.39500 1.32000 0.22000 +v -3.17500 0.13000 0.22000 +v 0.22500 0.07000 0.22000 +v 0.26500 0.11000 0.22000 +v 0.23500 0.15000 0.22000 +v -0.50500 1.02000 0.22000 +v -0.53500 1.00000 0.22000 +v -0.58500 1.05000 0.22000 +v -2.16500 0.77000 0.22000 +v -2.26500 0.67000 0.22000 +v -3.41500 1.45000 0.22000 +v -3.55500 1.59000 0.22000 +v -3.68500 2.17000 0.22000 +v -3.08500 2.84000 0.22000 +v -2.86500 3.85000 0.22000 +v -2.91500 3.89000 0.22000 +v 0.23300 0.10780 0.22000 +v -0.49920 0.97510 0.22000 +v -1.55810 0.15280 0.22000 +v 0.23300 0.10780 0.23000 +v -0.49920 0.97510 0.23000 +v -1.55810 0.15280 0.23000 +v 0.16700 0.13947 0.26000 +v -0.50355 0.93374 0.26000 +v -1.47667 0.17957 0.26000 +v 0.16700 0.13947 -0.26000 +v -0.50355 0.93374 -0.26000 +v -1.47667 0.17957 -0.26000 +v 0.23300 0.10780 -0.23000 +v -0.49920 0.97510 -0.23000 +v -1.55810 0.15280 -0.23000 +v 0.23300 0.10780 -0.22000 +v -0.49920 0.97510 -0.22000 +v -1.55810 0.15280 -0.22000 +v -1.54470 0.24930 0.22000 +v -0.58470 1.00780 0.22000 +v -2.16090 0.72330 0.22000 +v -1.54470 0.24930 0.23000 +v -0.58470 1.00780 0.23000 +v -2.16090 0.72330 0.23000 +v -1.54495 0.28734 0.26000 +v -0.66284 0.97257 0.26000 +v -2.08895 0.70580 0.26000 +v -1.54495 0.28734 -0.26000 +v -0.66284 0.97257 -0.26000 +v -2.08895 0.70580 -0.26000 +v -1.54470 0.24930 -0.23000 +v -0.58470 1.00780 -0.23000 +v -2.16090 0.72330 -0.23000 +v -1.54470 0.24930 -0.22000 +v -0.58470 1.00780 -0.22000 +v -2.16090 0.72330 -0.22000 +v -1.51280 0.11360 0.22000 +v -3.48900 1.46210 0.22000 +v -3.16650 0.16090 0.22000 +v -1.51280 0.11360 0.23000 +v -3.48900 1.46210 0.23000 +v -3.16650 0.16090 0.23000 +v -1.59433 0.14005 0.26000 +v -3.44097 1.39300 0.26000 +v -3.14286 0.19024 0.26000 +v -1.59433 0.14005 -0.26000 +v -3.44097 1.39300 -0.26000 +v -3.14286 0.19024 -0.26000 +v -1.51280 0.11360 -0.23000 +v -3.48900 1.46210 -0.23000 +v -3.16650 0.16090 -0.23000 +v -1.51280 0.11360 -0.22000 +v -3.48900 1.46210 -0.22000 +v -3.16650 0.16090 -0.22000 +v -4.34800 1.31620 0.22000 +v -3.27470 0.25420 0.22000 +v -3.71540 2.15230 0.22000 +v -4.34800 1.31620 0.23000 +v -3.27470 0.25420 0.23000 +v -3.71540 2.15230 0.23000 +v -4.30823 1.31905 0.26000 +v -3.31650 0.32903 0.26000 +v -3.73013 2.08311 0.26000 +v -4.30823 1.31905 -0.26000 +v -3.31650 0.32903 -0.26000 +v -3.73013 2.08311 -0.26000 +v -4.34800 1.31620 -0.23000 +v -3.27470 0.25420 -0.23000 +v -3.71540 2.15230 -0.23000 +v -4.34800 1.31620 -0.22000 +v -3.27470 0.25420 -0.22000 +v -3.71540 2.15230 -0.22000 +v -4.23430 2.88360 0.22000 +v -4.35570 1.38990 0.22000 +v -3.79710 2.10640 0.22000 +v -4.23430 2.88360 0.23000 +v -4.35570 1.38990 0.23000 +v -3.79710 2.10640 0.23000 +v -4.21596 2.79987 0.26000 +v -4.32457 1.46976 0.26000 +v -3.83304 2.10910 0.26000 +v -4.21596 2.79987 -0.26000 +v -4.32457 1.46976 -0.26000 +v -3.83304 2.10910 -0.26000 +v -4.23430 2.88360 -0.23000 +v -4.35570 1.38990 -0.23000 +v -3.79710 2.10640 -0.23000 +v -4.23430 2.88360 -0.22000 +v -4.35570 1.38990 -0.22000 +v -3.79710 2.10640 -0.22000 +v -4.12680 2.91050 0.22000 +v -3.71260 2.17180 0.22000 +v -3.11790 2.83140 0.22000 +v -2.91480 3.84710 0.22000 +v -4.12680 2.91050 0.23000 +v -3.71260 2.17180 0.23000 +v -3.11790 2.83140 0.23000 +v -2.91480 3.84710 0.23000 +v -4.08797 2.90259 0.26000 +v -3.70688 2.22294 0.26000 +v -3.14570 2.84537 0.26000 +v -2.95995 3.77429 0.26000 +v -4.08797 2.90259 -0.26000 +v -3.70688 2.22294 -0.26000 +v -3.14570 2.84537 -0.26000 +v -2.95995 3.77429 -0.26000 +v -4.12680 2.91050 -0.23000 +v -3.71260 2.17180 -0.23000 +v -3.11790 2.83140 -0.23000 +v -2.91480 3.84710 -0.23000 +v -4.12680 2.91050 -0.22000 +v -3.71260 2.17180 -0.22000 +v -3.11790 2.83140 -0.22000 +v -2.91480 3.84710 -0.22000 +v 3.54500 1.01000 -0.51000 +v 2.45500 0.95000 -0.51000 +v 2.41500 0.91000 -0.51000 +v 2.45500 0.84000 -0.51000 +v 2.34500 0.73000 -0.51000 +v 1.88500 0.73000 -0.51000 +v 1.84500 0.69000 -0.51000 +v 2.96500 0.05000 -0.51000 +v 3.07500 0.16000 -0.51000 +v 4.35500 0.09000 -0.51000 +v 4.39500 0.13000 -0.51000 +v 4.35500 0.18000 -0.51000 +v 3.57500 1.04000 -0.51000 +v 3.52797 1.04812 -0.29000 +v 2.43797 0.98812 -0.29000 +v 2.36636 0.91651 -0.29000 +v 2.40636 0.84651 -0.29000 +v 2.32885 0.76900 -0.29000 +v 1.86885 0.76900 -0.29000 +v 1.78132 0.68147 -0.29000 +v 2.97151 0.00136 -0.29000 +v 3.09026 0.12011 -0.29000 +v 4.37026 0.05011 -0.29000 +v 4.44726 0.12711 -0.29000 +v 4.38470 0.20531 -0.29000 +v 3.57638 1.09653 -0.29000 +v 3.54500 1.01000 -0.07000 +v 2.45500 0.95000 -0.07000 +v 2.41500 0.91000 -0.07000 +v 2.45500 0.84000 -0.07000 +v 2.34500 0.73000 -0.07000 +v 1.88500 0.73000 -0.07000 +v 1.84500 0.69000 -0.07000 +v 2.96500 0.05000 -0.07000 +v 3.07500 0.16000 -0.07000 +v 4.35500 0.09000 -0.07000 +v 4.39500 0.13000 -0.07000 +v 4.35500 0.18000 -0.07000 +v 3.57500 1.04000 -0.07000 +v 2.89000 0.18030 -0.07000 +v 3.53350 0.97590 -0.07000 +v 2.45710 0.90570 -0.07000 +v 2.89000 0.18030 -0.06000 +v 3.53350 0.97590 -0.06000 +v 2.45710 0.90570 -0.06000 +v 2.89372 0.23261 -0.03000 +v 3.46710 0.94151 -0.03000 +v 2.50800 0.87896 -0.03000 +v 2.89372 0.23261 -0.55000 +v 3.46710 0.94151 -0.55000 +v 2.50800 0.87896 -0.55000 +v 2.89000 0.18030 -0.52000 +v 3.53350 0.97590 -0.52000 +v 2.45710 0.90570 -0.52000 +v 2.89000 0.18030 -0.51000 +v 3.53350 0.97590 -0.51000 +v 2.45710 0.90570 -0.51000 +v 2.97380 0.08820 -0.07000 +v 2.55590 0.69000 -0.07000 +v 1.89060 0.69000 -0.07000 +v 2.97380 0.08820 -0.06000 +v 2.55590 0.69000 -0.06000 +v 1.89060 0.69000 -0.06000 +v 2.91024 0.14571 -0.03000 +v 2.54021 0.66000 -0.03000 +v 1.97357 0.66850 -0.03000 +v 2.91024 0.14571 -0.55000 +v 2.54021 0.66000 -0.55000 +v 1.97357 0.66850 -0.55000 +v 2.97380 0.08820 -0.52000 +v 2.55590 0.69000 -0.52000 +v 1.89060 0.69000 -0.52000 +v 2.97380 0.08820 -0.51000 +v 2.55590 0.69000 -0.51000 +v 1.89060 0.69000 -0.51000 +v 4.35770 0.12090 -0.07000 +v 3.58310 0.99230 -0.07000 +v 2.95590 0.19410 -0.07000 +v 4.35770 0.12090 -0.06000 +v 3.58310 0.99230 -0.06000 +v 2.95590 0.19410 -0.06000 +v 4.28760 0.15460 -0.03000 +v 3.58452 0.94555 -0.03000 +v 3.01522 0.22104 -0.03000 +v 4.28760 0.15460 -0.55000 +v 3.58452 0.94555 -0.55000 +v 3.01522 0.22104 -0.55000 +v 4.35770 0.12090 -0.52000 +v 3.58310 0.99230 -0.52000 +v 2.95590 0.19410 -0.52000 +v 4.35770 0.12090 -0.51000 +v 3.58310 0.99230 -0.51000 +v 2.95590 0.19410 -0.51000 +v -2.58500 2.28000 -0.45000 +v -2.88500 1.16000 -0.45000 +v -2.23500 0.66000 -0.45000 +v -0.94500 2.00000 -0.45000 +v -1.51500 3.09000 -0.45000 +v -1.64500 3.16000 -0.45000 +v -2.61444 2.29765 -0.27000 +v -2.92257 1.14727 -0.27000 +v -2.23171 0.61584 -0.27000 +v -0.90474 1.99423 -0.27000 +v -1.49045 3.11426 -0.27000 +v -1.65024 3.20030 -0.27000 +v -2.58500 2.28000 -0.09000 +v -2.88500 1.16000 -0.09000 +v -2.23500 0.66000 -0.09000 +v -0.94500 2.00000 -0.09000 +v -1.51500 3.09000 -0.09000 +v -1.64500 3.16000 -0.09000 +v -1.64230 3.11290 -0.09000 +v -2.54070 2.27140 -0.09000 +v -2.84090 1.14310 -0.09000 +v -2.25050 0.73440 -0.09000 +v -1.64230 3.11290 -0.08000 +v -2.54070 2.27140 -0.08000 +v -2.84090 1.14310 -0.08000 +v -2.25050 0.73440 -0.08000 +v -1.68594 3.03913 -0.05000 +v -2.51392 2.25538 -0.05000 +v -2.80649 1.15576 -0.05000 +v -2.26888 0.78361 -0.05000 +v -1.68594 3.03913 -0.49000 +v -2.51392 2.25538 -0.49000 +v -2.80649 1.15576 -0.49000 +v -2.26888 0.78361 -0.49000 +v -1.64230 3.11290 -0.46000 +v -2.54070 2.27140 -0.46000 +v -2.84090 1.14310 -0.46000 +v -2.25050 0.73440 -0.46000 +v -1.64230 3.11290 -0.45000 +v -2.54070 2.27140 -0.45000 +v -2.84090 1.14310 -0.45000 +v -2.25050 0.73440 -0.45000 +v -2.22390 0.69780 -0.09000 +v -0.97620 1.99300 -0.09000 +v -1.55650 3.09790 -0.09000 +v -2.22390 0.69780 -0.08000 +v -0.97620 1.99300 -0.08000 +v -1.55650 3.09790 -0.08000 +v -2.18139 0.77223 -0.05000 +v -1.01283 1.99822 -0.05000 +v -1.54787 3.01695 -0.05000 +v -2.18139 0.77223 -0.49000 +v -1.01283 1.99822 -0.49000 +v -1.54787 3.01695 -0.49000 +v -2.22390 0.69780 -0.46000 +v -0.97620 1.99300 -0.46000 +v -1.55650 3.09790 -0.46000 +v -2.22390 0.69780 -0.45000 +v -0.97620 1.99300 -0.45000 +v -1.55650 3.09790 -0.45000 +v -0.77865 7.09090 -0.31000 +v 0.04076 6.29017 -0.31000 +v 1.32301 6.23026 -0.31000 +v -0.85000 7.13840 -0.28000 +v 0.02790 6.26060 -0.28000 +v 1.40680 6.21220 -0.28000 +v -0.85000 7.13840 -0.24000 +v 0.02790 6.26060 -0.24000 +v 1.40680 6.21220 -0.24000 +v -0.77865 7.09090 -0.21000 +v 0.04076 6.29017 -0.21000 +v 1.32301 6.23026 -0.21000 +v 3.54500 1.01000 0.07000 +v 2.45500 0.95000 0.07000 +v 2.41500 0.91000 0.07000 +v 2.45500 0.84000 0.07000 +v 2.34500 0.73000 0.07000 +v 1.88500 0.73000 0.07000 +v 1.84500 0.69000 0.07000 +v 2.96500 0.05000 0.07000 +v 3.07500 0.16000 0.07000 +v 4.35500 0.09000 0.07000 +v 4.39500 0.13000 0.07000 +v 4.35500 0.18000 0.07000 +v 3.57500 1.04000 0.07000 +v 3.52797 1.04812 0.29000 +v 2.43797 0.98812 0.29000 +v 2.36636 0.91651 0.29000 +v 2.40636 0.84651 0.29000 +v 2.32885 0.76900 0.29000 +v 1.86885 0.76900 0.29000 +v 1.78132 0.68147 0.29000 +v 2.97151 0.00136 0.29000 +v 3.09026 0.12011 0.29000 +v 4.37026 0.05011 0.29000 +v 4.44726 0.12711 0.29000 +v 4.38470 0.20531 0.29000 +v 3.57638 1.09653 0.29000 +v 3.54500 1.01000 0.51000 +v 2.45500 0.95000 0.51000 +v 2.41500 0.91000 0.51000 +v 2.45500 0.84000 0.51000 +v 2.34500 0.73000 0.51000 +v 1.88500 0.73000 0.51000 +v 1.84500 0.69000 0.51000 +v 2.96500 0.05000 0.51000 +v 3.07500 0.16000 0.51000 +v 4.35500 0.09000 0.51000 +v 4.39500 0.13000 0.51000 +v 4.35500 0.18000 0.51000 +v 3.57500 1.04000 0.51000 +v 2.89000 0.18030 0.51000 +v 3.53350 0.97590 0.51000 +v 2.45710 0.90570 0.51000 +v 2.89000 0.18030 0.52000 +v 3.53350 0.97590 0.52000 +v 2.45710 0.90570 0.52000 +v 2.89372 0.23261 0.55000 +v 3.46710 0.94151 0.55000 +v 2.50800 0.87896 0.55000 +v 2.89372 0.23261 0.03000 +v 3.46710 0.94151 0.03000 +v 2.50800 0.87896 0.03000 +v 2.89000 0.18030 0.06000 +v 3.53350 0.97590 0.06000 +v 2.45710 0.90570 0.06000 +v 2.89000 0.18030 0.07000 +v 3.53350 0.97590 0.07000 +v 2.45710 0.90570 0.07000 +v 2.97380 0.08820 0.51000 +v 2.55590 0.69000 0.51000 +v 1.89060 0.69000 0.51000 +v 2.97380 0.08820 0.52000 +v 2.55590 0.69000 0.52000 +v 1.89060 0.69000 0.52000 +v 2.91024 0.14571 0.55000 +v 2.54021 0.66000 0.55000 +v 1.97357 0.66850 0.55000 +v 2.91024 0.14571 0.03000 +v 2.54021 0.66000 0.03000 +v 1.97357 0.66850 0.03000 +v 2.97380 0.08820 0.06000 +v 2.55590 0.69000 0.06000 +v 1.89060 0.69000 0.06000 +v 2.97380 0.08820 0.07000 +v 2.55590 0.69000 0.07000 +v 1.89060 0.69000 0.07000 +v 4.35770 0.12090 0.51000 +v 3.58310 0.99230 0.51000 +v 2.95590 0.19410 0.51000 +v 4.35770 0.12090 0.52000 +v 3.58310 0.99230 0.52000 +v 2.95590 0.19410 0.52000 +v 4.28760 0.15460 0.55000 +v 3.58452 0.94555 0.55000 +v 3.01522 0.22104 0.55000 +v 4.28760 0.15460 0.03000 +v 3.58452 0.94555 0.03000 +v 3.01522 0.22104 0.03000 +v 4.35770 0.12090 0.06000 +v 3.58310 0.99230 0.06000 +v 2.95590 0.19410 0.06000 +v 4.35770 0.12090 0.07000 +v 3.58310 0.99230 0.07000 +v 2.95590 0.19410 0.07000 +v -2.58500 2.28000 0.09000 +v -2.88500 1.16000 0.09000 +v -2.23500 0.66000 0.09000 +v -0.94500 2.00000 0.09000 +v -1.51500 3.09000 0.09000 +v -1.64500 3.16000 0.09000 +v -2.61444 2.29765 0.27000 +v -2.92257 1.14727 0.27000 +v -2.23171 0.61584 0.27000 +v -0.90474 1.99423 0.27000 +v -1.49045 3.11426 0.27000 +v -1.65024 3.20030 0.27000 +v -2.58500 2.28000 0.45000 +v -2.88500 1.16000 0.45000 +v -2.23500 0.66000 0.45000 +v -0.94500 2.00000 0.45000 +v -1.51500 3.09000 0.45000 +v -1.64500 3.16000 0.45000 +v -1.64230 3.11290 0.45000 +v -2.54070 2.27140 0.45000 +v -2.84090 1.14310 0.45000 +v -2.25050 0.73440 0.45000 +v -1.64230 3.11290 0.46000 +v -2.54070 2.27140 0.46000 +v -2.84090 1.14310 0.46000 +v -2.25050 0.73440 0.46000 +v -1.68594 3.03913 0.49000 +v -2.51392 2.25538 0.49000 +v -2.80649 1.15576 0.49000 +v -2.26888 0.78361 0.49000 +v -1.68594 3.03913 0.05000 +v -2.51392 2.25538 0.05000 +v -2.80649 1.15576 0.05000 +v -2.26888 0.78361 0.05000 +v -1.64230 3.11290 0.08000 +v -2.54070 2.27140 0.08000 +v -2.84090 1.14310 0.08000 +v -2.25050 0.73440 0.08000 +v -1.64230 3.11290 0.09000 +v -2.54070 2.27140 0.09000 +v -2.84090 1.14310 0.09000 +v -2.25050 0.73440 0.09000 +v -2.22390 0.69780 0.45000 +v -0.97620 1.99300 0.45000 +v -1.55650 3.09790 0.45000 +v -2.22390 0.69780 0.46000 +v -0.97620 1.99300 0.46000 +v -1.55650 3.09790 0.46000 +v -2.18139 0.77223 0.49000 +v -1.01283 1.99822 0.49000 +v -1.54787 3.01695 0.49000 +v -2.18139 0.77223 0.05000 +v -1.01283 1.99822 0.05000 +v -1.54787 3.01695 0.05000 +v -2.22390 0.69780 0.08000 +v -0.97620 1.99300 0.08000 +v -1.55650 3.09790 0.08000 +v -2.22390 0.69780 0.09000 +v -0.97620 1.99300 0.09000 +v -1.55650 3.09790 0.09000 +v -0.77865 7.09090 0.21000 +v 0.04076 6.29017 0.21000 +v 1.32301 6.23026 0.21000 +v -0.85000 7.13840 0.24000 +v 0.02790 6.26060 0.24000 +v 1.40680 6.21220 0.24000 +v -0.85000 7.13840 0.28000 +v 0.02790 6.26060 0.28000 +v 1.40680 6.21220 0.28000 +v -0.77865 7.09090 0.31000 +v 0.04076 6.29017 0.31000 +v 1.32301 6.23026 0.31000 +v 1.33258 6.65718 -0.04000 +v 0.21012 7.34980 -0.04000 +v 0.64753 6.72725 -0.04000 +v 1.40490 6.63320 -0.01333 +v 0.15500 7.40240 -0.01333 +v 0.63260 6.70180 -0.01333 +v 1.40490 6.63320 0.01333 +v 0.15500 7.40240 0.01333 +v 0.63260 6.70180 0.01333 +v 1.33258 6.65718 0.04000 +v 0.21012 7.34980 0.04000 +v 0.64753 6.72725 0.04000 +v -2.73764 6.31934 -2.06205 +v -1.84418 5.09377 -1.64542 +v -0.81468 4.58660 -1.16536 +v -0.35903 5.01443 -0.95289 +v -2.78641 6.35520 -2.06272 +v -1.86406 5.07750 -1.63262 +v -0.82008 4.56320 -1.14581 +v -0.33476 5.01890 -0.91950 +v -2.79486 6.35520 -2.04460 +v -1.87251 5.07750 -1.61450 +v -0.82854 4.56320 -1.12768 +v -0.34321 5.01890 -0.90137 +v -2.76300 6.31934 -2.00767 +v -1.86954 5.09377 -1.59104 +v -0.84003 4.58660 -1.11098 +v -0.38439 5.01443 -0.89851 +v -1.76317 3.73100 -1.60765 +v -0.92912 4.51532 -1.21872 +v -2.12504 5.10854 -1.77639 +v -2.92612 5.42882 -2.14994 +v -1.77452 3.70200 -1.59087 +v -0.90401 4.52060 -1.18494 +v -2.12671 5.12710 -1.75510 +v -2.97628 5.46270 -2.15126 +v -1.78297 3.70200 -1.57274 +v -0.91246 4.52060 -1.16682 +v -2.13516 5.12710 -1.73697 +v -2.98473 5.46270 -2.13314 +v -1.78852 3.73100 -1.55327 +v -0.95447 4.51532 -1.16434 +v -2.15039 5.10854 -1.72201 +v -2.95148 5.42882 -2.09556 +v 0.61214 3.16548 -0.50002 +v -0.31445 4.90034 -0.93210 +v -1.67364 3.65926 -1.56590 +v 0.10373 3.23025 -0.73710 +v 0.63780 3.14100 -0.46599 +v -0.31808 4.93070 -0.91172 +v -1.72231 3.64850 -1.56653 +v 0.09230 3.21050 -0.72036 +v 0.62935 3.14100 -0.44786 +v -0.32653 4.93070 -0.89360 +v -1.73077 3.64850 -1.54840 +v 0.08384 3.21050 -0.70223 +v 0.58678 3.16548 -0.44564 +v -0.33981 4.90034 -0.87772 +v -1.69900 3.65926 -1.51152 +v 0.07838 3.23025 -0.68272 +v -2.76300 6.31934 2.00767 +v -1.86954 5.09377 1.59104 +v -0.84003 4.58660 1.11098 +v -0.38439 5.01443 0.89851 +v -2.79486 6.35520 2.04460 +v -1.87251 5.07750 1.61450 +v -0.82854 4.56320 1.12768 +v -0.34321 5.01890 0.90137 +v -2.78641 6.35520 2.06272 +v -1.86406 5.07750 1.63262 +v -0.82008 4.56320 1.14581 +v -0.33476 5.01890 0.91950 +v -2.73764 6.31934 2.06205 +v -1.84418 5.09377 1.64542 +v -0.81468 4.58660 1.16536 +v -0.35903 5.01443 0.95289 +v -1.78852 3.73100 1.55327 +v -0.95447 4.51532 1.16434 +v -2.15039 5.10854 1.72201 +v -2.95148 5.42882 2.09556 +v -1.78297 3.70200 1.57274 +v -0.91246 4.52060 1.16682 +v -2.13516 5.12710 1.73697 +v -2.98473 5.46270 2.13314 +v -1.77452 3.70200 1.59087 +v -0.90401 4.52060 1.18494 +v -2.12671 5.12710 1.75510 +v -2.97628 5.46270 2.15126 +v -1.76317 3.73100 1.60765 +v -0.92912 4.51532 1.21872 +v -2.12504 5.10854 1.77639 +v -2.92612 5.42882 2.14994 +v 0.58678 3.16548 0.44564 +v -0.33981 4.90034 0.87772 +v -1.69900 3.65926 1.51152 +v 0.07838 3.23025 0.68272 +v 0.62935 3.14100 0.44786 +v -0.32653 4.93070 0.89360 +v -1.73077 3.64850 1.54840 +v 0.08384 3.21050 0.70223 +v 0.63780 3.14100 0.46599 +v -0.31808 4.93070 0.91172 +v -1.72231 3.64850 1.56653 +v 0.09230 3.21050 0.72036 +v 0.61214 3.16548 0.50002 +v -0.31445 4.90034 0.93210 +v -1.67364 3.65926 1.56590 +v 0.10373 3.23025 0.73710 +vn -0.6976 -0.6976 -0.1636 +vn -0.7084 0.6866 -0.1635 +vn 0.5511 0.8183 -0.1636 +vn -0.6976 0.6976 -0.1636 +vn -0.5406 -0.8252 0.1636 +vn -0.6976 -0.6976 0.1636 +vn -0.7084 0.6866 0.1635 +vn 0.5511 0.8183 0.1636 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +vn 0.5368 0.8437 0.0000 +vn 0.0404 0.9992 0.0000 +vn -0.4731 -0.8810 0.0000 +vn 0.0512 -0.9987 0.0000 +vn 0.3907 0.6196 0.6808 +vn 0.0350 0.7746 0.6315 +vn -0.3625 -0.6867 0.6301 +vn 0.0358 -0.7308 0.6816 +vn 0.3907 0.6196 -0.6808 +vn 0.0350 0.7746 -0.6315 +vn -0.3625 -0.6867 -0.6301 +vn 0.0358 -0.7308 -0.6816 +vn -0.4557 -0.8750 -0.1636 +vn 0.1971 -0.9666 -0.1636 +vn -0.4557 -0.8750 0.1636 +vn 0.1971 -0.9666 0.1636 +vn 0.1935 0.9674 0.1636 +vn 0.6059 -0.7955 0.0000 +vn 0.0000 1.0000 0.0000 +vn -0.6441 -0.7649 0.0000 +vn 0.4365 -0.5774 0.6900 +vn -0.0013 0.7262 0.6875 +vn -0.4575 -0.5443 0.7032 +vn 0.4365 -0.5774 -0.6900 +vn -0.0013 0.7262 -0.6875 +vn -0.4575 -0.5443 -0.7032 +vn -0.7150 0.6992 0.0000 +vn 0.2079 -0.9782 0.0000 +vn 0.6139 0.7894 0.0000 +vn -0.5270 0.5189 0.6731 +vn 0.1555 -0.7221 0.6741 +vn 0.4341 0.5582 0.7071 +vn -0.5270 0.5189 -0.6731 +vn 0.1555 -0.7221 -0.6741 +vn 0.4341 0.5582 -0.7071 +vn -0.5406 -0.8252 -0.1636 +vn -0.6287 -0.7603 -0.1636 +vn 0.6976 -0.6976 -0.1636 +vn 0.8824 -0.4412 -0.1636 +vn 0.6976 0.6976 -0.1636 +vn -0.3664 0.9160 -0.1636 +vn 0.2186 0.9620 -0.1635 +vn 0.2047 -0.9651 -0.1635 +vn -0.7510 -0.6397 -0.1636 +vn 0.6976 -0.6976 -0.1635 +vn 0.8869 -0.4321 -0.1636 +vn 0.7971 0.5812 -0.1636 +vn -0.9271 0.3371 -0.1635 +vn 0.0757 0.9836 -0.1636 +vn 0.1935 0.9674 -0.1636 +vn 0.0418 0.9856 -0.1636 +vn -0.6287 -0.7603 0.1636 +vn 0.6976 -0.6976 0.1636 +vn 0.8824 -0.4412 0.1636 +vn 0.6976 0.6976 0.1636 +vn -0.3664 0.9160 0.1636 +vn 0.2186 0.9620 0.1635 +vn 0.2047 -0.9651 0.1635 +vn -0.7510 -0.6397 0.1636 +vn 0.6976 -0.6976 0.1635 +vn 0.8869 -0.4321 0.1636 +vn 0.7971 0.5812 0.1636 +vn -0.9271 0.3371 0.1635 +vn -0.6976 0.6976 0.1636 +vn 0.0757 0.9836 0.1636 +vn 0.0418 0.9856 0.1636 +vn -0.0906 0.9959 0.0000 +vn -0.5597 -0.8287 0.0000 +vn 0.1982 -0.9802 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0899 0.9960 0.0000 +vn -0.0637 0.7123 0.6990 +vn -0.3996 -0.5925 0.6995 +vn 0.1402 -0.6931 0.7071 +vn 0.7071 0.0000 0.7071 +vn 0.0635 0.7042 0.7071 +vn -0.0637 0.7123 -0.6990 +vn -0.3996 -0.5925 -0.6995 +vn 0.1402 -0.6931 -0.7071 +vn 0.7071 0.0000 -0.7071 +vn 0.0635 0.7042 -0.7071 +vn 0.8944 -0.4472 0.0000 +vn 0.8204 0.5718 0.0000 +vn -0.9978 0.0667 0.0000 +vn -0.8532 -0.5216 0.0000 +vn 0.6324 -0.3162 0.7071 +vn 0.5891 0.4091 0.6969 +vn -0.7157 0.0467 0.6969 +vn -0.6033 -0.3688 0.7071 +vn 0.6324 -0.3162 -0.7071 +vn 0.5891 0.4091 -0.6969 +vn -0.7157 0.0467 -0.6969 +vn -0.6033 -0.3688 -0.7071 +vn 0.2179 0.9760 0.0000 +vn -0.6317 -0.7752 0.0000 +vn 0.8903 -0.4553 0.0000 +vn 0.1712 0.7520 0.6365 +vn -0.4846 -0.6000 0.6365 +vn 0.6296 -0.3220 0.7070 +vn 0.1712 0.7520 -0.6365 +vn -0.4846 -0.6000 -0.6365 +vn 0.6296 -0.3220 -0.7070 +vn -0.9811 0.0989 -0.1664 +vn -0.6160 -0.7700 -0.1664 +vn 0.7506 -0.6394 -0.1664 +vn 0.9855 -0.0340 -0.1664 +vn 0.6972 -0.6973 -0.1664 +vn -0.7833 -0.5990 -0.1664 +vn 0.8541 -0.4928 -0.1664 +vn 0.7513 -0.6386 -0.1664 +vn 0.7541 0.6353 -0.1664 +vn -0.4379 0.8835 -0.1664 +vn -0.9811 0.0989 0.1664 +vn -0.6160 -0.7700 0.1664 +vn 0.7506 -0.6394 0.1664 +vn 0.6973 -0.6972 0.1664 +vn 0.9855 -0.0340 0.1664 +vn 0.6972 -0.6973 0.1664 +vn -0.7833 -0.5990 0.1664 +vn 0.8541 -0.4928 0.1664 +vn 0.7513 -0.6386 0.1664 +vn 0.7541 0.6353 0.1664 +vn 0.5646 0.8084 0.1664 +vn 0.6972 0.6973 0.1664 +vn -0.4379 0.8835 0.1664 +vn 0.8288 -0.5596 0.0000 +vn -0.4322 0.9018 0.0000 +vn -0.9960 0.0897 0.0000 +vn 0.6695 -0.4517 0.5897 +vn -0.3281 0.6790 0.6567 +vn -0.7577 0.0709 0.6487 +vn 0.6695 -0.4517 -0.5897 +vn -0.3281 0.6790 -0.6567 +vn -0.7577 0.0709 -0.6487 +vn -0.8608 -0.5090 0.0000 +vn 0.8587 -0.5125 0.0000 +vn 0.7669 0.6418 0.0000 +vn -0.9732 -0.2300 0.0000 +vn -0.6086 -0.3599 0.7071 +vn 0.6072 -0.3624 0.7071 +vn 0.5925 0.4932 0.6369 +vn -0.7504 -0.1806 0.6358 +vn -0.6086 -0.3599 -0.7071 +vn 0.6072 -0.3624 -0.7071 +vn 0.5925 0.4932 -0.6369 +vn -0.7504 -0.1806 -0.6358 +vn -0.6973 -0.6972 -0.1664 +vn -0.8983 -0.4068 -0.1664 +vn 0.6973 -0.6972 -0.1664 +vn 0.7081 -0.6863 -0.1664 +vn -0.6973 -0.6972 0.1664 +vn -0.8983 -0.4068 0.1664 +vn 0.7081 -0.6863 0.1664 +vn -0.8289 0.5594 0.0000 +vn -0.9099 -0.4148 0.0000 +vn -0.8072 -0.5903 0.0000 +vn 0.9998 -0.0215 0.0000 +vn 0.8181 0.5751 0.0000 +vn -0.5861 0.3956 0.7071 +vn -0.6434 -0.2933 0.7071 +vn -0.5834 -0.4260 0.6915 +vn 0.7225 -0.0146 0.6913 +vn 0.5785 0.4066 0.7071 +vn -0.5861 0.3956 -0.7071 +vn -0.6434 -0.2933 -0.7071 +vn -0.5834 -0.4260 -0.6915 +vn 0.7225 -0.0146 -0.6913 +vn 0.5785 0.4066 -0.7071 +vn -0.7916 -0.5879 -0.1664 +vn 0.5646 0.8084 -0.1664 +vn 0.6972 0.6973 -0.1664 +vn -0.7916 -0.5879 0.1664 +vn -0.8087 -0.5882 0.0000 +vn 0.2615 -0.9652 0.0000 +vn 0.7201 -0.6939 0.0000 +vn 0.5706 0.8212 0.0000 +vn -0.8321 0.5547 0.0000 +vn -0.5719 -0.4159 0.7071 +vn 0.1849 -0.6825 0.7071 +vn 0.5091 -0.4906 0.7071 +vn 0.4035 0.5807 0.7071 +vn -0.5884 0.3922 0.7071 +vn -0.5719 -0.4159 -0.7071 +vn 0.1849 -0.6825 -0.7071 +vn 0.5091 -0.4906 -0.7071 +vn 0.4035 0.5807 -0.7071 +vn -0.5884 0.3922 -0.7071 +vn -0.8192 -0.5735 0.0000 +vn 0.9715 0.2370 0.0000 +vn -0.3823 0.9240 0.0000 +vn -0.6557 -0.4525 0.6044 +vn 0.7728 0.1941 0.6042 +vn -0.2703 0.6533 0.7071 +vn -0.6557 -0.4525 -0.6044 +vn 0.7728 0.1941 -0.6042 +vn -0.2703 0.6533 -0.7071 +vn -0.6980 0.6980 -0.1597 +vn 0.6980 0.6980 -0.1597 +vn 0.0880 0.9832 -0.1597 +vn -0.6980 0.6981 -0.1597 +vn -0.5878 -0.7931 -0.1597 +vn 0.6980 -0.6980 -0.1597 +vn -0.8972 0.4118 -0.1597 +vn -0.6980 0.6980 0.1597 +vn 0.6980 0.6980 0.1597 +vn 0.0880 0.9832 0.1597 +vn -0.6980 0.6981 0.1597 +vn -0.5878 -0.7931 0.1597 +vn 0.6980 -0.6980 0.1597 +vn -0.8972 0.4118 0.1597 +vn -0.9200 0.3919 0.0000 +vn -0.7506 -0.6607 0.0000 +vn 0.9983 -0.0588 0.0000 +vn 0.8099 0.5865 0.0000 +vn -0.6505 0.2771 0.7071 +vn -0.5308 -0.4672 0.7071 +vn 0.7059 -0.0416 0.7071 +vn 0.5727 0.4147 0.7071 +vn -0.6505 0.2771 -0.7071 +vn -0.5308 -0.4672 -0.7071 +vn 0.7059 -0.0416 -0.7071 +vn 0.5727 0.4147 -0.7071 +vn -0.8889 -0.4581 0.0000 +vn 0.5579 -0.8299 0.0000 +vn 0.0869 0.9962 0.0000 +vn -0.6285 -0.3239 0.7071 +vn 0.3991 -0.5943 0.6982 +vn 0.0619 0.7132 0.6982 +vn -0.6285 -0.3239 -0.7071 +vn 0.3991 -0.5943 -0.6982 +vn 0.0619 0.7132 -0.6982 +vn 0.7557 0.6550 0.0000 +vn -0.4678 0.8838 0.0000 +vn -0.5753 0.8180 0.0000 +vn -0.5944 -0.8042 0.0000 +vn 0.9999 -0.0116 0.0000 +vn 0.5343 0.4631 0.7071 +vn -0.3308 0.6250 0.7071 +vn -0.4068 0.5784 0.7071 +vn -0.4203 -0.5687 0.7071 +vn 0.7070 -0.0082 0.7071 +vn 0.5343 0.4631 -0.7071 +vn -0.3308 0.6250 -0.7071 +vn -0.4068 0.5784 -0.7071 +vn -0.4203 -0.5687 -0.7071 +vn 0.7070 -0.0082 -0.7071 +vn -0.8796 -0.4481 -0.1597 +vn -0.6980 -0.6980 -0.1597 +vn 0.6981 -0.6980 -0.1597 +vn 0.7584 0.6320 -0.1597 +vn 0.9305 -0.3296 -0.1597 +vn -0.9871 -0.0130 -0.1597 +vn 0.7883 0.5942 -0.1597 +vn -0.8796 -0.4481 0.1597 +vn -0.6980 -0.6980 0.1597 +vn 0.6981 -0.6980 0.1597 +vn 0.7584 0.6320 0.1597 +vn 0.9305 -0.3296 0.1597 +vn -0.9871 -0.0130 0.1597 +vn 0.7883 0.5942 0.1597 +vn -0.7385 -0.6743 0.0000 +vn 0.9470 -0.3212 0.0000 +vn -0.6484 0.7613 0.0000 +vn -0.5222 -0.4768 0.7071 +vn 0.7109 -0.2432 0.6599 +vn -0.4892 0.5708 0.6595 +vn -0.5222 -0.4768 -0.7071 +vn 0.7109 -0.2432 -0.6599 +vn -0.4892 0.5708 -0.6595 +vn -0.6981 -0.6980 -0.1597 +vn -0.0597 -0.9854 -0.1597 +vn 0.9711 0.1774 -0.1597 +vn -0.8740 0.4589 -0.1597 +vn -0.7217 0.6736 -0.1597 +vn 0.8465 0.5079 -0.1597 +vn -0.6981 -0.6980 0.1597 +vn -0.0597 -0.9854 0.1597 +vn 0.9711 0.1774 0.1597 +vn -0.8740 0.4589 0.1597 +vn -0.7217 0.6736 0.1597 +vn 0.8465 0.5079 0.1597 +vn -0.8734 0.4869 0.0000 +vn -0.8348 -0.5506 0.0000 +vn 0.6668 -0.7453 0.0000 +vn 0.9849 0.1731 0.0000 +vn -0.6249 0.3477 0.6990 +vn -0.5903 -0.3893 0.7071 +vn 0.4714 -0.5270 0.7071 +vn 0.7044 0.1234 0.6990 +vn -0.6249 0.3477 -0.6990 +vn -0.5903 -0.3893 -0.7071 +vn 0.4714 -0.5270 -0.7071 +vn 0.7044 0.1234 -0.6990 +vn 0.8545 0.5194 0.0000 +vn -0.9999 -0.0132 0.0000 +vn 0.6325 -0.7746 0.0000 +vn 0.6447 0.3894 0.6579 +vn -0.7523 -0.0116 0.6587 +vn 0.4472 -0.5477 0.7071 +vn 0.6447 0.3894 -0.6579 +vn -0.7523 -0.0116 -0.6587 +vn 0.4472 -0.5477 -0.7071 +vn -0.0595 -0.9982 0.0000 +vn 0.7442 0.6680 0.0000 +vn -0.9999 0.0127 0.0000 +vn -0.0420 -0.7059 0.7071 +vn 0.5262 0.4723 0.7071 +vn -0.7071 0.0090 0.7071 +vn -0.0420 -0.7059 -0.7071 +vn 0.5262 0.4723 -0.7071 +vn -0.7071 0.0090 -0.7071 +vn 0.5908 0.7877 -0.1745 +vn -0.6963 0.6963 -0.1745 +vn -0.9804 0.0915 -0.1745 +vn -0.6963 -0.6962 -0.1746 +vn -0.8193 0.5461 -0.1746 +vn -0.6875 -0.7049 -0.1746 +vn 0.6963 -0.6962 -0.1746 +vn 0.7877 0.5908 -0.1745 +vn 0.7500 0.6380 -0.1745 +vn -0.5461 0.8193 -0.1746 +vn 0.9608 0.2153 -0.1746 +vn 0.5908 0.7877 0.1745 +vn -0.6963 0.6963 0.1745 +vn -0.9804 0.0915 0.1745 +vn -0.6963 -0.6962 0.1746 +vn -0.8193 0.5461 0.1746 +vn -0.6875 -0.7049 0.1746 +vn 0.6963 -0.6962 0.1746 +vn 0.7877 0.5908 0.1745 +vn 0.7500 0.6380 0.1745 +vn -0.5461 0.8193 0.1746 +vn 0.9608 0.2153 0.1746 +vn 0.7641 0.6451 0.0000 +vn -0.6133 0.7898 0.0000 +vn -0.0251 -0.9997 0.0000 +vn 0.5403 0.4561 0.7071 +vn -0.4379 0.5644 0.6998 +vn -0.0177 -0.7140 0.7000 +vn 0.5403 0.4561 -0.7071 +vn -0.4379 0.5644 -0.6998 +vn -0.0177 -0.7140 -0.7000 +vn -0.7034 -0.7108 0.0000 +vn 0.9741 0.2262 0.0000 +vn -0.7975 0.6034 0.0000 +vn -0.5254 -0.5287 0.6667 +vn 0.7252 0.1696 0.6673 +vn -0.5639 0.4267 0.7071 +vn -0.5254 -0.5287 -0.6667 +vn 0.7252 0.1696 -0.6673 +vn -0.5639 0.4267 -0.7071 +vn -0.9967 0.0810 0.0000 +vn 0.7886 -0.6148 0.0000 +vn 0.8716 0.4903 0.0000 +vn -0.7681 0.0626 0.6373 +vn 0.5859 -0.4538 0.6714 +vn 0.6441 0.3599 0.6750 +vn -0.7681 0.0626 -0.6373 +vn 0.5859 -0.4538 -0.6714 +vn 0.6441 0.3599 -0.6750 +vn -0.0174 -0.9845 -0.1746 +vn -0.6963 0.6962 -0.1746 +vn 0.5527 0.8149 -0.1746 +vn 0.6962 0.6963 -0.1746 +vn -0.0174 -0.9845 0.1746 +vn -0.6963 0.6962 0.1746 +vn 0.6962 0.6963 0.1746 +vn 0.5636 0.8260 0.0000 +vn -0.9706 -0.2406 0.0000 +vn -0.0286 -0.9996 0.0000 +vn 0.4178 0.6140 0.6697 +vn -0.6863 -0.1701 0.7071 +vn -0.0226 -0.7429 0.6690 +vn 0.4178 0.6140 -0.6697 +vn -0.6863 -0.1701 -0.7071 +vn -0.0226 -0.7429 -0.6690 +vn 0.6962 0.6962 -0.1746 +vn -0.1718 0.9695 -0.1746 +vn 0.7335 -0.6569 -0.1746 +vn 0.9621 -0.2096 -0.1746 +vn 0.6151 0.7689 -0.1746 +vn -0.6024 0.7789 -0.1745 +vn 0.6962 0.6962 0.1746 +vn -0.1718 0.9695 0.1746 +vn 0.5527 0.8149 0.1746 +vn 0.7335 -0.6569 0.1746 +vn 0.9621 -0.2096 0.1746 +vn 0.6151 0.7689 0.1746 +vn -0.6024 0.7789 0.1745 +vn 0.6199 -0.7846 0.0000 +vn -0.1776 0.9841 0.0000 +vn -0.6097 -0.7926 0.0000 +vn 0.4717 -0.6018 0.6444 +vn -0.1379 0.7510 0.6458 +vn -0.4311 -0.5605 0.7071 +vn 0.4717 -0.6018 -0.6444 +vn -0.1379 0.7510 -0.6458 +vn -0.4311 -0.5605 -0.7071 +vn -0.8722 -0.4891 0.0000 +vn 0.7427 -0.6696 0.0000 +vn 0.9806 -0.1961 0.0000 +vn -0.6115 0.7913 0.0000 +vn -0.6168 -0.3458 0.7071 +vn 0.5252 -0.4735 0.7071 +vn 0.6934 -0.1387 0.7071 +vn -0.4323 0.5595 0.7071 +vn -0.6168 -0.3458 -0.7071 +vn 0.5252 -0.4735 -0.7071 +vn 0.6934 -0.1387 -0.7071 +vn -0.4323 0.5595 -0.7071 +vn -0.0541 0.9832 -0.1745 +vn -0.4885 -0.8549 -0.1746 +vn 0.6963 -0.6963 -0.1745 +vn -0.0541 0.9832 0.1745 +vn -0.4885 -0.8549 0.1746 +vn 0.6963 -0.6963 0.1745 +vn 0.7775 -0.6289 0.0000 +vn -0.0651 0.9979 0.0000 +vn -0.8587 -0.5125 0.0000 +vn 0.5498 -0.4447 0.7071 +vn -0.0460 0.7057 0.7071 +vn -0.6072 -0.3623 0.7071 +vn 0.5498 -0.4447 -0.7071 +vn -0.0460 0.7057 -0.7071 +vn -0.6072 -0.3623 -0.7071 +vn 0.8214 0.5704 0.0000 +vn -0.4857 -0.8741 0.0000 +vn 0.6334 0.4472 0.6316 +vn 0.0053 0.7617 0.6479 +vn -0.4019 -0.7219 0.5633 +vn 0.6334 0.4472 -0.6316 +vn 0.0053 0.7617 -0.6479 +vn -0.4019 -0.7219 -0.5633 +vn -0.8549 -0.4885 -0.1746 +vn 0.0000 0.9846 -0.1746 +vn -0.0538 -0.9832 -0.1745 +vn 0.7689 0.6151 -0.1746 +vn 0.7293 0.6615 -0.1746 +vn -0.8549 -0.4885 0.1746 +vn 0.0000 0.9846 0.1746 +vn -0.0538 -0.9832 0.1745 +vn 0.7689 0.6151 0.1746 +vn 0.7293 0.6615 0.1746 +vn 0.7474 0.6644 0.0000 +vn -0.7863 0.6178 0.0000 +vn -0.0521 -0.9986 0.0000 +vn 0.5285 0.4698 0.7071 +vn -0.5560 0.4369 0.7071 +vn -0.0369 -0.7062 0.7071 +vn 0.5285 0.4698 -0.7071 +vn -0.5560 0.4369 -0.7071 +vn -0.0369 -0.7062 -0.7071 +vn -0.9501 0.2545 -0.1803 +vn -0.5997 -0.7796 -0.1803 +vn -0.6722 0.7181 -0.1803 +vn -0.9501 0.2545 0.1803 +vn 0.8716 0.4558 0.1803 +vn 0.4663 0.8660 0.1803 +vn -0.6722 0.7181 0.1803 +vn -0.6836 0.7298 0.0000 +vn -0.9664 0.2571 0.0000 +vn -0.5692 -0.8222 0.0000 +vn 0.9688 -0.2477 0.0000 +vn -0.5103 0.5420 0.6677 +vn -0.6834 0.1818 0.7071 +vn -0.4025 -0.5814 0.7071 +vn 0.7202 -0.1851 0.6686 +vn -0.5103 0.5420 -0.6677 +vn -0.6834 0.1818 -0.7071 +vn -0.4025 -0.5814 -0.7071 +vn 0.7202 -0.1851 -0.6686 +vn 0.7086 -0.6822 -0.1803 +vn 0.8716 0.4558 -0.1803 +vn 0.4663 0.8660 -0.1803 +vn -0.5997 -0.7796 0.1803 +vn 0.7086 -0.6822 0.1803 +vn 0.7202 -0.6938 0.0000 +vn 0.8853 0.4650 0.0000 +vn -0.9634 0.2679 0.0000 +vn 0.5507 -0.5278 0.6467 +vn 0.6260 0.3288 0.7071 +vn -0.7337 0.2055 0.6477 +vn 0.5507 -0.5278 -0.6467 +vn 0.6260 0.3288 -0.7071 +vn -0.7337 0.2055 -0.6477 +vn -0.5559 -0.5622 -0.6123 +vn -0.0326 -0.8012 -0.5975 +vn 0.3348 0.8166 -0.4701 +vn -0.7071 -0.7071 0.0000 +vn -0.0351 -0.9994 0.0000 +vn 0.3797 0.9251 0.0000 +vn -0.5559 -0.5622 0.6123 +vn -0.0326 -0.8012 0.5975 +vn 0.3348 0.8166 0.4701 +vn 0.4447 0.7216 -0.5306 +vn -0.6452 -0.4462 -0.6202 +vn -0.0732 -0.7696 -0.6344 +vn 0.5241 0.8516 0.0000 +vn -0.8263 -0.5633 0.0000 +vn -0.0885 -0.9961 0.0000 +vn 0.4447 0.7216 0.5306 +vn -0.6452 -0.4462 0.6202 +vn -0.0732 -0.7696 0.6344 +vn 0.4226 -0.0000 -0.9063 +vn -0.4226 -0.0000 0.9063 +vn -0.2847 -0.4860 -0.8263 +vn 0.0375 -0.6458 -0.7626 +vn 0.7142 -0.5385 -0.4471 +vn 0.5787 0.6962 -0.4247 +vn -0.7089 -0.6230 -0.3306 +vn -0.3695 -0.9131 -0.1724 +vn 0.5874 -0.7616 0.2739 +vn 0.4014 0.8966 0.1871 +vn -0.8160 -0.4860 0.3130 +vn -0.5601 -0.6457 0.5190 +vn 0.1164 -0.5385 0.8346 +vn 0.0465 0.6961 0.7164 +vn 0.7145 -0.5382 -0.4470 +vn 0.5616 0.6450 -0.5183 +vn 0.5114 0.6969 -0.5028 +vn -0.2513 -0.4458 -0.8592 +vn 0.5879 -0.7611 0.2741 +vn 0.3716 0.9121 0.1733 +vn 0.3055 0.9415 0.1424 +vn -0.7240 -0.6016 -0.3375 +vn 0.1168 -0.5382 0.8347 +vn -0.0360 0.6450 0.7634 +vn -0.0565 0.6969 0.7149 +vn -0.8197 -0.4457 0.3599 +vn 0.4226 0.0001 -0.9063 +vn -0.4226 -0.0002 0.9063 +vn 0.8510 0.3590 -0.3833 +vn -0.1098 0.5448 -0.8314 +vn 0.1618 -0.6909 -0.7047 +vn 0.2253 -0.7025 -0.6751 +vn 0.7808 0.5077 0.3640 +vn -0.5778 0.7704 -0.2695 +vn -0.1937 -0.9769 -0.0904 +vn -0.1040 -0.9934 -0.0485 +vn 0.2533 0.3590 0.8983 +vn -0.7074 0.5448 0.4503 +vn -0.4358 -0.6907 0.5770 +vn -0.3724 -0.7024 0.6066 +vn -0.4226 -0.0000 -0.9063 +vn 0.4226 -0.0000 0.9063 +vn -0.8160 -0.4860 -0.3130 +vn -0.5601 -0.6457 -0.5190 +vn 0.1164 -0.5385 -0.8346 +vn 0.0465 0.6961 -0.7164 +vn -0.7089 -0.6230 0.3306 +vn -0.3695 -0.9131 0.1724 +vn 0.5874 -0.7616 -0.2739 +vn 0.4014 0.8966 -0.1871 +vn -0.2847 -0.4860 0.8263 +vn 0.0375 -0.6458 0.7626 +vn 0.7142 -0.5385 0.4471 +vn 0.5787 0.6962 0.4247 +vn 0.1168 -0.5382 -0.8347 +vn -0.0360 0.6450 -0.7634 +vn -0.0565 0.6969 -0.7149 +vn -0.8197 -0.4457 -0.3599 +vn 0.5879 -0.7611 -0.2741 +vn 0.3716 0.9121 -0.1733 +vn 0.3055 0.9415 -0.1424 +vn -0.7240 -0.6016 0.3375 +vn 0.7145 -0.5382 0.4470 +vn 0.5616 0.6450 0.5183 +vn 0.5114 0.6969 0.5028 +vn -0.2513 -0.4458 0.8592 +vn -0.4226 -0.0002 -0.9063 +vn 0.4226 0.0001 0.9063 +vn 0.2533 0.3590 -0.8983 +vn -0.7074 0.5448 -0.4503 +vn -0.4358 -0.6907 -0.5770 +vn -0.3724 -0.7024 -0.6066 +vn 0.7808 0.5077 -0.3640 +vn -0.5778 0.7704 0.2695 +vn -0.1937 -0.9769 0.0904 +vn -0.1040 -0.9934 0.0485 +vn 0.8510 0.3590 0.3833 +vn -0.1098 0.5448 0.8314 +vn 0.1618 -0.6909 0.7047 +vn 0.2253 -0.7025 0.6751 +o head +usemtl glass +f 1//1 2//1 30//1 29//1 +f 6//2 7//2 35//2 34//2 +f 26//3 27//3 55//3 54//3 +f 28//4 1//4 29//4 56//4 +f 32//5 33//5 61//5 60//5 +f 33//6 34//6 62//6 61//6 +f 34//7 35//7 63//7 62//7 +f 35//6 36//6 64//6 63//6 +f 54//8 55//8 83//8 82//8 +f 1//9 28//9 27//9 +f 83//10 84//10 57//10 +f 1//9 27//9 26//9 +f 82//10 83//10 57//10 +f 1//9 26//9 25//9 +f 81//10 82//10 57//10 +f 3//9 2//9 1//9 +f 57//10 58//10 59//10 +f 85//9 88//9 87//9 +f 95//10 96//10 93//10 +f 87//9 86//9 85//9 +f 93//10 94//10 95//10 +f 85//11 86//11 90//11 89//11 +f 86//12 87//12 91//12 90//12 +f 87//13 88//13 92//13 91//13 +f 88//14 85//14 89//14 92//14 +f 89//15 90//15 94//15 93//15 +f 90//16 91//16 95//16 94//16 +f 91//17 92//17 96//17 95//17 +f 92//18 89//18 93//18 96//18 +f 97//9 100//9 99//9 +f 107//10 108//10 105//10 +f 99//9 98//9 97//9 +f 105//10 106//10 107//10 +f 97//19 98//19 102//19 101//19 +f 98//20 99//20 103//20 102//20 +f 99//21 100//21 104//21 103//21 +f 100//22 97//22 101//22 104//22 +f 101//11 102//11 106//11 105//11 +f 102//12 103//12 107//12 106//12 +f 103//13 104//13 108//13 107//13 +f 104//14 101//14 105//14 108//14 +usemtl dark +f 2//23 3//23 31//23 30//23 +f 3//1 4//1 32//1 31//1 +f 7//1 8//1 36//1 35//1 +f 8//24 9//24 37//24 36//24 +f 29//6 30//6 58//6 57//6 +f 30//25 31//25 59//25 58//25 +f 36//26 37//26 65//26 64//26 +f 53//27 54//27 82//27 81//27 +f 1//9 25//9 21//9 +f 77//10 81//10 57//10 +f 15//9 9//9 8//9 +f 64//10 65//10 71//10 +f 15//9 8//9 7//9 +f 63//10 64//10 71//10 +f 15//9 7//9 6//9 +f 62//10 63//10 71//10 +f 165//9 164//9 163//9 +f 169//10 170//10 171//10 +f 163//28 164//28 167//28 166//28 +f 164//29 165//29 168//29 167//29 +f 165//30 163//30 166//30 168//30 +f 166//31 167//31 170//31 169//31 +f 167//32 168//32 171//32 170//32 +f 168//33 166//33 169//33 171//33 +f 174//9 173//9 172//9 +f 178//10 179//10 180//10 +f 172//34 173//34 176//34 175//34 +f 173//35 174//35 177//35 176//35 +f 174//36 172//36 175//36 177//36 +f 175//28 176//28 179//28 178//28 +f 176//29 177//29 180//29 179//29 +f 177//30 175//30 178//30 180//30 +f 183//9 182//9 181//9 +f 187//10 188//10 189//10 +f 181//37 182//37 185//37 184//37 +f 182//38 183//38 186//38 185//38 +f 183//39 181//39 184//39 186//39 +f 184//40 185//40 188//40 187//40 +f 185//41 186//41 189//41 188//41 +f 186//42 184//42 187//42 189//42 +f 192//9 191//9 190//9 +f 196//10 197//10 198//10 +f 190//43 191//43 194//43 193//43 +f 191//44 192//44 195//44 194//44 +f 192//45 190//45 193//45 195//45 +f 193//37 194//37 197//37 196//37 +f 194//38 195//38 198//38 197//38 +f 195//39 193//39 196//39 198//39 +usemtl champagne +f 4//46 5//46 33//46 32//46 +f 5//1 6//1 34//1 33//1 +f 9//47 10//47 38//47 37//47 +f 10//48 11//48 39//48 38//48 +f 11//49 12//49 40//49 39//49 +f 12//50 13//50 41//50 40//50 +f 13//51 14//51 42//51 41//51 +f 14//52 15//52 43//52 42//52 +f 15//50 16//50 44//50 43//50 +f 16//48 17//48 45//48 44//48 +f 17//53 18//53 46//53 45//53 +f 18//54 19//54 47//54 46//54 +f 19//55 20//55 48//55 47//55 +f 20//56 21//56 49//56 48//56 +f 21//57 22//57 50//57 49//57 +f 22//58 23//58 51//58 50//58 +f 23//4 24//4 52//4 51//4 +f 24//59 25//59 53//59 52//59 +f 25//60 26//60 54//60 53//60 +f 27//61 28//61 56//61 55//61 +f 31//6 32//6 60//6 59//6 +f 37//62 38//62 66//62 65//62 +f 38//63 39//63 67//63 66//63 +f 39//64 40//64 68//64 67//64 +f 40//65 41//65 69//65 68//65 +f 41//66 42//66 70//66 69//66 +f 42//67 43//67 71//67 70//67 +f 43//65 44//65 72//65 71//65 +f 44//63 45//63 73//63 72//63 +f 45//68 46//68 74//68 73//68 +f 46//69 47//69 75//69 74//69 +f 47//70 48//70 76//70 75//70 +f 48//71 49//71 77//71 76//71 +f 49//72 50//72 78//72 77//72 +f 50//73 51//73 79//73 78//73 +f 51//74 52//74 80//74 79//74 +f 52//75 53//75 81//75 80//75 +f 55//76 56//76 84//76 83//76 +f 56//74 29//74 57//74 84//74 +f 23//9 22//9 21//9 +f 77//10 78//10 79//10 +f 24//9 23//9 21//9 +f 77//10 79//10 80//10 +f 25//9 24//9 21//9 +f 77//10 80//10 81//10 +f 21//9 20//9 19//9 +f 75//10 76//10 77//10 +f 21//9 19//9 18//9 +f 74//10 75//10 77//10 +f 1//9 21//9 18//9 +f 74//10 77//10 57//10 +f 1//9 18//9 17//9 +f 73//10 74//10 57//10 +f 1//9 17//9 16//9 +f 72//10 73//10 57//10 +f 1//9 16//9 15//9 +f 71//10 72//10 57//10 +f 14//9 13//9 12//9 +f 68//10 69//10 70//10 +f 15//9 14//9 12//9 +f 68//10 70//10 71//10 +f 15//9 12//9 11//9 +f 67//10 68//10 71//10 +f 15//9 11//9 10//9 +f 66//10 67//10 71//10 +f 15//9 10//9 9//9 +f 65//10 66//10 71//10 +f 15//9 6//9 5//9 +f 61//10 62//10 71//10 +f 15//9 5//9 4//9 +f 60//10 61//10 71//10 +f 15//9 4//9 3//9 +f 59//10 60//10 71//10 +f 1//9 15//9 3//9 +f 59//10 71//10 57//10 +f 109//9 113//9 112//9 +f 122//10 123//10 119//10 +f 109//9 112//9 111//9 +f 121//10 122//10 119//10 +f 111//9 110//9 109//9 +f 119//10 120//10 121//10 +f 109//77 110//77 115//77 114//77 +f 110//78 111//78 116//78 115//78 +f 111//79 112//79 117//79 116//79 +f 112//80 113//80 118//80 117//80 +f 113//81 109//81 114//81 118//81 +f 114//82 115//82 120//82 119//82 +f 115//83 116//83 121//83 120//83 +f 116//84 117//84 122//84 121//84 +f 117//85 118//85 123//85 122//85 +f 118//86 114//86 119//86 123//86 +f 124//9 128//9 127//9 +f 137//10 138//10 134//10 +f 124//9 127//9 126//9 +f 136//10 137//10 134//10 +f 126//9 125//9 124//9 +f 134//10 135//10 136//10 +f 124//87 125//87 130//87 129//87 +f 125//88 126//88 131//88 130//88 +f 126//89 127//89 132//89 131//89 +f 127//90 128//90 133//90 132//90 +f 128//91 124//91 129//91 133//91 +f 129//77 130//77 135//77 134//77 +f 130//78 131//78 136//78 135//78 +f 131//79 132//79 137//79 136//79 +f 132//80 133//80 138//80 137//80 +f 133//81 129//81 134//81 138//81 +f 139//9 142//9 141//9 +f 149//10 150//10 147//10 +f 141//9 140//9 139//9 +f 147//10 148//10 149//10 +f 139//92 140//92 144//92 143//92 +f 140//93 141//93 145//93 144//93 +f 141//94 142//94 146//94 145//94 +f 142//95 139//95 143//95 146//95 +f 143//96 144//96 148//96 147//96 +f 144//97 145//97 149//97 148//97 +f 145//98 146//98 150//98 149//98 +f 146//99 143//99 147//99 150//99 +f 151//9 154//9 153//9 +f 161//10 162//10 159//10 +f 153//9 152//9 151//9 +f 159//10 160//10 161//10 +f 151//100 152//100 156//100 155//100 +f 152//101 153//101 157//101 156//101 +f 153//102 154//102 158//102 157//102 +f 154//103 151//103 155//103 158//103 +f 155//92 156//92 160//92 159//92 +f 156//93 157//93 161//93 160//93 +f 157//94 158//94 162//94 161//94 +f 158//95 155//95 159//95 162//95 +f 201//9 200//9 199//9 +f 205//10 206//10 207//10 +f 199//104 200//104 203//104 202//104 +f 200//105 201//105 204//105 203//105 +f 201//106 199//106 202//106 204//106 +f 202//107 203//107 206//107 205//107 +f 203//108 204//108 207//108 206//108 +f 204//109 202//109 205//109 207//109 +f 210//9 209//9 208//9 +f 214//10 215//10 216//10 +f 208//110 209//110 212//110 211//110 +f 209//111 210//111 213//111 212//111 +f 210//112 208//112 211//112 213//112 +f 211//104 212//104 215//104 214//104 +f 212//105 213//105 216//105 215//105 +f 213//106 211//106 214//106 216//106 +o neck +usemtl champagne +f 217//113 218//113 235//113 234//113 +f 218//114 219//114 236//114 235//114 +f 219//115 220//115 237//115 236//115 +f 224//116 225//116 242//116 241//116 +f 225//117 226//117 243//117 242//117 +f 226//118 227//118 244//118 243//118 +f 227//119 228//119 245//119 244//119 +f 228//120 229//120 246//120 245//120 +f 229//121 230//121 247//121 246//121 +f 233//122 217//122 234//122 250//122 +f 234//123 235//123 252//123 251//123 +f 235//124 236//124 253//124 252//124 +f 236//125 237//125 254//125 253//125 +f 240//126 241//126 258//126 257//126 +f 241//127 242//127 259//127 258//127 +f 242//128 243//128 260//128 259//128 +f 243//129 244//129 261//129 260//129 +f 244//130 245//130 262//130 261//130 +f 245//131 246//131 263//131 262//131 +f 246//132 247//132 264//132 263//132 +f 248//133 249//133 266//133 265//133 +f 249//134 250//134 267//134 266//134 +f 250//135 234//135 251//135 267//135 +f 217//9 233//9 232//9 +f 266//10 267//10 251//10 +f 217//9 229//9 228//9 +f 262//10 263//10 251//10 +f 217//9 228//9 227//9 +f 261//10 262//10 251//10 +f 217//9 221//9 220//9 +f 254//10 255//10 251//10 +f 217//9 220//9 219//9 +f 253//10 254//10 251//10 +f 219//9 218//9 217//9 +f 251//10 252//10 253//10 +f 270//9 269//9 268//9 +f 274//10 275//10 276//10 +f 268//136 269//136 272//136 271//136 +f 269//137 270//137 273//137 272//137 +f 270//138 268//138 271//138 273//138 +f 271//139 272//139 275//139 274//139 +f 272//140 273//140 276//140 275//140 +f 273//141 271//141 274//141 276//141 +f 279//9 278//9 277//9 +f 283//10 284//10 285//10 +f 277//142 278//142 281//142 280//142 +f 278//143 279//143 282//143 281//143 +f 279//144 277//144 280//144 282//144 +f 280//136 281//136 284//136 283//136 +f 281//137 282//137 285//137 284//137 +f 282//138 280//138 283//138 285//138 +f 364//9 367//9 366//9 +f 374//10 375//10 372//10 +f 366//9 365//9 364//9 +f 372//10 373//10 374//10 +f 364//145 365//145 369//145 368//145 +f 365//146 366//146 370//146 369//146 +f 366//147 367//147 371//147 370//147 +f 367//148 364//148 368//148 371//148 +f 368//149 369//149 373//149 372//149 +f 369//150 370//150 374//150 373//150 +f 370//151 371//151 375//151 374//151 +f 371//152 368//152 372//152 375//152 +f 376//9 379//9 378//9 +f 386//10 387//10 384//10 +f 378//9 377//9 376//9 +f 384//10 385//10 386//10 +f 376//153 377//153 381//153 380//153 +f 377//154 378//154 382//154 381//154 +f 378//155 379//155 383//155 382//155 +f 379//156 376//156 380//156 383//156 +f 380//145 381//145 385//145 384//145 +f 381//146 382//146 386//146 385//146 +f 382//147 383//147 387//147 386//147 +f 383//148 380//148 384//148 387//148 +usemtl dark +f 220//157 221//157 238//157 237//157 +f 221//158 222//158 239//158 238//158 +f 223//159 224//159 241//159 240//159 +f 230//160 231//160 248//160 247//160 +f 237//161 238//161 255//161 254//161 +f 238//162 239//162 256//162 255//162 +f 247//163 248//163 265//163 264//163 +f 217//9 227//9 226//9 +f 260//10 261//10 251//10 +f 217//9 226//9 225//9 +f 259//10 260//10 251//10 +f 217//9 225//9 224//9 +f 258//10 259//10 251//10 +f 217//9 224//9 223//9 +f 257//10 258//10 251//10 +f 217//9 223//9 222//9 +f 256//10 257//10 251//10 +f 217//9 222//9 221//9 +f 255//10 256//10 251//10 +f 334//9 338//9 337//9 +f 347//10 348//10 344//10 +f 334//9 337//9 336//9 +f 346//10 347//10 344//10 +f 336//9 335//9 334//9 +f 344//10 345//10 346//10 +f 334//164 335//164 340//164 339//164 +f 335//165 336//165 341//165 340//165 +f 336//166 337//166 342//166 341//166 +f 337//167 338//167 343//167 342//167 +f 338//168 334//168 339//168 343//168 +f 339//169 340//169 345//169 344//169 +f 340//170 341//170 346//170 345//170 +f 341//171 342//171 347//171 346//171 +f 342//172 343//172 348//172 347//172 +f 343//173 339//173 344//173 348//173 +f 349//9 353//9 352//9 +f 362//10 363//10 359//10 +f 349//9 352//9 351//9 +f 361//10 362//10 359//10 +f 351//9 350//9 349//9 +f 359//10 360//10 361//10 +f 349//174 350//174 355//174 354//174 +f 350//175 351//175 356//175 355//175 +f 351//176 352//176 357//176 356//176 +f 352//177 353//177 358//177 357//177 +f 353//178 349//178 354//178 358//178 +f 354//164 355//164 360//164 359//164 +f 355//165 356//165 361//165 360//165 +f 356//166 357//166 362//166 361//166 +f 357//167 358//167 363//167 362//167 +f 358//168 354//168 359//168 363//168 +usemtl glass +f 222//179 223//179 240//179 239//179 +f 231//180 232//180 249//180 248//180 +f 232//181 233//181 250//181 249//181 +f 239//182 240//182 257//182 256//182 +f 217//9 232//9 231//9 +f 265//10 266//10 251//10 +f 217//9 231//9 230//9 +f 264//10 265//10 251//10 +f 217//9 230//9 229//9 +f 263//10 264//10 251//10 +f 286//9 290//9 289//9 +f 299//10 300//10 296//10 +f 286//9 289//9 288//9 +f 298//10 299//10 296//10 +f 288//9 287//9 286//9 +f 296//10 297//10 298//10 +f 286//183 287//183 292//183 291//183 +f 287//184 288//184 293//184 292//184 +f 288//185 289//185 294//185 293//185 +f 289//186 290//186 295//186 294//186 +f 290//187 286//187 291//187 295//187 +f 291//188 292//188 297//188 296//188 +f 292//189 293//189 298//189 297//189 +f 293//190 294//190 299//190 298//190 +f 294//191 295//191 300//191 299//191 +f 295//192 291//192 296//192 300//192 +f 301//9 305//9 304//9 +f 314//10 315//10 311//10 +f 301//9 304//9 303//9 +f 313//10 314//10 311//10 +f 303//9 302//9 301//9 +f 311//10 312//10 313//10 +f 301//193 302//193 307//193 306//193 +f 302//194 303//194 308//194 307//194 +f 303//195 304//195 309//195 308//195 +f 304//196 305//196 310//196 309//196 +f 305//197 301//197 306//197 310//197 +f 306//183 307//183 312//183 311//183 +f 307//184 308//184 313//184 312//184 +f 308//185 309//185 314//185 313//185 +f 309//186 310//186 315//186 314//186 +f 310//187 306//187 311//187 315//187 +f 318//9 317//9 316//9 +f 322//10 323//10 324//10 +f 316//198 317//198 320//198 319//198 +f 317//199 318//199 321//199 320//199 +f 318//200 316//200 319//200 321//200 +f 319//201 320//201 323//201 322//201 +f 320//202 321//202 324//202 323//202 +f 321//203 319//203 322//203 324//203 +f 327//9 326//9 325//9 +f 331//10 332//10 333//10 +f 325//204 326//204 329//204 328//204 +f 326//205 327//205 330//205 329//205 +f 327//206 325//206 328//206 330//206 +f 328//198 329//198 332//198 331//198 +f 329//199 330//199 333//199 332//199 +f 330//200 328//200 331//200 333//200 +o body +usemtl glass +f 388//207 389//207 415//207 414//207 +f 389//208 390//208 416//208 415//208 +f 390//209 391//209 417//209 416//209 +f 391//210 392//210 418//210 417//210 +f 394//211 395//211 421//211 420//211 +f 395//212 396//212 422//212 421//212 +f 413//213 388//213 414//213 439//213 +f 414//214 415//214 441//214 440//214 +f 415//215 416//215 442//215 441//215 +f 416//216 417//216 443//216 442//216 +f 417//217 418//217 444//217 443//217 +f 420//218 421//218 447//218 446//218 +f 421//219 422//219 448//219 447//219 +f 439//220 414//220 440//220 465//220 +f 388//9 413//9 412//9 +f 464//10 465//10 440//10 +f 388//9 412//9 400//9 +f 452//10 464//10 440//10 +f 388//9 400//9 399//9 +f 451//10 452//10 440//10 +f 388//9 397//9 396//9 +f 448//10 449//10 440//10 +f 388//9 396//9 395//9 +f 447//10 448//10 440//10 +f 395//9 394//9 393//9 +f 445//10 446//10 447//10 +f 395//9 393//9 392//9 +f 444//10 445//10 447//10 +f 395//9 392//9 391//9 +f 443//10 444//10 447//10 +f 395//9 391//9 390//9 +f 442//10 443//10 447//10 +f 395//9 390//9 389//9 +f 441//10 442//10 447//10 +f 395//9 389//9 388//9 +f 440//10 441//10 447//10 +f 466//9 469//9 468//9 +f 476//10 477//10 474//10 +f 468//9 467//9 466//9 +f 474//10 475//10 476//10 +f 466//221 467//221 471//221 470//221 +f 467//222 468//222 472//222 471//222 +f 468//223 469//223 473//223 472//223 +f 469//224 466//224 470//224 473//224 +f 470//225 471//225 475//225 474//225 +f 471//226 472//226 476//226 475//226 +f 472//227 473//227 477//227 476//227 +f 473//228 470//228 474//228 477//228 +f 478//9 481//9 480//9 +f 488//10 489//10 486//10 +f 480//9 479//9 478//9 +f 486//10 487//10 488//10 +f 478//229 479//229 483//229 482//229 +f 479//230 480//230 484//230 483//230 +f 480//231 481//231 485//231 484//231 +f 481//232 478//232 482//232 485//232 +f 482//221 483//221 487//221 486//221 +f 483//222 484//222 488//222 487//222 +f 484//223 485//223 489//223 488//223 +f 485//224 482//224 486//224 489//224 +f 492//9 491//9 490//9 +f 496//10 497//10 498//10 +f 490//233 491//233 494//233 493//233 +f 491//234 492//234 495//234 494//234 +f 492//235 490//235 493//235 495//235 +f 493//236 494//236 497//236 496//236 +f 494//237 495//237 498//237 497//237 +f 495//238 493//238 496//238 498//238 +f 501//9 500//9 499//9 +f 505//10 506//10 507//10 +f 499//239 500//239 503//239 502//239 +f 500//240 501//240 504//240 503//240 +f 501//241 499//241 502//241 504//241 +f 502//233 503//233 506//233 505//233 +f 503//234 504//234 507//234 506//234 +f 504//235 502//235 505//235 507//235 +f 508//9 512//9 511//9 +f 521//10 522//10 518//10 +f 508//9 511//9 510//9 +f 520//10 521//10 518//10 +f 510//9 509//9 508//9 +f 518//10 519//10 520//10 +f 508//242 509//242 514//242 513//242 +f 509//243 510//243 515//243 514//243 +f 510//244 511//244 516//244 515//244 +f 511//245 512//245 517//245 516//245 +f 512//246 508//246 513//246 517//246 +f 513//247 514//247 519//247 518//247 +f 514//248 515//248 520//248 519//248 +f 515//249 516//249 521//249 520//249 +f 516//250 517//250 522//250 521//250 +f 517//251 513//251 518//251 522//251 +f 523//9 527//9 526//9 +f 536//10 537//10 533//10 +f 523//9 526//9 525//9 +f 535//10 536//10 533//10 +f 525//9 524//9 523//9 +f 533//10 534//10 535//10 +f 523//252 524//252 529//252 528//252 +f 524//253 525//253 530//253 529//253 +f 525//254 526//254 531//254 530//254 +f 526//255 527//255 532//255 531//255 +f 527//256 523//256 528//256 532//256 +f 528//242 529//242 534//242 533//242 +f 529//243 530//243 535//243 534//243 +f 530//244 531//244 536//244 535//244 +f 531//245 532//245 537//245 536//245 +f 532//246 528//246 533//246 537//246 +usemtl dark +f 392//257 393//257 419//257 418//257 +f 393//258 394//258 420//258 419//258 +f 398//259 399//259 425//259 424//259 +f 399//260 400//260 426//260 425//260 +f 400//261 401//261 427//261 426//261 +f 409//207 410//207 436//207 435//207 +f 410//262 411//262 437//262 436//262 +f 411//210 412//210 438//210 437//210 +f 412//263 413//263 439//263 438//263 +f 418//264 419//264 445//264 444//264 +f 419//265 420//265 446//265 445//265 +f 424//266 425//266 451//266 450//266 +f 425//267 426//267 452//267 451//267 +f 426//268 427//268 453//268 452//268 +f 435//214 436//214 462//214 461//214 +f 436//269 437//269 463//269 462//269 +f 437//217 438//217 464//217 463//217 +f 438//270 439//270 465//270 464//270 +f 411//9 410//9 409//9 +f 461//10 462//10 463//10 +f 600//9 599//9 598//9 +f 604//10 605//10 606//10 +f 598//271 599//271 602//271 601//271 +f 599//272 600//272 603//272 602//272 +f 600//273 598//273 601//273 603//273 +f 601//274 602//274 605//274 604//274 +f 602//275 603//275 606//275 605//275 +f 603//276 601//276 604//276 606//276 +f 609//9 608//9 607//9 +f 613//10 614//10 615//10 +f 607//277 608//277 611//277 610//277 +f 608//278 609//278 612//278 611//278 +f 609//279 607//279 610//279 612//279 +f 610//271 611//271 614//271 613//271 +f 611//272 612//272 615//272 614//272 +f 612//273 610//273 613//273 615//273 +usemtl champagne +f 396//280 397//280 423//280 422//280 +f 397//281 398//281 424//281 423//281 +f 401//282 402//282 428//282 427//282 +f 402//208 403//208 429//208 428//208 +f 403//207 404//207 430//207 429//207 +f 404//283 405//283 431//283 430//283 +f 405//284 406//284 432//284 431//284 +f 406//208 407//208 433//208 432//208 +f 407//285 408//285 434//285 433//285 +f 408//208 409//208 435//208 434//208 +f 422//286 423//286 449//286 448//286 +f 423//287 424//287 450//287 449//287 +f 427//288 428//288 454//288 453//288 +f 428//215 429//215 455//215 454//215 +f 429//214 430//214 456//214 455//214 +f 430//289 431//289 457//289 456//289 +f 431//290 432//290 458//290 457//290 +f 432//215 433//215 459//215 458//215 +f 433//291 434//291 460//291 459//291 +f 434//215 435//215 461//215 460//215 +f 411//9 409//9 408//9 +f 460//10 461//10 463//10 +f 411//9 408//9 407//9 +f 459//10 460//10 463//10 +f 411//9 407//9 406//9 +f 458//10 459//10 463//10 +f 412//9 411//9 406//9 +f 458//10 463//10 464//10 +f 405//9 404//9 403//9 +f 455//10 456//10 457//10 +f 405//9 403//9 402//9 +f 454//10 455//10 457//10 +f 405//9 402//9 401//9 +f 453//10 454//10 457//10 +f 406//9 405//9 401//9 +f 453//10 457//10 458//10 +f 412//9 406//9 401//9 +f 453//10 458//10 464//10 +f 412//9 401//9 400//9 +f 452//10 453//10 464//10 +f 388//9 399//9 398//9 +f 450//10 451//10 440//10 +f 388//9 398//9 397//9 +f 449//10 450//10 440//10 +f 538//9 541//9 540//9 +f 548//10 549//10 546//10 +f 540//9 539//9 538//9 +f 546//10 547//10 548//10 +f 538//292 539//292 543//292 542//292 +f 539//293 540//293 544//293 543//293 +f 540//294 541//294 545//294 544//294 +f 541//295 538//295 542//295 545//295 +f 542//296 543//296 547//296 546//296 +f 543//297 544//297 548//297 547//297 +f 544//298 545//298 549//298 548//298 +f 545//299 542//299 546//299 549//299 +f 550//9 553//9 552//9 +f 560//10 561//10 558//10 +f 552//9 551//9 550//9 +f 558//10 559//10 560//10 +f 550//300 551//300 555//300 554//300 +f 551//301 552//301 556//301 555//301 +f 552//302 553//302 557//302 556//302 +f 553//303 550//303 554//303 557//303 +f 554//292 555//292 559//292 558//292 +f 555//293 556//293 560//293 559//293 +f 556//294 557//294 561//294 560//294 +f 557//295 554//295 558//295 561//295 +f 564//9 563//9 562//9 +f 568//10 569//10 570//10 +f 562//304 563//304 566//304 565//304 +f 563//305 564//305 567//305 566//305 +f 564//306 562//306 565//306 567//306 +f 565//307 566//307 569//307 568//307 +f 566//308 567//308 570//308 569//308 +f 567//309 565//309 568//309 570//309 +f 573//9 572//9 571//9 +f 577//10 578//10 579//10 +f 571//310 572//310 575//310 574//310 +f 572//311 573//311 576//311 575//311 +f 573//312 571//312 574//312 576//312 +f 574//304 575//304 578//304 577//304 +f 575//305 576//305 579//305 578//305 +f 576//306 574//306 577//306 579//306 +f 582//9 581//9 580//9 +f 586//10 587//10 588//10 +f 580//313 581//313 584//313 583//313 +f 581//314 582//314 585//314 584//314 +f 582//315 580//315 583//315 585//315 +f 583//316 584//316 587//316 586//316 +f 584//317 585//317 588//317 587//317 +f 585//318 583//318 586//318 588//318 +f 591//9 590//9 589//9 +f 595//10 596//10 597//10 +f 589//319 590//319 593//319 592//319 +f 590//320 591//320 594//320 593//320 +f 591//321 589//321 592//321 594//321 +f 592//313 593//313 596//313 595//313 +f 593//314 594//314 597//314 596//314 +f 594//315 592//315 595//315 597//315 +o tail +usemtl champagne +f 616//322 617//322 638//322 637//322 +f 617//323 618//323 639//323 638//323 +f 618//324 619//324 640//324 639//324 +f 619//325 620//325 641//325 640//325 +f 620//326 621//326 642//326 641//326 +f 621//327 622//327 643//327 642//327 +f 623//328 624//328 645//328 644//328 +f 624//329 625//329 646//329 645//329 +f 625//330 626//330 647//330 646//330 +f 626//331 627//331 648//331 647//331 +f 632//332 633//332 654//332 653//332 +f 637//333 638//333 659//333 658//333 +f 638//334 639//334 660//334 659//334 +f 639//335 640//335 661//335 660//335 +f 640//336 641//336 662//336 661//336 +f 641//337 642//337 663//337 662//337 +f 642//338 643//338 664//338 663//338 +f 644//339 645//339 666//339 665//339 +f 645//340 646//340 667//340 666//340 +f 646//341 647//341 668//341 667//341 +f 647//342 648//342 669//342 668//342 +f 653//343 654//343 675//343 674//343 +f 616//9 633//9 632//9 +f 674//10 675//10 658//10 +f 627//9 626//9 625//9 +f 667//10 668//10 669//10 +f 630//9 627//9 625//9 +f 667//10 669//10 672//10 +f 630//9 625//9 624//9 +f 666//10 667//10 672//10 +f 630//9 624//9 623//9 +f 665//10 666//10 672//10 +f 616//9 632//9 622//9 +f 664//10 674//10 658//10 +f 622//9 621//9 620//9 +f 662//10 663//10 664//10 +f 616//9 622//9 620//9 +f 662//10 664//10 658//10 +f 616//9 620//9 619//9 +f 661//10 662//10 658//10 +f 616//9 619//9 618//9 +f 660//10 661//10 658//10 +f 618//9 617//9 616//9 +f 658//10 659//10 660//10 +f 681//9 680//9 679//9 +f 685//10 686//10 687//10 +f 679//344 680//344 683//344 682//344 +f 680//345 681//345 684//345 683//345 +f 681//346 679//346 682//346 684//346 +f 682//347 683//347 686//347 685//347 +f 683//348 684//348 687//348 686//348 +f 684//349 682//349 685//349 687//349 +f 690//9 689//9 688//9 +f 694//10 695//10 696//10 +f 688//350 689//350 692//350 691//350 +f 689//351 690//351 693//351 692//351 +f 690//352 688//352 691//352 693//352 +f 691//344 692//344 695//344 694//344 +f 692//345 693//345 696//345 695//345 +f 693//346 691//346 694//346 696//346 +f 735//9 734//9 733//9 +f 739//10 740//10 741//10 +f 733//353 734//353 737//353 736//353 +f 734//354 735//354 738//354 737//354 +f 735//355 733//355 736//355 738//355 +f 736//356 737//356 740//356 739//356 +f 737//357 738//357 741//357 740//357 +f 738//358 736//358 739//358 741//358 +f 744//9 743//9 742//9 +f 748//10 749//10 750//10 +f 742//359 743//359 746//359 745//359 +f 743//360 744//360 747//360 746//360 +f 744//361 742//361 745//361 747//361 +f 745//353 746//353 749//353 748//353 +f 746//354 747//354 750//354 749//354 +f 747//355 745//355 748//355 750//355 +f 753//9 752//9 751//9 +f 757//10 758//10 759//10 +f 751//362 752//362 755//362 754//362 +f 752//363 753//363 756//363 755//363 +f 753//364 751//364 754//364 756//364 +f 754//365 755//365 758//365 757//365 +f 755//366 756//366 759//366 758//366 +f 756//367 754//367 757//367 759//367 +f 762//9 761//9 760//9 +f 766//10 767//10 768//10 +f 760//368 761//368 764//368 763//368 +f 761//369 762//369 765//369 764//369 +f 762//370 760//370 763//370 765//370 +f 763//362 764//362 767//362 766//362 +f 764//363 765//363 768//363 767//363 +f 765//364 763//364 766//364 768//364 +usemtl dark +f 622//371 623//371 644//371 643//371 +f 629//372 630//372 651//372 650//372 +f 630//373 631//373 652//373 651//373 +f 631//374 632//374 653//374 652//374 +f 643//375 644//375 665//375 664//375 +f 650//376 651//376 672//376 671//376 +f 652//377 653//377 674//377 673//377 +f 630//9 623//9 622//9 +f 664//10 665//10 672//10 +f 631//9 630//9 622//9 +f 664//10 672//10 673//10 +f 632//9 631//9 622//9 +f 664//10 673//10 674//10 +f 717//9 716//9 715//9 +f 721//10 722//10 723//10 +f 715//378 716//378 719//378 718//378 +f 716//379 717//379 720//379 719//379 +f 717//380 715//380 718//380 720//380 +f 718//381 719//381 722//381 721//381 +f 719//382 720//382 723//382 722//382 +f 720//383 718//383 721//383 723//383 +f 726//9 725//9 724//9 +f 730//10 731//10 732//10 +f 724//384 725//384 728//384 727//384 +f 725//385 726//385 729//385 728//385 +f 726//386 724//386 727//386 729//386 +f 727//378 728//378 731//378 730//378 +f 728//379 729//379 732//379 731//379 +f 729//380 727//380 730//380 732//380 +usemtl glass +f 627//387 628//387 649//387 648//387 +f 628//388 629//388 650//388 649//388 +f 633//389 634//389 655//389 654//389 +f 634//390 635//390 656//390 655//390 +f 635//391 636//391 657//391 656//391 +f 636//392 616//392 637//392 657//392 +f 648//393 649//393 670//393 669//393 +f 649//394 650//394 671//394 670//394 +f 651//395 652//395 673//395 672//395 +f 654//396 655//396 676//396 675//396 +f 655//397 656//397 677//397 676//397 +f 656//398 657//398 678//398 677//398 +f 657//399 637//399 658//399 678//399 +f 616//9 636//9 635//9 +f 677//10 678//10 658//10 +f 616//9 635//9 634//9 +f 676//10 677//10 658//10 +f 616//9 634//9 633//9 +f 675//10 676//10 658//10 +f 630//9 629//9 628//9 +f 670//10 671//10 672//10 +f 630//9 628//9 627//9 +f 669//10 670//10 672//10 +f 699//9 698//9 697//9 +f 703//10 704//10 705//10 +f 697//400 698//400 701//400 700//400 +f 698//401 699//401 702//401 701//401 +f 699//402 697//402 700//402 702//402 +f 700//403 701//403 704//403 703//403 +f 701//404 702//404 705//404 704//404 +f 702//405 700//405 703//405 705//405 +f 708//9 707//9 706//9 +f 712//10 713//10 714//10 +f 706//406 707//406 710//406 709//406 +f 707//407 708//407 711//407 710//407 +f 708//408 706//408 709//408 711//408 +f 709//400 710//400 713//400 712//400 +f 710//401 711//401 714//401 713//401 +f 711//402 709//402 712//402 714//402 +f 769//9 772//9 771//9 +f 779//10 780//10 777//10 +f 771//9 770//9 769//9 +f 777//10 778//10 779//10 +f 769//409 770//409 774//409 773//409 +f 770//410 771//410 775//410 774//410 +f 771//411 772//411 776//411 775//411 +f 772//412 769//412 773//412 776//412 +f 773//413 774//413 778//413 777//413 +f 774//414 775//414 779//414 778//414 +f 775//415 776//415 780//415 779//415 +f 776//416 773//416 777//416 780//416 +f 781//9 784//9 783//9 +f 791//10 792//10 789//10 +f 783//9 782//9 781//9 +f 789//10 790//10 791//10 +f 781//417 782//417 786//417 785//417 +f 782//418 783//418 787//418 786//418 +f 783//419 784//419 788//419 787//419 +f 784//420 781//420 785//420 788//420 +f 785//409 786//409 790//409 789//409 +f 786//410 787//410 791//410 790//410 +f 787//411 788//411 792//411 791//411 +f 788//412 785//412 789//412 792//412 +o hindLegL +usemtl dark +f 793//421 794//421 807//421 806//421 +f 794//323 795//323 808//323 807//323 +f 798//323 799//323 812//323 811//323 +f 799//422 800//422 813//422 812//422 +f 800//423 801//423 814//423 813//423 +f 806//424 807//424 820//424 819//424 +f 807//334 808//334 821//334 820//334 +f 812//425 813//425 826//425 825//425 +f 813//426 814//426 827//426 826//426 +f 800//9 799//9 798//9 +f 824//10 825//10 826//10 +f 800//9 798//9 797//9 +f 823//10 824//10 826//10 +f 793//9 800//9 797//9 +f 823//10 826//10 819//10 +f 793//9 797//9 796//9 +f 822//10 823//10 819//10 +f 793//9 796//9 795//9 +f 821//10 822//10 819//10 +f 795//9 794//9 793//9 +f 819//10 820//10 821//10 +f 834//9 833//9 832//9 +f 838//10 839//10 840//10 +f 832//427 833//427 836//427 835//427 +f 833//428 834//428 837//428 836//428 +f 834//429 832//429 835//429 837//429 +f 835//430 836//430 839//430 838//430 +f 836//431 837//431 840//431 839//431 +f 837//432 835//432 838//432 840//432 +f 843//9 842//9 841//9 +f 847//10 848//10 849//10 +f 841//433 842//433 845//433 844//433 +f 842//434 843//434 846//434 845//434 +f 843//435 841//435 844//435 846//435 +f 844//427 845//427 848//427 847//427 +f 845//428 846//428 849//428 848//428 +f 846//429 844//429 847//429 849//429 +f 852//9 851//9 850//9 +f 856//10 857//10 858//10 +f 850//436 851//436 854//436 853//436 +f 851//29 852//29 855//29 854//29 +f 852//437 850//437 853//437 855//437 +f 853//438 854//438 857//438 856//438 +f 854//439 855//439 858//439 857//439 +f 855//440 853//440 856//440 858//440 +f 861//9 860//9 859//9 +f 865//10 866//10 867//10 +f 859//441 860//441 863//441 862//441 +f 860//442 861//442 864//442 863//442 +f 861//443 859//443 862//443 864//443 +f 862//436 863//436 866//436 865//436 +f 863//29 864//29 867//29 866//29 +f 864//437 862//437 865//437 867//437 +usemtl champagne +f 795//444 796//444 809//444 808//444 +f 796//323 797//323 810//323 809//323 +f 797//445 798//445 811//445 810//445 +f 801//446 802//446 815//446 814//446 +f 802//423 803//423 816//423 815//423 +f 803//447 804//447 817//447 816//447 +f 804//448 805//448 818//448 817//448 +f 805//323 793//323 806//323 818//323 +f 808//449 809//449 822//449 821//449 +f 809//334 810//334 823//334 822//334 +f 810//450 811//450 824//450 823//450 +f 811//334 812//334 825//334 824//334 +f 814//451 815//451 828//451 827//451 +f 815//426 816//426 829//426 828//426 +f 816//452 817//452 830//452 829//452 +f 817//453 818//453 831//453 830//453 +f 818//334 806//334 819//334 831//334 +f 793//9 805//9 804//9 +f 830//10 831//10 819//10 +f 793//9 804//9 803//9 +f 829//10 830//10 819//10 +f 793//9 803//9 802//9 +f 828//10 829//10 819//10 +f 793//9 802//9 801//9 +f 827//10 828//10 819//10 +f 793//9 801//9 800//9 +f 826//10 827//10 819//10 +f 870//9 869//9 868//9 +f 874//10 875//10 876//10 +f 868//454 869//454 872//454 871//454 +f 869//455 870//455 873//455 872//455 +f 870//456 868//456 871//456 873//456 +f 871//457 872//457 875//457 874//457 +f 872//458 873//458 876//458 875//458 +f 873//459 871//459 874//459 876//459 +f 879//9 878//9 877//9 +f 883//10 884//10 885//10 +f 877//460 878//460 881//460 880//460 +f 878//461 879//461 882//461 881//461 +f 879//462 877//462 880//462 882//462 +f 880//454 881//454 884//454 883//454 +f 881//455 882//455 885//455 884//455 +f 882//456 880//456 883//456 885//456 +o foreLegL +usemtl glass +f 886//463 887//463 893//463 892//463 +f 887//464 888//464 894//464 893//464 +f 891//465 886//465 892//465 897//465 +f 892//466 893//466 899//466 898//466 +f 895//467 896//467 902//467 901//467 +f 896//468 897//468 903//468 902//468 +f 897//469 892//469 898//469 903//469 +f 886//9 891//9 890//9 +f 902//10 903//10 898//10 +f 888//9 887//9 886//9 +f 898//10 899//10 900//10 +f 904//9 907//9 906//9 +f 914//10 915//10 912//10 +f 906//9 905//9 904//9 +f 912//10 913//10 914//10 +f 904//470 905//470 909//470 908//470 +f 905//471 906//471 910//471 909//471 +f 906//472 907//472 911//472 910//472 +f 907//473 904//473 908//473 911//473 +f 908//474 909//474 913//474 912//474 +f 909//475 910//475 914//475 913//475 +f 910//476 911//476 915//476 914//476 +f 911//477 908//477 912//477 915//477 +f 916//9 919//9 918//9 +f 926//10 927//10 924//10 +f 918//9 917//9 916//9 +f 924//10 925//10 926//10 +f 916//478 917//478 921//478 920//478 +f 917//479 918//479 922//479 921//479 +f 918//480 919//480 923//480 922//480 +f 919//481 916//481 920//481 923//481 +f 920//470 921//470 925//470 924//470 +f 921//471 922//471 926//471 925//471 +f 922//472 923//472 927//472 926//472 +f 923//473 920//473 924//473 927//473 +usemtl dark +f 888//482 889//482 895//482 894//482 +f 889//483 890//483 896//483 895//483 +f 890//484 891//484 897//484 896//484 +f 893//485 894//485 900//485 899//485 +f 894//486 895//486 901//486 900//486 +f 886//9 890//9 889//9 +f 901//10 902//10 898//10 +f 886//9 889//9 888//9 +f 900//10 901//10 898//10 +f 930//9 929//9 928//9 +f 934//10 935//10 936//10 +f 928//487 929//487 932//487 931//487 +f 929//488 930//488 933//488 932//488 +f 930//489 928//489 931//489 933//489 +f 931//490 932//490 935//490 934//490 +f 932//491 933//491 936//491 935//491 +f 933//492 931//492 934//492 936//492 +f 939//9 938//9 937//9 +f 943//10 944//10 945//10 +f 937//493 938//493 941//493 940//493 +f 938//494 939//494 942//494 941//494 +f 939//495 937//495 940//495 942//495 +f 940//487 941//487 944//487 943//487 +f 941//488 942//488 945//488 944//488 +f 942//489 940//489 943//489 945//489 +o hornL +usemtl dark +f 948//9 947//9 946//9 +f 955//10 956//10 957//10 +f 946//496 947//496 950//496 949//496 +f 947//497 948//497 951//497 950//497 +f 948//498 946//498 949//498 951//498 +f 949//499 950//499 953//499 952//499 +f 950//500 951//500 954//500 953//500 +f 951//501 949//501 952//501 954//501 +f 952//502 953//502 956//502 955//502 +f 953//503 954//503 957//503 956//503 +f 954//504 952//504 955//504 957//504 +o hindLegR +usemtl dark +f 958//421 959//421 972//421 971//421 +f 959//323 960//323 973//323 972//323 +f 963//323 964//323 977//323 976//323 +f 964//422 965//422 978//422 977//422 +f 965//423 966//423 979//423 978//423 +f 971//424 972//424 985//424 984//424 +f 972//334 973//334 986//334 985//334 +f 977//425 978//425 991//425 990//425 +f 978//426 979//426 992//426 991//426 +f 965//9 964//9 963//9 +f 989//10 990//10 991//10 +f 965//9 963//9 962//9 +f 988//10 989//10 991//10 +f 958//9 965//9 962//9 +f 988//10 991//10 984//10 +f 958//9 962//9 961//9 +f 987//10 988//10 984//10 +f 958//9 961//9 960//9 +f 986//10 987//10 984//10 +f 960//9 959//9 958//9 +f 984//10 985//10 986//10 +f 999//9 998//9 997//9 +f 1003//10 1004//10 1005//10 +f 997//427 998//427 1001//427 1000//427 +f 998//428 999//428 1002//428 1001//428 +f 999//429 997//429 1000//429 1002//429 +f 1000//430 1001//430 1004//430 1003//430 +f 1001//431 1002//431 1005//431 1004//431 +f 1002//432 1000//432 1003//432 1005//432 +f 1008//9 1007//9 1006//9 +f 1012//10 1013//10 1014//10 +f 1006//433 1007//433 1010//433 1009//433 +f 1007//434 1008//434 1011//434 1010//434 +f 1008//435 1006//435 1009//435 1011//435 +f 1009//427 1010//427 1013//427 1012//427 +f 1010//428 1011//428 1014//428 1013//428 +f 1011//429 1009//429 1012//429 1014//429 +f 1017//9 1016//9 1015//9 +f 1021//10 1022//10 1023//10 +f 1015//436 1016//436 1019//436 1018//436 +f 1016//29 1017//29 1020//29 1019//29 +f 1017//437 1015//437 1018//437 1020//437 +f 1018//438 1019//438 1022//438 1021//438 +f 1019//439 1020//439 1023//439 1022//439 +f 1020//440 1018//440 1021//440 1023//440 +f 1026//9 1025//9 1024//9 +f 1030//10 1031//10 1032//10 +f 1024//441 1025//441 1028//441 1027//441 +f 1025//442 1026//442 1029//442 1028//442 +f 1026//443 1024//443 1027//443 1029//443 +f 1027//436 1028//436 1031//436 1030//436 +f 1028//29 1029//29 1032//29 1031//29 +f 1029//437 1027//437 1030//437 1032//437 +usemtl champagne +f 960//444 961//444 974//444 973//444 +f 961//323 962//323 975//323 974//323 +f 962//445 963//445 976//445 975//445 +f 966//446 967//446 980//446 979//446 +f 967//423 968//423 981//423 980//423 +f 968//447 969//447 982//447 981//447 +f 969//448 970//448 983//448 982//448 +f 970//323 958//323 971//323 983//323 +f 973//449 974//449 987//449 986//449 +f 974//334 975//334 988//334 987//334 +f 975//450 976//450 989//450 988//450 +f 976//334 977//334 990//334 989//334 +f 979//451 980//451 993//451 992//451 +f 980//426 981//426 994//426 993//426 +f 981//452 982//452 995//452 994//452 +f 982//453 983//453 996//453 995//453 +f 983//334 971//334 984//334 996//334 +f 958//9 970//9 969//9 +f 995//10 996//10 984//10 +f 958//9 969//9 968//9 +f 994//10 995//10 984//10 +f 958//9 968//9 967//9 +f 993//10 994//10 984//10 +f 958//9 967//9 966//9 +f 992//10 993//10 984//10 +f 958//9 966//9 965//9 +f 991//10 992//10 984//10 +f 1035//9 1034//9 1033//9 +f 1039//10 1040//10 1041//10 +f 1033//454 1034//454 1037//454 1036//454 +f 1034//455 1035//455 1038//455 1037//455 +f 1035//456 1033//456 1036//456 1038//456 +f 1036//457 1037//457 1040//457 1039//457 +f 1037//458 1038//458 1041//458 1040//458 +f 1038//459 1036//459 1039//459 1041//459 +f 1044//9 1043//9 1042//9 +f 1048//10 1049//10 1050//10 +f 1042//460 1043//460 1046//460 1045//460 +f 1043//461 1044//461 1047//461 1046//461 +f 1044//462 1042//462 1045//462 1047//462 +f 1045//454 1046//454 1049//454 1048//454 +f 1046//455 1047//455 1050//455 1049//455 +f 1047//456 1045//456 1048//456 1050//456 +o foreLegR +usemtl glass +f 1051//463 1052//463 1058//463 1057//463 +f 1052//464 1053//464 1059//464 1058//464 +f 1056//465 1051//465 1057//465 1062//465 +f 1057//466 1058//466 1064//466 1063//466 +f 1060//467 1061//467 1067//467 1066//467 +f 1061//468 1062//468 1068//468 1067//468 +f 1062//469 1057//469 1063//469 1068//469 +f 1051//9 1056//9 1055//9 +f 1067//10 1068//10 1063//10 +f 1053//9 1052//9 1051//9 +f 1063//10 1064//10 1065//10 +f 1069//9 1072//9 1071//9 +f 1079//10 1080//10 1077//10 +f 1071//9 1070//9 1069//9 +f 1077//10 1078//10 1079//10 +f 1069//470 1070//470 1074//470 1073//470 +f 1070//471 1071//471 1075//471 1074//471 +f 1071//472 1072//472 1076//472 1075//472 +f 1072//473 1069//473 1073//473 1076//473 +f 1073//474 1074//474 1078//474 1077//474 +f 1074//475 1075//475 1079//475 1078//475 +f 1075//476 1076//476 1080//476 1079//476 +f 1076//477 1073//477 1077//477 1080//477 +f 1081//9 1084//9 1083//9 +f 1091//10 1092//10 1089//10 +f 1083//9 1082//9 1081//9 +f 1089//10 1090//10 1091//10 +f 1081//478 1082//478 1086//478 1085//478 +f 1082//479 1083//479 1087//479 1086//479 +f 1083//480 1084//480 1088//480 1087//480 +f 1084//481 1081//481 1085//481 1088//481 +f 1085//470 1086//470 1090//470 1089//470 +f 1086//471 1087//471 1091//471 1090//471 +f 1087//472 1088//472 1092//472 1091//472 +f 1088//473 1085//473 1089//473 1092//473 +usemtl dark +f 1053//482 1054//482 1060//482 1059//482 +f 1054//483 1055//483 1061//483 1060//483 +f 1055//484 1056//484 1062//484 1061//484 +f 1058//485 1059//485 1065//485 1064//485 +f 1059//486 1060//486 1066//486 1065//486 +f 1051//9 1055//9 1054//9 +f 1066//10 1067//10 1063//10 +f 1051//9 1054//9 1053//9 +f 1065//10 1066//10 1063//10 +f 1095//9 1094//9 1093//9 +f 1099//10 1100//10 1101//10 +f 1093//487 1094//487 1097//487 1096//487 +f 1094//488 1095//488 1098//488 1097//488 +f 1095//489 1093//489 1096//489 1098//489 +f 1096//490 1097//490 1100//490 1099//490 +f 1097//491 1098//491 1101//491 1100//491 +f 1098//492 1096//492 1099//492 1101//492 +f 1104//9 1103//9 1102//9 +f 1108//10 1109//10 1110//10 +f 1102//493 1103//493 1106//493 1105//493 +f 1103//494 1104//494 1107//494 1106//494 +f 1104//495 1102//495 1105//495 1107//495 +f 1105//487 1106//487 1109//487 1108//487 +f 1106//488 1107//488 1110//488 1109//488 +f 1107//489 1105//489 1108//489 1110//489 +o hornR +usemtl dark +f 1113//9 1112//9 1111//9 +f 1120//10 1121//10 1122//10 +f 1111//496 1112//496 1115//496 1114//496 +f 1112//497 1113//497 1116//497 1115//497 +f 1113//498 1111//498 1114//498 1116//498 +f 1114//499 1115//499 1118//499 1117//499 +f 1115//500 1116//500 1119//500 1118//500 +f 1116//501 1114//501 1117//501 1119//501 +f 1117//502 1118//502 1121//502 1120//502 +f 1118//503 1119//503 1122//503 1121//503 +f 1119//504 1117//504 1120//504 1122//504 +o crest +usemtl champagne +f 1125//9 1124//9 1123//9 +f 1132//10 1133//10 1134//10 +f 1123//505 1124//505 1127//505 1126//505 +f 1124//506 1125//506 1128//506 1127//506 +f 1125//507 1123//507 1126//507 1128//507 +f 1126//508 1127//508 1130//508 1129//508 +f 1127//509 1128//509 1131//509 1130//509 +f 1128//510 1126//510 1129//510 1131//510 +f 1129//511 1130//511 1133//511 1132//511 +f 1130//512 1131//512 1134//512 1133//512 +f 1131//513 1129//513 1132//513 1134//513 +o wingL +usemtl dark +f 1135//514 1138//514 1137//514 +f 1149//515 1150//515 1147//515 +f 1137//514 1136//514 1135//514 +f 1147//515 1148//515 1149//515 +f 1135//516 1136//516 1140//516 1139//516 +f 1136//517 1137//517 1141//517 1140//517 +f 1137//518 1138//518 1142//518 1141//518 +f 1138//519 1135//519 1139//519 1142//519 +f 1139//520 1140//520 1144//520 1143//520 +f 1140//521 1141//521 1145//521 1144//521 +f 1141//522 1142//522 1146//522 1145//522 +f 1142//523 1139//523 1143//523 1146//523 +f 1143//524 1144//524 1148//524 1147//524 +f 1144//525 1145//525 1149//525 1148//525 +f 1145//526 1146//526 1150//526 1149//526 +f 1146//527 1143//527 1147//527 1150//527 +usemtl glass +f 1151//514 1154//514 1153//514 +f 1165//515 1166//515 1163//515 +f 1153//514 1152//514 1151//514 +f 1163//515 1164//515 1165//515 +f 1151//528 1152//528 1156//528 1155//528 +f 1152//529 1153//529 1157//529 1156//529 +f 1153//530 1154//530 1158//530 1157//530 +f 1154//531 1151//531 1155//531 1158//531 +f 1155//532 1156//532 1160//532 1159//532 +f 1156//533 1157//533 1161//533 1160//533 +f 1157//534 1158//534 1162//534 1161//534 +f 1158//535 1155//535 1159//535 1162//535 +f 1159//536 1160//536 1164//536 1163//536 +f 1160//537 1161//537 1165//537 1164//537 +f 1161//538 1162//538 1166//538 1165//538 +f 1162//539 1159//539 1163//539 1166//539 +f 1167//540 1170//540 1169//540 +f 1181//541 1182//541 1179//541 +f 1169//514 1168//514 1167//514 +f 1179//515 1180//515 1181//515 +f 1167//542 1168//542 1172//542 1171//542 +f 1168//543 1169//543 1173//543 1172//543 +f 1169//544 1170//544 1174//544 1173//544 +f 1170//545 1167//545 1171//545 1174//545 +f 1171//546 1172//546 1176//546 1175//546 +f 1172//547 1173//547 1177//547 1176//547 +f 1173//548 1174//548 1178//548 1177//548 +f 1174//549 1171//549 1175//549 1178//549 +f 1175//550 1176//550 1180//550 1179//550 +f 1176//551 1177//551 1181//551 1180//551 +f 1177//552 1178//552 1182//552 1181//552 +f 1178//553 1175//553 1179//553 1182//553 +o wingR +usemtl dark +f 1183//554 1186//554 1185//554 +f 1197//555 1198//555 1195//555 +f 1185//554 1184//554 1183//554 +f 1195//555 1196//555 1197//555 +f 1183//556 1184//556 1188//556 1187//556 +f 1184//557 1185//557 1189//557 1188//557 +f 1185//558 1186//558 1190//558 1189//558 +f 1186//559 1183//559 1187//559 1190//559 +f 1187//560 1188//560 1192//560 1191//560 +f 1188//561 1189//561 1193//561 1192//561 +f 1189//562 1190//562 1194//562 1193//562 +f 1190//563 1187//563 1191//563 1194//563 +f 1191//564 1192//564 1196//564 1195//564 +f 1192//565 1193//565 1197//565 1196//565 +f 1193//566 1194//566 1198//566 1197//566 +f 1194//567 1191//567 1195//567 1198//567 +usemtl glass +f 1199//554 1202//554 1201//554 +f 1213//555 1214//555 1211//555 +f 1201//554 1200//554 1199//554 +f 1211//555 1212//555 1213//555 +f 1199//568 1200//568 1204//568 1203//568 +f 1200//569 1201//569 1205//569 1204//569 +f 1201//570 1202//570 1206//570 1205//570 +f 1202//571 1199//571 1203//571 1206//571 +f 1203//572 1204//572 1208//572 1207//572 +f 1204//573 1205//573 1209//573 1208//573 +f 1205//574 1206//574 1210//574 1209//574 +f 1206//575 1203//575 1207//575 1210//575 +f 1207//576 1208//576 1212//576 1211//576 +f 1208//577 1209//577 1213//577 1212//577 +f 1209//578 1210//578 1214//578 1213//578 +f 1210//579 1207//579 1211//579 1214//579 +f 1215//580 1218//580 1217//580 +f 1229//581 1230//581 1227//581 +f 1217//554 1216//554 1215//554 +f 1227//555 1228//555 1229//555 +f 1215//582 1216//582 1220//582 1219//582 +f 1216//583 1217//583 1221//583 1220//583 +f 1217//584 1218//584 1222//584 1221//584 +f 1218//585 1215//585 1219//585 1222//585 +f 1219//586 1220//586 1224//586 1223//586 +f 1220//587 1221//587 1225//587 1224//587 +f 1221//588 1222//588 1226//588 1225//588 +f 1222//589 1219//589 1223//589 1226//589 +f 1223//590 1224//590 1228//590 1227//590 +f 1224//591 1225//591 1229//591 1228//591 +f 1225//592 1226//592 1230//592 1229//592 +f 1226//593 1223//593 1227//593 1230//593 diff --git a/assets/DragonPrint-champagne.stl b/assets/DragonPrint-champagne.stl new file mode 100644 index 000000000..8c184cf78 --- /dev/null +++ b/assets/DragonPrint-champagne.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dfa207d0b0017278aa636ea8c62b9a4a7413911008222ceea1eeaad7223e86c +size 109684 diff --git a/assets/DragonPrint-dark.stl b/assets/DragonPrint-dark.stl new file mode 100644 index 000000000..f3bb136af --- /dev/null +++ b/assets/DragonPrint-dark.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f76c828e0319cdcfe42465fc3e552193bdfe0394ff69885cebe1f8e6748b41b +size 86484 diff --git a/assets/DragonPrint-frame.stl b/assets/DragonPrint-frame.stl new file mode 100644 index 000000000..8ab76379e --- /dev/null +++ b/assets/DragonPrint-frame.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6759aed3174256756394f33d9871eeb514e7b910cd874cbc8b2b942dc2b4e66e +size 49384 diff --git a/assets/DragonPrint-glass.stl b/assets/DragonPrint-glass.stl new file mode 100644 index 000000000..8855a3ede --- /dev/null +++ b/assets/DragonPrint-glass.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:72850a2650c4b758c9aa96c65002f20af6f06c156e409ba3f1217842d219dcbb +size 94084 diff --git a/assets/DragonPrint.mtl b/assets/DragonPrint.mtl new file mode 100644 index 000000000..4c90ffb80 --- /dev/null +++ b/assets/DragonPrint.mtl @@ -0,0 +1,45 @@ +# Materials for DragonPrint.obj +newmtl dark +Ka 0.060 0.062 0.066 +Kd 0.300 0.310 0.330 +Ks 0.700 0.700 0.700 +Ns 180.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.35 + +newmtl champagne +Ka 0.144 0.128 0.108 +Kd 0.720 0.640 0.540 +Ks 0.850 0.800 0.700 +Ns 120.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.40 + +newmtl glass +Ka 0.176 0.184 0.180 +Kd 0.880 0.920 0.900 +Ks 0.950 0.950 0.950 +Ns 300.0 +d 0.55 +Ni 1.50 +illum 4 +Pm 0.00 +Pr 0.15 + +newmtl frame +Ka 0.024 0.022 0.020 +Kd 0.120 0.110 0.100 +Ks 0.300 0.300 0.300 +Ns 40.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.00 +Pr 0.70 + diff --git a/assets/DragonPrint.obj b/assets/DragonPrint.obj new file mode 100644 index 000000000..bf4ef0062 --- /dev/null +++ b/assets/DragonPrint.obj @@ -0,0 +1,9538 @@ +# Singe dragon, printable solid model built by util/dragonModel.py from the tiles of Dragon.jpeg +# Units: 0.01 per image pixel; Y up, faces right (+X), resting on Y = 0, symmetric about Z = 0 +mtllib DragonPrint.mtl +v 0.70500 6.60000 -0.38000 +v 0.74500 6.56000 -0.38000 +v 1.22500 6.31000 -0.38000 +v 1.37500 6.16000 -0.38000 +v 1.95500 5.78000 -0.38000 +v 2.06500 5.67000 -0.38000 +v 1.43500 5.02000 -0.38000 +v 1.47500 4.98000 -0.38000 +v 2.50500 5.19000 -0.38000 +v 3.02500 4.76000 -0.38000 +v 3.06500 4.80000 -0.38000 +v 3.32500 5.32000 -0.38000 +v 3.25500 5.39000 -0.38000 +v 3.20500 5.37000 -0.38000 +v 2.76500 5.47000 -0.38000 +v 2.65500 5.58000 -0.38000 +v 2.76500 5.69000 -0.38000 +v 3.09500 5.76000 -0.38000 +v 3.32500 5.49000 -0.38000 +v 3.36500 5.53000 -0.38000 +v 3.55500 5.92000 -0.38000 +v 3.20500 6.40000 -0.38000 +v 3.16500 6.29000 -0.38000 +v 3.05500 6.18000 -0.38000 +v 2.66500 6.21000 -0.38000 +v 2.41500 6.26000 -0.38000 +v 1.92500 6.59000 -0.38000 +v 0.74500 6.64000 -0.38000 +v 0.61590 6.60000 0.00000 +v 0.70730 6.50860 0.00000 +v 1.18730 6.25860 0.00000 +v 1.33504 6.11086 0.00000 +v 1.91504 5.73086 0.00000 +v 1.97660 5.66931 0.00000 +v 1.34660 5.01931 0.00000 +v 1.45440 4.91150 0.00000 +v 2.48807 5.12225 0.00000 +v 3.02902 4.67493 0.00000 +v 3.11678 4.76268 0.00000 +v 3.40166 5.33244 0.00000 +v 3.27017 5.46392 0.00000 +v 3.19982 5.43578 0.00000 +v 2.79669 5.52740 0.00000 +v 2.74410 5.58000 0.00000 +v 2.79634 5.63225 0.00000 +v 3.07137 5.69059 0.00000 +v 3.32129 5.39720 0.00000 +v 3.41701 5.49292 0.00000 +v 3.62824 5.92649 0.00000 +v 3.18622 6.53269 0.00000 +v 3.11057 6.32466 0.00000 +v 3.03094 6.24504 0.00000 +v 2.67362 6.27252 0.00000 +v 2.43973 6.31930 0.00000 +v 1.94544 6.65219 0.00000 +v 0.72002 6.70412 0.00000 +v 0.70500 6.60000 0.38000 +v 0.74500 6.56000 0.38000 +v 1.22500 6.31000 0.38000 +v 1.37500 6.16000 0.38000 +v 1.95500 5.78000 0.38000 +v 2.06500 5.67000 0.38000 +v 1.43500 5.02000 0.38000 +v 1.47500 4.98000 0.38000 +v 2.50500 5.19000 0.38000 +v 3.02500 4.76000 0.38000 +v 3.06500 4.80000 0.38000 +v 3.32500 5.32000 0.38000 +v 3.25500 5.39000 0.38000 +v 3.20500 5.37000 0.38000 +v 2.76500 5.47000 0.38000 +v 2.65500 5.58000 0.38000 +v 2.76500 5.69000 0.38000 +v 3.09500 5.76000 0.38000 +v 3.32500 5.49000 0.38000 +v 3.36500 5.53000 0.38000 +v 3.55500 5.92000 0.38000 +v 3.20500 6.40000 0.38000 +v 3.16500 6.29000 0.38000 +v 3.05500 6.18000 0.38000 +v 2.66500 6.21000 0.38000 +v 2.41500 6.26000 0.38000 +v 1.92500 6.59000 0.38000 +v 0.74500 6.64000 0.38000 +v 0.71349 6.60000 0.38000 +v 0.74859 6.56490 0.38000 +v 1.22859 6.31490 0.38000 +v 1.30717 6.23632 0.38000 +v 1.38547 6.24611 0.38000 +v 1.45560 6.22607 0.38000 +v 1.82484 6.25601 0.38000 +v 2.18493 6.24600 0.38000 +v 2.38239 6.25588 0.38000 +v 2.39593 6.26941 0.38000 +v 2.27157 6.36507 0.38000 +v 1.92316 6.58407 0.38000 +v 0.74738 6.63389 0.38000 +v 0.71349 6.60000 0.41250 +v 0.74859 6.56490 0.41250 +v 1.22859 6.31490 0.41250 +v 1.30717 6.23632 0.41250 +v 1.38547 6.24611 0.41250 +v 1.45560 6.22607 0.41250 +v 1.82484 6.25601 0.41250 +v 2.18493 6.24600 0.41250 +v 2.38239 6.25588 0.41250 +v 2.39593 6.26941 0.41250 +v 2.27157 6.36507 0.41250 +v 1.92316 6.58407 0.41250 +v 0.74738 6.63389 0.41250 +v 0.72409 6.60000 0.42000 +v 0.75308 6.57101 0.42000 +v 1.23308 6.32101 0.42000 +v 1.30988 6.24421 0.42000 +v 1.38606 6.25374 0.42000 +v 1.45635 6.23365 0.42000 +v 1.82464 6.26352 0.42000 +v 2.18485 6.25351 0.42000 +v 2.37913 6.26322 0.42000 +v 2.38458 6.26867 0.42000 +v 2.26728 6.35891 0.42000 +v 1.92085 6.57666 0.42000 +v 0.75035 6.62626 0.42000 +v 0.72409 6.60000 -0.42000 +v 0.75308 6.57101 -0.42000 +v 1.23308 6.32101 -0.42000 +v 1.30988 6.24421 -0.42000 +v 1.38606 6.25374 -0.42000 +v 1.45635 6.23365 -0.42000 +v 1.82464 6.26352 -0.42000 +v 2.18485 6.25351 -0.42000 +v 2.37913 6.26322 -0.42000 +v 2.38458 6.26867 -0.42000 +v 2.26728 6.35891 -0.42000 +v 1.92085 6.57666 -0.42000 +v 0.75035 6.62626 -0.42000 +v 0.71349 6.60000 -0.41250 +v 0.74859 6.56490 -0.41250 +v 1.22859 6.31490 -0.41250 +v 1.30717 6.23632 -0.41250 +v 1.38547 6.24611 -0.41250 +v 1.45560 6.22607 -0.41250 +v 1.82484 6.25601 -0.41250 +v 2.18493 6.24600 -0.41250 +v 2.38239 6.25588 -0.41250 +v 2.39593 6.26941 -0.41250 +v 2.27157 6.36507 -0.41250 +v 1.92316 6.58407 -0.41250 +v 0.74738 6.63389 -0.41250 +v 0.71349 6.60000 -0.38000 +v 0.74859 6.56490 -0.38000 +v 1.22859 6.31490 -0.38000 +v 1.30717 6.23632 -0.38000 +v 1.38547 6.24611 -0.38000 +v 1.45560 6.22607 -0.38000 +v 1.82484 6.25601 -0.38000 +v 2.18493 6.24600 -0.38000 +v 2.38239 6.25588 -0.38000 +v 2.39593 6.26941 -0.38000 +v 2.27157 6.36507 -0.38000 +v 1.92316 6.58407 -0.38000 +v 0.74738 6.63389 -0.38000 +v 1.45440 6.20393 0.38000 +v 1.38458 6.22388 0.38000 +v 1.32773 6.21576 0.38000 +v 1.37881 6.16468 0.38000 +v 1.95881 5.78468 0.38000 +v 2.07844 5.66504 0.38000 +v 2.23626 5.59600 0.38000 +v 2.66251 5.59600 0.38000 +v 2.76202 5.69550 0.38000 +v 3.09694 5.76655 0.38000 +v 3.11423 5.74925 0.38000 +v 3.16191 5.81600 0.38000 +v 3.17900 5.81600 0.38000 +v 3.17900 6.12772 0.38000 +v 3.10430 6.21176 0.38000 +v 3.05710 6.17400 0.38000 +v 2.98465 6.17400 0.38000 +v 2.55582 6.22386 0.38000 +v 2.45588 6.18389 0.38000 +v 2.24054 6.20440 0.38000 +v 2.23068 6.23400 0.38000 +v 1.82524 6.23400 0.38000 +v 1.45440 6.20393 0.40500 +v 1.38458 6.22388 0.40500 +v 1.32773 6.21576 0.40500 +v 1.37881 6.16468 0.40500 +v 1.95881 5.78468 0.40500 +v 2.07844 5.66504 0.40500 +v 2.23626 5.59600 0.40500 +v 2.66251 5.59600 0.40500 +v 2.76202 5.69550 0.40500 +v 3.09694 5.76655 0.40500 +v 3.11423 5.74925 0.40500 +v 3.16191 5.81600 0.40500 +v 3.17900 5.81600 0.40500 +v 3.17900 6.12772 0.40500 +v 3.10430 6.21176 0.40500 +v 3.05710 6.17400 0.40500 +v 2.98465 6.17400 0.40500 +v 2.55582 6.22386 0.40500 +v 2.45588 6.18389 0.40500 +v 2.24054 6.20440 0.40500 +v 2.23068 6.23400 0.40500 +v 1.82524 6.23400 0.40500 +v 1.45290 6.18876 0.42000 +v 1.38354 6.20858 0.42000 +v 1.35955 6.20515 0.42000 +v 1.38832 6.17638 0.42000 +v 1.96832 5.79638 0.42000 +v 2.08705 5.67765 0.42000 +v 2.23939 5.61100 0.42000 +v 2.65630 5.61100 0.42000 +v 2.75455 5.70925 0.42000 +v 3.10179 5.78291 0.42000 +v 3.11232 5.77238 0.42000 +v 3.15419 5.83100 0.42000 +v 3.16400 5.83100 0.42000 +v 3.16400 6.12202 0.42000 +v 3.10255 6.19115 0.42000 +v 3.06237 6.15900 0.42000 +v 2.98378 6.15900 0.42000 +v 2.55786 6.20853 0.42000 +v 2.45807 6.16861 0.42000 +v 2.22940 6.19039 0.42000 +v 2.21986 6.21900 0.42000 +v 1.82585 6.21900 0.42000 +v 1.45290 6.18876 -0.42000 +v 1.38354 6.20858 -0.42000 +v 1.35955 6.20515 -0.42000 +v 1.38832 6.17638 -0.42000 +v 1.96832 5.79638 -0.42000 +v 2.08705 5.67765 -0.42000 +v 2.23939 5.61100 -0.42000 +v 2.65630 5.61100 -0.42000 +v 2.75455 5.70925 -0.42000 +v 3.10179 5.78291 -0.42000 +v 3.11232 5.77238 -0.42000 +v 3.15419 5.83100 -0.42000 +v 3.16400 5.83100 -0.42000 +v 3.16400 6.12202 -0.42000 +v 3.10255 6.19115 -0.42000 +v 3.06237 6.15900 -0.42000 +v 2.98378 6.15900 -0.42000 +v 2.55786 6.20853 -0.42000 +v 2.45807 6.16861 -0.42000 +v 2.22940 6.19039 -0.42000 +v 2.21986 6.21900 -0.42000 +v 1.82585 6.21900 -0.42000 +v 1.45440 6.20393 -0.40500 +v 1.38458 6.22388 -0.40500 +v 1.32773 6.21576 -0.40500 +v 1.37881 6.16468 -0.40500 +v 1.95881 5.78468 -0.40500 +v 2.07844 5.66504 -0.40500 +v 2.23626 5.59600 -0.40500 +v 2.66251 5.59600 -0.40500 +v 2.76202 5.69550 -0.40500 +v 3.09694 5.76655 -0.40500 +v 3.11423 5.74925 -0.40500 +v 3.16191 5.81600 -0.40500 +v 3.17900 5.81600 -0.40500 +v 3.17900 6.12772 -0.40500 +v 3.10430 6.21176 -0.40500 +v 3.05710 6.17400 -0.40500 +v 2.98465 6.17400 -0.40500 +v 2.55582 6.22386 -0.40500 +v 2.45588 6.18389 -0.40500 +v 2.24054 6.20440 -0.40500 +v 2.23068 6.23400 -0.40500 +v 1.82524 6.23400 -0.40500 +v 1.45440 6.20393 -0.38000 +v 1.38458 6.22388 -0.38000 +v 1.32773 6.21576 -0.38000 +v 1.37881 6.16468 -0.38000 +v 1.95881 5.78468 -0.38000 +v 2.07844 5.66504 -0.38000 +v 2.23626 5.59600 -0.38000 +v 2.66251 5.59600 -0.38000 +v 2.76202 5.69550 -0.38000 +v 3.09694 5.76655 -0.38000 +v 3.11423 5.74925 -0.38000 +v 3.16191 5.81600 -0.38000 +v 3.17900 5.81600 -0.38000 +v 3.17900 6.12772 -0.38000 +v 3.10430 6.21176 -0.38000 +v 3.05710 6.17400 -0.38000 +v 2.98465 6.17400 -0.38000 +v 2.55582 6.22386 -0.38000 +v 2.45588 6.18389 -0.38000 +v 2.24054 6.20440 -0.38000 +v 2.23068 6.23400 -0.38000 +v 1.82524 6.23400 -0.38000 +v 3.17100 6.35751 0.38000 +v 3.17100 6.28751 0.38000 +v 3.11327 6.22978 0.38000 +v 3.20100 6.13230 0.38000 +v 3.20100 5.80660 0.38000 +v 3.14982 5.77589 0.38000 +v 3.13204 5.73145 0.38000 +v 3.22984 5.63365 0.38000 +v 3.27984 5.54365 0.38000 +v 3.32500 5.49849 0.38000 +v 3.36005 5.53353 0.38000 +v 3.54807 5.91947 0.38000 +v 3.28027 6.30629 0.38000 +v 3.20474 6.39126 0.38000 +v 3.17100 6.35751 0.39000 +v 3.17100 6.28751 0.39000 +v 3.11327 6.22978 0.39000 +v 3.20100 6.13230 0.39000 +v 3.20100 5.80660 0.39000 +v 3.14982 5.77589 0.39000 +v 3.13204 5.73145 0.39000 +v 3.22984 5.63365 0.39000 +v 3.27984 5.54365 0.39000 +v 3.32500 5.49849 0.39000 +v 3.36005 5.53353 0.39000 +v 3.54807 5.91947 0.39000 +v 3.28027 6.30629 0.39000 +v 3.20474 6.39126 0.39000 +v 3.20100 6.34509 0.42000 +v 3.20100 6.27509 0.42000 +v 3.15461 6.22870 0.42000 +v 3.23100 6.14381 0.42000 +v 3.23100 5.78962 0.42000 +v 3.17392 5.75537 0.42000 +v 3.16724 5.73867 0.42000 +v 3.25402 5.65189 0.42000 +v 3.30402 5.56189 0.42000 +v 3.32500 5.54091 0.42000 +v 3.33528 5.55119 0.42000 +v 3.51341 5.91683 0.42000 +v 3.25664 6.28771 0.42000 +v 3.20346 6.34755 0.42000 +v 3.20100 6.34509 -0.42000 +v 3.20100 6.27509 -0.42000 +v 3.15461 6.22870 -0.42000 +v 3.23100 6.14381 -0.42000 +v 3.23100 5.78962 -0.42000 +v 3.17392 5.75537 -0.42000 +v 3.16724 5.73867 -0.42000 +v 3.25402 5.65189 -0.42000 +v 3.30402 5.56189 -0.42000 +v 3.32500 5.54091 -0.42000 +v 3.33528 5.55119 -0.42000 +v 3.51341 5.91683 -0.42000 +v 3.25664 6.28771 -0.42000 +v 3.20346 6.34755 -0.42000 +v 3.17100 6.35751 -0.39000 +v 3.17100 6.28751 -0.39000 +v 3.11327 6.22978 -0.39000 +v 3.20100 6.13230 -0.39000 +v 3.20100 5.80660 -0.39000 +v 3.14982 5.77589 -0.39000 +v 3.13204 5.73145 -0.39000 +v 3.22984 5.63365 -0.39000 +v 3.27984 5.54365 -0.39000 +v 3.32500 5.49849 -0.39000 +v 3.36005 5.53353 -0.39000 +v 3.54807 5.91947 -0.39000 +v 3.28027 6.30629 -0.39000 +v 3.20474 6.39126 -0.39000 +v 3.17100 6.35751 -0.38000 +v 3.17100 6.28751 -0.38000 +v 3.11327 6.22978 -0.38000 +v 3.20100 6.13230 -0.38000 +v 3.20100 5.80660 -0.38000 +v 3.14982 5.77589 -0.38000 +v 3.13204 5.73145 -0.38000 +v 3.22984 5.63365 -0.38000 +v 3.27984 5.54365 -0.38000 +v 3.32500 5.49849 -0.38000 +v 3.36005 5.53353 -0.38000 +v 3.54807 5.91947 -0.38000 +v 3.28027 6.30629 -0.38000 +v 3.20474 6.39126 -0.38000 +v 2.38749 6.23400 0.38000 +v 2.25100 6.23400 0.38000 +v 2.25100 6.22546 0.38000 +v 2.45469 6.20606 0.38000 +v 2.50204 6.21553 0.38000 +v 2.51651 6.23000 0.38000 +v 2.49251 6.25400 0.38000 +v 2.40749 6.25400 0.38000 +v 2.38749 6.23400 0.41625 +v 2.25100 6.23400 0.41625 +v 2.25100 6.22546 0.41625 +v 2.45469 6.20606 0.41625 +v 2.50204 6.21553 0.41625 +v 2.51651 6.23000 0.41625 +v 2.49251 6.25400 0.41625 +v 2.40749 6.25400 0.41625 +v 2.38904 6.23025 0.42000 +v 2.25475 6.23025 0.42000 +v 2.25475 6.22887 0.42000 +v 2.45450 6.20984 0.42000 +v 2.50019 6.21898 0.42000 +v 2.51121 6.23000 0.42000 +v 2.49096 6.25025 0.42000 +v 2.40904 6.25025 0.42000 +v 2.38904 6.23025 -0.42000 +v 2.25475 6.23025 -0.42000 +v 2.25475 6.22887 -0.42000 +v 2.45450 6.20984 -0.42000 +v 2.50019 6.21898 -0.42000 +v 2.51121 6.23000 -0.42000 +v 2.49096 6.25025 -0.42000 +v 2.40904 6.25025 -0.42000 +v 2.38749 6.23400 -0.41625 +v 2.25100 6.23400 -0.41625 +v 2.25100 6.22546 -0.41625 +v 2.45469 6.20606 -0.41625 +v 2.50204 6.21553 -0.41625 +v 2.51651 6.23000 -0.41625 +v 2.49251 6.25400 -0.41625 +v 2.40749 6.25400 -0.41625 +v 2.38749 6.23400 -0.38000 +v 2.25100 6.23400 -0.38000 +v 2.25100 6.22546 -0.38000 +v 2.45469 6.20606 -0.38000 +v 2.50204 6.21553 -0.38000 +v 2.51651 6.23000 -0.38000 +v 2.49251 6.25400 -0.38000 +v 2.40749 6.25400 -0.38000 +v 1.44345 5.02004 0.38000 +v 1.47695 4.98653 0.38000 +v 2.47900 5.19488 0.38000 +v 2.47900 5.22751 0.38000 +v 2.45100 5.25552 0.38000 +v 2.01549 5.60194 0.38000 +v 1.44345 5.02004 0.40500 +v 1.47695 4.98653 0.40500 +v 2.47900 5.19488 0.40500 +v 2.47900 5.22751 0.40500 +v 2.45100 5.25552 0.40500 +v 2.01549 5.60194 0.40500 +v 1.46457 5.02013 0.42000 +v 1.48183 5.00287 0.42000 +v 2.46400 5.20708 0.42000 +v 2.46400 5.22130 0.42000 +v 2.44099 5.24431 0.42000 +v 2.01672 5.58180 0.42000 +v 1.46457 5.02013 -0.42000 +v 1.48183 5.00287 -0.42000 +v 2.46400 5.20708 -0.42000 +v 2.46400 5.22130 -0.42000 +v 2.44099 5.24431 -0.42000 +v 2.01672 5.58180 -0.42000 +v 1.44345 5.02004 -0.40500 +v 1.47695 4.98653 -0.40500 +v 2.47900 5.19488 -0.40500 +v 2.47900 5.22751 -0.40500 +v 2.45100 5.25552 -0.40500 +v 2.01549 5.60194 -0.40500 +v 1.44345 5.02004 -0.38000 +v 1.47695 4.98653 -0.38000 +v 2.47900 5.19488 -0.38000 +v 2.47900 5.22751 -0.38000 +v 2.45100 5.25552 -0.38000 +v 2.01549 5.60194 -0.38000 +v 2.03394 5.62046 0.38000 +v 2.45903 5.27446 0.38000 +v 2.50100 5.23249 0.38000 +v 2.50100 5.19286 0.38000 +v 3.02458 4.76807 0.38000 +v 3.06007 4.80355 0.38000 +v 3.31770 5.31882 0.38000 +v 3.25355 5.38296 0.38000 +v 3.20549 5.36373 0.38000 +v 2.76198 5.46453 0.38000 +v 2.65251 5.57400 0.38000 +v 2.23366 5.57400 0.38000 +v 2.06626 5.65278 0.38000 +v 2.03394 5.62046 0.40500 +v 2.45903 5.27446 0.40500 +v 2.50100 5.23249 0.40500 +v 2.50100 5.19286 0.40500 +v 3.02458 4.76807 0.40500 +v 3.06007 4.80355 0.40500 +v 3.31770 5.31882 0.40500 +v 3.25355 5.38296 0.40500 +v 3.20549 5.36373 0.40500 +v 2.76198 5.46453 0.40500 +v 2.65251 5.57400 0.40500 +v 2.23366 5.57400 0.40500 +v 2.06626 5.65278 0.40500 +v 2.05630 5.62160 0.42000 +v 2.46909 5.28561 0.42000 +v 2.51600 5.23870 0.42000 +v 2.51600 5.20000 0.42000 +v 3.02353 4.78823 0.42000 +v 3.04774 4.81244 0.42000 +v 3.29945 5.31585 0.42000 +v 3.24994 5.36536 0.42000 +v 3.20673 5.34807 0.42000 +v 2.75444 5.45087 0.42000 +v 2.64630 5.55900 0.42000 +v 2.23031 5.55900 0.42000 +v 2.06941 5.63471 0.42000 +v 2.05630 5.62160 -0.42000 +v 2.46909 5.28561 -0.42000 +v 2.51600 5.23870 -0.42000 +v 2.51600 5.20000 -0.42000 +v 3.02353 4.78823 -0.42000 +v 3.04774 4.81244 -0.42000 +v 3.29945 5.31585 -0.42000 +v 3.24994 5.36536 -0.42000 +v 3.20673 5.34807 -0.42000 +v 2.75444 5.45087 -0.42000 +v 2.64630 5.55900 -0.42000 +v 2.23031 5.55900 -0.42000 +v 2.06941 5.63471 -0.42000 +v 2.03394 5.62046 -0.40500 +v 2.45903 5.27446 -0.40500 +v 2.50100 5.23249 -0.40500 +v 2.50100 5.19286 -0.40500 +v 3.02458 4.76807 -0.40500 +v 3.06007 4.80355 -0.40500 +v 3.31770 5.31882 -0.40500 +v 3.25355 5.38296 -0.40500 +v 3.20549 5.36373 -0.40500 +v 2.76198 5.46453 -0.40500 +v 2.65251 5.57400 -0.40500 +v 2.23366 5.57400 -0.40500 +v 2.06626 5.65278 -0.40500 +v 2.03394 5.62046 -0.38000 +v 2.45903 5.27446 -0.38000 +v 2.50100 5.23249 -0.38000 +v 2.50100 5.19286 -0.38000 +v 3.02458 4.76807 -0.38000 +v 3.06007 4.80355 -0.38000 +v 3.31770 5.31882 -0.38000 +v 3.25355 5.38296 -0.38000 +v 3.20549 5.36373 -0.38000 +v 2.76198 5.46453 -0.38000 +v 2.65251 5.57400 -0.38000 +v 2.23366 5.57400 -0.38000 +v 2.06626 5.65278 -0.38000 +v 0.14500 5.65000 -0.32000 +v 0.01500 4.36000 -0.32000 +v 0.06500 4.32000 -0.32000 +v 0.29500 4.59000 -0.32000 +v 0.40500 4.48000 -0.32000 +v 0.64500 3.95000 -0.32000 +v 1.91500 2.24000 -0.32000 +v 1.95500 2.28000 -0.32000 +v 1.97500 2.86000 -0.32000 +v 2.08500 2.97000 -0.32000 +v 2.34500 2.63000 -0.32000 +v 2.64500 3.15000 -0.32000 +v 2.81500 3.35000 -0.32000 +v 1.42500 5.00000 -0.32000 +v 2.05500 5.65000 -0.32000 +v 1.42500 6.09000 -0.32000 +v 1.29500 6.22000 -0.32000 +v 0.09427 5.68512 0.00000 +v -0.04167 4.33619 0.00000 +v 0.07215 4.24512 0.00000 +v 0.29818 4.51046 0.00000 +v 0.35986 4.44877 0.00000 +v 0.59820 3.92245 0.00000 +v 1.90895 2.15758 0.00000 +v 2.00823 2.25687 0.00000 +v 2.02823 2.83687 0.00000 +v 2.07957 2.88820 0.00000 +v 2.35130 2.53286 0.00000 +v 2.68932 3.11877 0.00000 +v 2.88574 3.34984 0.00000 +v 1.49774 4.99746 0.00000 +v 2.13791 5.65796 0.00000 +v 1.45982 6.13155 0.00000 +v 1.30576 6.28560 0.00000 +v 0.14500 5.65000 0.32000 +v 0.01500 4.36000 0.32000 +v 0.06500 4.32000 0.32000 +v 0.29500 4.59000 0.32000 +v 0.40500 4.48000 0.32000 +v 0.64500 3.95000 0.32000 +v 1.91500 2.24000 0.32000 +v 1.95500 2.28000 0.32000 +v 1.97500 2.86000 0.32000 +v 2.08500 2.97000 0.32000 +v 2.34500 2.63000 0.32000 +v 2.64500 3.15000 0.32000 +v 2.81500 3.35000 0.32000 +v 1.42500 5.00000 0.32000 +v 2.05500 5.65000 0.32000 +v 1.42500 6.09000 0.32000 +v 1.29500 6.22000 0.32000 +v 0.28763 5.73461 0.32000 +v 0.18855 5.68507 0.32000 +v 0.15078 5.64730 0.32000 +v 0.04091 4.41877 0.32000 +v 0.02186 4.36162 0.32000 +v 0.05537 4.32812 0.32000 +v 0.11038 4.37396 0.32000 +v 0.17024 4.47372 0.32000 +v 0.29500 4.59849 0.32000 +v 0.30100 4.59249 0.32000 +v 0.31929 4.70226 0.32000 +v 0.62047 5.15402 0.32000 +v 0.69054 5.21408 0.32000 +v 0.74005 5.28339 0.32000 +v 1.29032 6.12380 0.32000 +v 1.33651 6.17000 0.32000 +v 1.29375 6.21276 0.32000 +v 0.28763 5.73461 0.35250 +v 0.18855 5.68507 0.35250 +v 0.15078 5.64730 0.35250 +v 0.04091 4.41877 0.35250 +v 0.02186 4.36162 0.35250 +v 0.05537 4.32812 0.35250 +v 0.11038 4.37396 0.35250 +v 0.17024 4.47372 0.35250 +v 0.29500 4.59849 0.35250 +v 0.30100 4.59249 0.35250 +v 0.31929 4.70226 0.35250 +v 0.62047 5.15402 0.35250 +v 0.69054 5.21408 0.35250 +v 0.74005 5.28339 0.35250 +v 1.29032 6.12380 0.35250 +v 1.33651 6.17000 0.35250 +v 1.29375 6.21276 0.35250 +v 0.29092 5.72786 0.36000 +v 0.19300 5.67890 0.36000 +v 0.15801 5.64392 0.36000 +v 0.04831 4.41723 0.36000 +v 0.03045 4.36365 0.36000 +v 0.05583 4.33826 0.36000 +v 0.10460 4.37891 0.36000 +v 0.16428 4.47837 0.36000 +v 0.29500 4.60909 0.36000 +v 0.29600 4.60810 0.36000 +v 0.31216 4.70508 0.36000 +v 0.61480 5.15904 0.36000 +v 0.68497 5.21918 0.36000 +v 0.73386 5.28762 0.36000 +v 1.28447 6.12856 0.36000 +v 1.32591 6.17000 0.36000 +v 1.29219 6.20372 0.36000 +v 0.29092 5.72786 -0.36000 +v 0.19300 5.67890 -0.36000 +v 0.15801 5.64392 -0.36000 +v 0.04831 4.41723 -0.36000 +v 0.03045 4.36365 -0.36000 +v 0.05583 4.33826 -0.36000 +v 0.10460 4.37891 -0.36000 +v 0.16428 4.47837 -0.36000 +v 0.29500 4.60909 -0.36000 +v 0.29600 4.60810 -0.36000 +v 0.31216 4.70508 -0.36000 +v 0.61480 5.15904 -0.36000 +v 0.68497 5.21918 -0.36000 +v 0.73386 5.28762 -0.36000 +v 1.28447 6.12856 -0.36000 +v 1.32591 6.17000 -0.36000 +v 1.29219 6.20372 -0.36000 +v 0.28763 5.73461 -0.35250 +v 0.18855 5.68507 -0.35250 +v 0.15078 5.64730 -0.35250 +v 0.04091 4.41877 -0.35250 +v 0.02186 4.36162 -0.35250 +v 0.05537 4.32812 -0.35250 +v 0.11038 4.37396 -0.35250 +v 0.17024 4.47372 -0.35250 +v 0.29500 4.59849 -0.35250 +v 0.30100 4.59249 -0.35250 +v 0.31929 4.70226 -0.35250 +v 0.62047 5.15402 -0.35250 +v 0.69054 5.21408 -0.35250 +v 0.74005 5.28339 -0.35250 +v 1.29032 6.12380 -0.35250 +v 1.33651 6.17000 -0.35250 +v 1.29375 6.21276 -0.35250 +v 0.28763 5.73461 -0.32000 +v 0.18855 5.68507 -0.32000 +v 0.15078 5.64730 -0.32000 +v 0.04091 4.41877 -0.32000 +v 0.02186 4.36162 -0.32000 +v 0.05537 4.32812 -0.32000 +v 0.11038 4.37396 -0.32000 +v 0.17024 4.47372 -0.32000 +v 0.29500 4.59849 -0.32000 +v 0.30100 4.59249 -0.32000 +v 0.31929 4.70226 -0.32000 +v 0.62047 5.15402 -0.32000 +v 0.69054 5.21408 -0.32000 +v 0.74005 5.28339 -0.32000 +v 1.29032 6.12380 -0.32000 +v 1.33651 6.17000 -0.32000 +v 1.29375 6.21276 -0.32000 +v 1.30967 6.11618 0.32000 +v 0.70162 5.19912 0.32000 +v 0.72037 5.14287 0.32000 +v 0.92707 4.87712 0.32000 +v 1.31396 5.01600 0.32000 +v 1.36623 5.01600 0.32000 +v 1.42253 4.99176 0.32000 +v 1.41773 5.00112 0.32000 +v 2.04658 5.64993 0.32000 +v 2.00118 5.69534 0.32000 +v 1.52149 6.01513 0.32000 +v 1.34550 6.15201 0.32000 +v 1.30967 6.11618 0.35625 +v 0.70162 5.19912 0.35625 +v 0.72037 5.14287 0.35625 +v 0.92707 4.87712 0.35625 +v 1.31396 5.01600 0.35625 +v 1.36623 5.01600 0.35625 +v 1.42253 4.99176 0.35625 +v 1.41773 5.00112 0.35625 +v 2.04658 5.64993 0.35625 +v 2.00118 5.69534 0.35625 +v 1.52149 6.01513 0.35625 +v 1.34550 6.15201 0.35625 +v 1.31259 6.11380 0.36000 +v 0.70575 5.19857 0.36000 +v 0.72372 5.14467 0.36000 +v 0.92836 4.88156 0.36000 +v 1.31330 5.01975 0.36000 +v 1.36700 5.01975 0.36000 +v 1.41470 4.99908 0.36000 +v 1.41317 5.00180 0.36000 +v 2.04132 5.64989 0.36000 +v 1.99879 5.69242 0.36000 +v 1.51930 6.01209 0.36000 +v 1.34581 6.14702 0.36000 +v 1.31259 6.11380 -0.36000 +v 0.70575 5.19857 -0.36000 +v 0.72372 5.14467 -0.36000 +v 0.92836 4.88156 -0.36000 +v 1.31330 5.01975 -0.36000 +v 1.36700 5.01975 -0.36000 +v 1.41470 4.99908 -0.36000 +v 1.41317 5.00180 -0.36000 +v 2.04132 5.64989 -0.36000 +v 1.99879 5.69242 -0.36000 +v 1.51930 6.01209 -0.36000 +v 1.34581 6.14702 -0.36000 +v 1.30967 6.11618 -0.35625 +v 0.70162 5.19912 -0.35625 +v 0.72037 5.14287 -0.35625 +v 0.92707 4.87712 -0.35625 +v 1.31396 5.01600 -0.35625 +v 1.36623 5.01600 -0.35625 +v 1.42253 4.99176 -0.35625 +v 1.41773 5.00112 -0.35625 +v 2.04658 5.64993 -0.35625 +v 2.00118 5.69534 -0.35625 +v 1.52149 6.01513 -0.35625 +v 1.34550 6.15201 -0.35625 +v 1.30967 6.11618 -0.32000 +v 0.70162 5.19912 -0.32000 +v 0.72037 5.14287 -0.32000 +v 0.92707 4.87712 -0.32000 +v 1.31396 5.01600 -0.32000 +v 1.36623 5.01600 -0.32000 +v 1.42253 4.99176 -0.32000 +v 1.41773 5.00112 -0.32000 +v 2.04658 5.64993 -0.32000 +v 2.00118 5.69534 -0.32000 +v 1.52149 6.01513 -0.32000 +v 1.34550 6.15201 -0.32000 +v 0.95766 4.87456 0.32000 +v 0.93100 4.85679 0.32000 +v 0.93100 4.84188 0.32000 +v 1.56943 3.92414 0.32000 +v 1.61710 3.88600 0.32000 +v 1.64032 3.88600 0.32000 +v 1.64881 3.91999 0.32000 +v 1.40900 4.86925 0.32000 +v 1.40900 4.96660 0.32000 +v 1.36334 4.99400 0.32000 +v 1.31597 4.99400 0.32000 +v 0.95766 4.87456 0.34500 +v 0.93100 4.85679 0.34500 +v 0.93100 4.84188 0.34500 +v 1.56943 3.92414 0.34500 +v 1.61710 3.88600 0.34500 +v 1.64032 3.88600 0.34500 +v 1.64881 3.91999 0.34500 +v 1.40900 4.86925 0.34500 +v 1.40900 4.96660 0.34500 +v 1.36334 4.99400 0.34500 +v 1.31597 4.99400 0.34500 +v 0.96431 4.86097 0.36000 +v 0.94600 4.84876 0.36000 +v 0.94600 4.84659 0.36000 +v 1.58050 3.93449 0.36000 +v 1.62237 3.90100 0.36000 +v 1.62860 3.90100 0.36000 +v 1.63335 3.91997 0.36000 +v 1.39400 4.86739 0.36000 +v 1.39400 4.95811 0.36000 +v 1.35918 4.97900 0.36000 +v 1.31841 4.97900 0.36000 +v 0.96431 4.86097 -0.36000 +v 0.94600 4.84876 -0.36000 +v 0.94600 4.84659 -0.36000 +v 1.58050 3.93449 -0.36000 +v 1.62237 3.90100 -0.36000 +v 1.62860 3.90100 -0.36000 +v 1.63335 3.91997 -0.36000 +v 1.39400 4.86739 -0.36000 +v 1.39400 4.95811 -0.36000 +v 1.35918 4.97900 -0.36000 +v 1.31841 4.97900 -0.36000 +v 0.95766 4.87456 -0.34500 +v 0.93100 4.85679 -0.34500 +v 0.93100 4.84188 -0.34500 +v 1.56943 3.92414 -0.34500 +v 1.61710 3.88600 -0.34500 +v 1.64032 3.88600 -0.34500 +v 1.64881 3.91999 -0.34500 +v 1.40900 4.86925 -0.34500 +v 1.40900 4.96660 -0.34500 +v 1.36334 4.99400 -0.34500 +v 1.31597 4.99400 -0.34500 +v 0.95766 4.87456 -0.32000 +v 0.93100 4.85679 -0.32000 +v 0.93100 4.84188 -0.32000 +v 1.56943 3.92414 -0.32000 +v 1.61710 3.88600 -0.32000 +v 1.64032 3.88600 -0.32000 +v 1.64881 3.91999 -0.32000 +v 1.40900 4.86925 -0.32000 +v 1.40900 4.96660 -0.32000 +v 1.36334 4.99400 -0.32000 +v 1.31597 4.99400 -0.32000 +v 0.63948 5.14590 0.32000 +v 0.34073 4.69778 0.32000 +v 0.32139 4.57209 0.32000 +v 0.41002 4.48347 0.32000 +v 0.65021 3.95305 0.32000 +v 1.77974 2.41368 0.32000 +v 1.91543 2.24892 0.32000 +v 1.94905 2.28253 0.32000 +v 1.95901 2.81065 0.32000 +v 1.96947 2.86296 0.32000 +v 2.03841 2.93189 0.32000 +v 1.94939 3.31763 0.32000 +v 1.74969 3.62716 0.32000 +v 1.65045 3.85542 0.32000 +v 1.58135 3.88504 0.32000 +v 1.52026 3.95631 0.32000 +v 0.92951 4.79738 0.32000 +v 0.90955 4.86723 0.32000 +v 0.69974 5.13699 0.32000 +v 0.68243 5.18026 0.32000 +v 0.63948 5.14590 0.33000 +v 0.34073 4.69778 0.33000 +v 0.32139 4.57209 0.33000 +v 0.41002 4.48347 0.33000 +v 0.65021 3.95305 0.33000 +v 1.77974 2.41368 0.33000 +v 1.91543 2.24892 0.33000 +v 1.94905 2.28253 0.33000 +v 1.95901 2.81065 0.33000 +v 1.96947 2.86296 0.33000 +v 2.03841 2.93189 0.33000 +v 1.94939 3.31763 0.33000 +v 1.74969 3.62716 0.33000 +v 1.65045 3.85542 0.33000 +v 1.58135 3.88504 0.33000 +v 1.52026 3.95631 0.33000 +v 0.92951 4.79738 0.33000 +v 0.90955 4.86723 0.33000 +v 0.69974 5.13699 0.33000 +v 0.68243 5.18026 0.33000 +v 0.66185 5.12538 0.36000 +v 0.36937 4.68666 0.36000 +v 0.35336 4.58256 0.36000 +v 0.43509 4.50082 0.36000 +v 0.67625 3.96827 0.36000 +v 1.80343 2.43211 0.36000 +v 1.91758 2.29349 0.36000 +v 1.91928 2.29519 0.36000 +v 1.92907 2.81390 0.36000 +v 1.94184 2.87775 0.36000 +v 2.00544 2.94135 0.36000 +v 1.92134 3.30577 0.36000 +v 1.72316 3.61295 0.36000 +v 1.62768 3.83254 0.36000 +v 1.56312 3.86021 0.36000 +v 1.49654 3.93789 0.36000 +v 0.90205 4.78427 0.36000 +v 0.88230 4.85340 0.36000 +v 0.67346 5.12191 0.36000 +v 0.66960 5.13157 0.36000 +v 0.66185 5.12538 -0.36000 +v 0.36937 4.68666 -0.36000 +v 0.35336 4.58256 -0.36000 +v 0.43509 4.50082 -0.36000 +v 0.67625 3.96827 -0.36000 +v 1.80343 2.43211 -0.36000 +v 1.91758 2.29349 -0.36000 +v 1.91928 2.29519 -0.36000 +v 1.92907 2.81390 -0.36000 +v 1.94184 2.87775 -0.36000 +v 2.00544 2.94135 -0.36000 +v 1.92134 3.30577 -0.36000 +v 1.72316 3.61295 -0.36000 +v 1.62768 3.83254 -0.36000 +v 1.56312 3.86021 -0.36000 +v 1.49654 3.93789 -0.36000 +v 0.90205 4.78427 -0.36000 +v 0.88230 4.85340 -0.36000 +v 0.67346 5.12191 -0.36000 +v 0.66960 5.13157 -0.36000 +v 0.63948 5.14590 -0.33000 +v 0.34073 4.69778 -0.33000 +v 0.32139 4.57209 -0.33000 +v 0.41002 4.48347 -0.33000 +v 0.65021 3.95305 -0.33000 +v 1.77974 2.41368 -0.33000 +v 1.91543 2.24892 -0.33000 +v 1.94905 2.28253 -0.33000 +v 1.95901 2.81065 -0.33000 +v 1.96947 2.86296 -0.33000 +v 2.03841 2.93189 -0.33000 +v 1.94939 3.31763 -0.33000 +v 1.74969 3.62716 -0.33000 +v 1.65045 3.85542 -0.33000 +v 1.58135 3.88504 -0.33000 +v 1.52026 3.95631 -0.33000 +v 0.92951 4.79738 -0.33000 +v 0.90955 4.86723 -0.33000 +v 0.69974 5.13699 -0.33000 +v 0.68243 5.18026 -0.33000 +v 0.63948 5.14590 -0.32000 +v 0.34073 4.69778 -0.32000 +v 0.32139 4.57209 -0.32000 +v 0.41002 4.48347 -0.32000 +v 0.65021 3.95305 -0.32000 +v 1.77974 2.41368 -0.32000 +v 1.91543 2.24892 -0.32000 +v 1.94905 2.28253 -0.32000 +v 1.95901 2.81065 -0.32000 +v 1.96947 2.86296 -0.32000 +v 2.03841 2.93189 -0.32000 +v 1.94939 3.31763 -0.32000 +v 1.74969 3.62716 -0.32000 +v 1.65045 3.85542 -0.32000 +v 1.58135 3.88504 -0.32000 +v 1.52026 3.95631 -0.32000 +v 0.92951 4.79738 -0.32000 +v 0.90955 4.86723 -0.32000 +v 0.69974 5.13699 -0.32000 +v 0.68243 5.18026 -0.32000 +v 1.43100 4.96629 0.32000 +v 1.43100 4.87075 0.32000 +v 1.67115 3.92015 0.32000 +v 1.66127 3.87073 0.32000 +v 1.77028 3.63289 0.32000 +v 1.97060 3.32239 0.32000 +v 2.05836 2.95185 0.32000 +v 2.08500 2.97849 0.32000 +v 2.19987 2.86361 0.32000 +v 2.27997 2.71343 0.32000 +v 2.34529 2.63878 0.32000 +v 2.39012 2.68361 0.32000 +v 2.64008 3.15352 0.32000 +v 2.79002 3.31347 0.32000 +v 2.80788 3.34918 0.32000 +v 1.53056 4.88595 0.32000 +v 1.44382 4.97270 0.32000 +v 1.43100 4.96629 0.35250 +v 1.43100 4.87075 0.35250 +v 1.67115 3.92015 0.35250 +v 1.66127 3.87073 0.35250 +v 1.77028 3.63289 0.35250 +v 1.97060 3.32239 0.35250 +v 2.05836 2.95185 0.35250 +v 2.08500 2.97849 0.35250 +v 2.19987 2.86361 0.35250 +v 2.27997 2.71343 0.35250 +v 2.34529 2.63878 0.35250 +v 2.39012 2.68361 0.35250 +v 2.64008 3.15352 0.35250 +v 2.79002 3.31347 0.35250 +v 2.80788 3.34918 0.35250 +v 1.53056 4.88595 0.35250 +v 1.44382 4.97270 0.35250 +v 1.43850 4.96166 0.36000 +v 1.43850 4.87168 0.36000 +v 1.67884 3.92035 0.36000 +v 1.66910 3.87165 0.36000 +v 1.77687 3.63651 0.36000 +v 1.97760 3.32537 0.36000 +v 2.06256 2.96665 0.36000 +v 2.08500 2.98909 0.36000 +v 2.20597 2.86813 0.36000 +v 2.28618 2.71772 0.36000 +v 2.34566 2.64975 0.36000 +v 2.38403 2.68812 0.36000 +v 2.63392 3.15792 0.36000 +v 2.78381 3.31780 0.36000 +v 2.79898 3.34815 0.36000 +v 1.52501 4.88089 0.36000 +v 1.44233 4.96357 0.36000 +v 1.43850 4.96166 -0.36000 +v 1.43850 4.87168 -0.36000 +v 1.67884 3.92035 -0.36000 +v 1.66910 3.87165 -0.36000 +v 1.77687 3.63651 -0.36000 +v 1.97760 3.32537 -0.36000 +v 2.06256 2.96665 -0.36000 +v 2.08500 2.98909 -0.36000 +v 2.20597 2.86813 -0.36000 +v 2.28618 2.71772 -0.36000 +v 2.34566 2.64975 -0.36000 +v 2.38403 2.68812 -0.36000 +v 2.63392 3.15792 -0.36000 +v 2.78381 3.31780 -0.36000 +v 2.79898 3.34815 -0.36000 +v 1.52501 4.88089 -0.36000 +v 1.44233 4.96357 -0.36000 +v 1.43100 4.96629 -0.35250 +v 1.43100 4.87075 -0.35250 +v 1.67115 3.92015 -0.35250 +v 1.66127 3.87073 -0.35250 +v 1.77028 3.63289 -0.35250 +v 1.97060 3.32239 -0.35250 +v 2.05836 2.95185 -0.35250 +v 2.08500 2.97849 -0.35250 +v 2.19987 2.86361 -0.35250 +v 2.27997 2.71343 -0.35250 +v 2.34529 2.63878 -0.35250 +v 2.39012 2.68361 -0.35250 +v 2.64008 3.15352 -0.35250 +v 2.79002 3.31347 -0.35250 +v 2.80788 3.34918 -0.35250 +v 1.53056 4.88595 -0.35250 +v 1.44382 4.97270 -0.35250 +v 1.43100 4.96629 -0.32000 +v 1.43100 4.87075 -0.32000 +v 1.67115 3.92015 -0.32000 +v 1.66127 3.87073 -0.32000 +v 1.77028 3.63289 -0.32000 +v 1.97060 3.32239 -0.32000 +v 2.05836 2.95185 -0.32000 +v 2.08500 2.97849 -0.32000 +v 2.19987 2.86361 -0.32000 +v 2.27997 2.71343 -0.32000 +v 2.34529 2.63878 -0.32000 +v 2.39012 2.68361 -0.32000 +v 2.64008 3.15352 -0.32000 +v 2.79002 3.31347 -0.32000 +v 2.80788 3.34918 -0.32000 +v 1.53056 4.88595 -0.32000 +v 1.44382 4.97270 -0.32000 +v 0.53500 3.13000 -0.51000 +v 0.42500 3.02000 -0.51000 +v 0.36500 3.08000 -0.51000 +v -1.53500 3.25000 -0.51000 +v -1.57500 3.21000 -0.51000 +v -1.03500 2.15000 -0.51000 +v -0.89500 2.01000 -0.51000 +v 0.80500 0.75000 -0.51000 +v 0.86500 0.81000 -0.51000 +v 0.91500 0.76000 -0.51000 +v 2.56500 0.66000 -0.51000 +v 2.60500 0.70000 -0.51000 +v 2.45500 0.88000 -0.51000 +v 2.96500 2.32000 -0.51000 +v 2.77500 3.36000 -0.51000 +v 2.73500 3.40000 -0.51000 +v 2.68500 3.35000 -0.51000 +v 2.47500 2.95000 -0.51000 +v 2.33500 2.80000 -0.51000 +v 2.22500 2.91000 -0.51000 +v 2.01500 3.26000 -0.51000 +v 1.96500 3.31000 -0.51000 +v 1.92500 3.27000 -0.51000 +v 1.93500 2.51000 -0.51000 +v 1.82500 2.40000 -0.51000 +v 0.81500 3.74000 -0.51000 +v 0.46620 3.17787 0.00000 +v 0.42500 3.13667 0.00000 +v 0.40217 3.15950 0.00000 +v -1.56606 3.33561 0.00000 +v -1.67572 3.22596 0.00000 +v -1.10258 2.10091 0.00000 +v -0.94902 1.94735 0.00000 +v 0.81303 0.64136 0.00000 +v 0.86500 0.69333 0.00000 +v 0.87878 0.67954 0.00000 +v 2.59708 0.57540 0.00000 +v 2.71661 0.69494 0.00000 +v 2.54845 0.89673 0.00000 +v 3.05011 2.31319 0.00000 +v 2.85153 3.40014 0.00000 +v 2.73500 3.51667 0.00000 +v 2.61779 3.39946 0.00000 +v 2.40711 2.99818 0.00000 +v 2.33295 2.91872 0.00000 +v 2.29052 2.96115 0.00000 +v 2.08052 3.31115 0.00000 +v 1.96500 3.42667 0.00000 +v 1.84205 3.30372 0.00000 +v 1.85205 2.54372 0.00000 +v 1.83376 2.52544 0.00000 +v 0.79768 3.90004 0.00000 +v 0.53500 3.13000 0.51000 +v 0.42500 3.02000 0.51000 +v 0.36500 3.08000 0.51000 +v -1.53500 3.25000 0.51000 +v -1.57500 3.21000 0.51000 +v -1.03500 2.15000 0.51000 +v -0.89500 2.01000 0.51000 +v 0.80500 0.75000 0.51000 +v 0.86500 0.81000 0.51000 +v 0.91500 0.76000 0.51000 +v 2.56500 0.66000 0.51000 +v 2.60500 0.70000 0.51000 +v 2.45500 0.88000 0.51000 +v 2.96500 2.32000 0.51000 +v 2.77500 3.36000 0.51000 +v 2.73500 3.40000 0.51000 +v 2.68500 3.35000 0.51000 +v 2.47500 2.95000 0.51000 +v 2.33500 2.80000 0.51000 +v 2.22500 2.91000 0.51000 +v 2.01500 3.26000 0.51000 +v 1.96500 3.31000 0.51000 +v 1.92500 3.27000 0.51000 +v 1.93500 2.51000 0.51000 +v 1.82500 2.40000 0.51000 +v 0.81500 3.74000 0.51000 +v 0.75030 3.63710 0.51000 +v 0.54017 3.12680 0.51000 +v 0.47509 3.05242 0.51000 +v 0.51837 3.03511 0.51000 +v 0.59938 2.95411 0.51000 +v 0.89031 2.62305 0.51000 +v 0.90932 2.56600 0.51000 +v 0.94749 2.56600 0.51000 +v 1.02938 2.48410 0.51000 +v 1.81923 1.58428 0.51000 +v 1.88722 1.52600 0.51000 +v 1.91941 1.52600 0.51000 +v 1.93900 1.80021 0.51000 +v 1.93900 2.19903 0.51000 +v 1.86238 2.42889 0.51000 +v 1.82500 2.39151 0.51000 +v 1.70041 2.51611 0.51000 +v 0.92026 3.60631 0.51000 +v 0.81545 3.73018 0.51000 +v 0.75030 3.63710 0.53500 +v 0.54017 3.12680 0.53500 +v 0.47509 3.05242 0.53500 +v 0.51837 3.03511 0.53500 +v 0.59938 2.95411 0.53500 +v 0.89031 2.62305 0.53500 +v 0.90932 2.56600 0.53500 +v 0.94749 2.56600 0.53500 +v 1.02938 2.48410 0.53500 +v 1.81923 1.58428 0.53500 +v 1.88722 1.52600 0.53500 +v 1.91941 1.52600 0.53500 +v 1.93900 1.80021 0.53500 +v 1.93900 2.19903 0.53500 +v 1.86238 2.42889 0.53500 +v 1.82500 2.39151 0.53500 +v 1.70041 2.51611 0.53500 +v 0.92026 3.60631 0.53500 +v 0.81545 3.73018 0.53500 +v 0.76353 3.62986 0.55000 +v 0.55309 3.11879 0.55000 +v 0.50033 3.05849 0.55000 +v 0.52680 3.04790 0.55000 +v 0.61033 2.96437 0.55000 +v 0.90358 2.63067 0.55000 +v 0.92014 2.58100 0.55000 +v 0.95370 2.58100 0.55000 +v 1.04033 2.49437 0.55000 +v 1.82980 1.59497 0.55000 +v 1.89277 1.54100 0.55000 +v 1.90545 1.54100 0.55000 +v 1.92400 1.80075 0.55000 +v 1.92400 2.19659 0.55000 +v 1.85582 2.40112 0.55000 +v 1.82500 2.37030 0.55000 +v 1.68893 2.50637 0.55000 +v 0.90842 3.59709 0.55000 +v 0.81657 3.70563 0.55000 +v 0.76353 3.62986 -0.55000 +v 0.55309 3.11879 -0.55000 +v 0.50033 3.05849 -0.55000 +v 0.52680 3.04790 -0.55000 +v 0.61033 2.96437 -0.55000 +v 0.90358 2.63067 -0.55000 +v 0.92014 2.58100 -0.55000 +v 0.95370 2.58100 -0.55000 +v 1.04033 2.49437 -0.55000 +v 1.82980 1.59497 -0.55000 +v 1.89277 1.54100 -0.55000 +v 1.90545 1.54100 -0.55000 +v 1.92400 1.80075 -0.55000 +v 1.92400 2.19659 -0.55000 +v 1.85582 2.40112 -0.55000 +v 1.82500 2.37030 -0.55000 +v 1.68893 2.50637 -0.55000 +v 0.90842 3.59709 -0.55000 +v 0.81657 3.70563 -0.55000 +v 0.75030 3.63710 -0.53500 +v 0.54017 3.12680 -0.53500 +v 0.47509 3.05242 -0.53500 +v 0.51837 3.03511 -0.53500 +v 0.59938 2.95411 -0.53500 +v 0.89031 2.62305 -0.53500 +v 0.90932 2.56600 -0.53500 +v 0.94749 2.56600 -0.53500 +v 1.02938 2.48410 -0.53500 +v 1.81923 1.58428 -0.53500 +v 1.88722 1.52600 -0.53500 +v 1.91941 1.52600 -0.53500 +v 1.93900 1.80021 -0.53500 +v 1.93900 2.19903 -0.53500 +v 1.86238 2.42889 -0.53500 +v 1.82500 2.39151 -0.53500 +v 1.70041 2.51611 -0.53500 +v 0.92026 3.60631 -0.53500 +v 0.81545 3.73018 -0.53500 +v 0.75030 3.63710 -0.51000 +v 0.54017 3.12680 -0.51000 +v 0.47509 3.05242 -0.51000 +v 0.51837 3.03511 -0.51000 +v 0.59938 2.95411 -0.51000 +v 0.89031 2.62305 -0.51000 +v 0.90932 2.56600 -0.51000 +v 0.94749 2.56600 -0.51000 +v 1.02938 2.48410 -0.51000 +v 1.81923 1.58428 -0.51000 +v 1.88722 1.52600 -0.51000 +v 1.91941 1.52600 -0.51000 +v 1.93900 1.80021 -0.51000 +v 1.93900 2.19903 -0.51000 +v 1.86238 2.42889 -0.51000 +v 1.82500 2.39151 -0.51000 +v 1.70041 2.51611 -0.51000 +v 0.92026 3.60631 -0.51000 +v 0.81545 3.73018 -0.51000 +v -1.56768 3.20884 0.51000 +v -1.03009 2.15357 0.51000 +v -0.96500 2.08849 0.51000 +v -0.91885 2.13463 0.51000 +v -0.05822 2.72507 0.51000 +v 0.35163 2.96498 0.51000 +v 0.43050 3.02086 0.51000 +v 0.42382 3.01270 0.51000 +v 0.36230 3.07422 0.51000 +v -1.53274 3.24377 0.51000 +v -1.56768 3.20884 0.52000 +v -1.03009 2.15357 0.52000 +v -0.96500 2.08849 0.52000 +v -0.91885 2.13463 0.52000 +v -0.05822 2.72507 0.52000 +v 0.35163 2.96498 0.52000 +v 0.43050 3.02086 0.52000 +v 0.42382 3.01270 0.52000 +v 0.36230 3.07422 0.52000 +v -1.53274 3.24377 0.52000 +v -1.53105 3.20304 0.55000 +v -1.00551 2.17142 0.55000 +v -0.96500 2.13091 0.55000 +v -0.93811 2.15781 0.55000 +v -0.07430 2.75042 0.55000 +v 0.33535 2.99021 0.55000 +v 0.49318 3.07932 0.55000 +v 0.42603 2.96805 0.55000 +v 0.34878 3.04531 0.55000 +v -1.52144 3.21264 0.55000 +v -1.53105 3.20304 -0.55000 +v -1.00551 2.17142 -0.55000 +v -0.96500 2.13091 -0.55000 +v -0.93811 2.15781 -0.55000 +v -0.07430 2.75042 -0.55000 +v 0.33535 2.99021 -0.55000 +v 0.49318 3.07932 -0.55000 +v 0.42603 2.96805 -0.55000 +v 0.34878 3.04531 -0.55000 +v -1.52144 3.21264 -0.55000 +v -1.56768 3.20884 -0.52000 +v -1.03009 2.15357 -0.52000 +v -0.96500 2.08849 -0.52000 +v -0.91885 2.13463 -0.52000 +v -0.05822 2.72507 -0.52000 +v 0.35163 2.96498 -0.52000 +v 0.43050 3.02086 -0.52000 +v 0.42382 3.01270 -0.52000 +v 0.36230 3.07422 -0.52000 +v -1.53274 3.24377 -0.52000 +v -1.56768 3.20884 -0.51000 +v -1.03009 2.15357 -0.51000 +v -0.96500 2.08849 -0.51000 +v -0.91885 2.13463 -0.51000 +v -0.05822 2.72507 -0.51000 +v 0.35163 2.96498 -0.51000 +v 0.43050 3.02086 -0.51000 +v 0.42382 3.01270 -0.51000 +v 0.36230 3.07422 -0.51000 +v -1.53274 3.24377 -0.51000 +v 0.41898 2.98537 0.51000 +v 0.03820 2.76492 0.51000 +v -0.91087 2.11555 0.51000 +v -0.94664 2.07084 0.51000 +v -0.77129 1.92472 0.51000 +v 0.80441 0.75790 0.51000 +v 0.84903 0.80252 0.51000 +v 0.86900 2.25022 0.51000 +v 0.88888 2.57827 0.51000 +v 0.82028 2.67627 0.51000 +v 0.58063 2.94588 0.51000 +v 0.51176 3.01476 0.51000 +v 0.45707 3.03298 0.51000 +v 0.41898 2.98537 0.52000 +v 0.03820 2.76492 0.52000 +v -0.91087 2.11555 0.52000 +v -0.94664 2.07084 0.52000 +v -0.77129 1.92472 0.52000 +v 0.80441 0.75790 0.52000 +v 0.84903 0.80252 0.52000 +v 0.86900 2.25022 0.52000 +v 0.88888 2.57827 0.52000 +v 0.82028 2.67627 0.52000 +v 0.58063 2.94588 0.52000 +v 0.51176 3.01476 0.52000 +v 0.45707 3.03298 0.52000 +v 0.43889 2.96223 0.55000 +v 0.05421 2.73952 0.55000 +v -0.89024 2.09332 0.55000 +v -0.90484 2.07507 0.55000 +v -0.75275 1.94832 0.55000 +v 0.80149 0.79740 0.55000 +v 0.81921 0.81512 0.55000 +v 0.83902 2.25134 0.55000 +v 0.85831 2.56964 0.55000 +v 0.79671 2.65764 0.55000 +v 0.55880 2.92529 0.55000 +v 0.49555 2.98853 0.55000 +v 0.46743 2.99791 0.55000 +v 0.43889 2.96223 -0.55000 +v 0.05421 2.73952 -0.55000 +v -0.89024 2.09332 -0.55000 +v -0.90484 2.07507 -0.55000 +v -0.75275 1.94832 -0.55000 +v 0.80149 0.79740 -0.55000 +v 0.81921 0.81512 -0.55000 +v 0.83902 2.25134 -0.55000 +v 0.85831 2.56964 -0.55000 +v 0.79671 2.65764 -0.55000 +v 0.55880 2.92529 -0.55000 +v 0.49555 2.98853 -0.55000 +v 0.46743 2.99791 -0.55000 +v 0.41898 2.98537 -0.52000 +v 0.03820 2.76492 -0.52000 +v -0.91087 2.11555 -0.52000 +v -0.94664 2.07084 -0.52000 +v -0.77129 1.92472 -0.52000 +v 0.80441 0.75790 -0.52000 +v 0.84903 0.80252 -0.52000 +v 0.86900 2.25022 -0.52000 +v 0.88888 2.57827 -0.52000 +v 0.82028 2.67627 -0.52000 +v 0.58063 2.94588 -0.52000 +v 0.51176 3.01476 -0.52000 +v 0.45707 3.03298 -0.52000 +v 0.41898 2.98537 -0.51000 +v 0.03820 2.76492 -0.51000 +v -0.91087 2.11555 -0.51000 +v -0.94664 2.07084 -0.51000 +v -0.77129 1.92472 -0.51000 +v 0.80441 0.75790 -0.51000 +v 0.84903 0.80252 -0.51000 +v 0.86900 2.25022 -0.51000 +v 0.88888 2.57827 -0.51000 +v 0.82028 2.67627 -0.51000 +v 0.58063 2.94588 -0.51000 +v 0.51176 3.01476 -0.51000 +v 0.45707 3.03298 -0.51000 +v 2.68989 3.34640 0.51000 +v 2.47994 2.94650 0.51000 +v 2.35100 2.80764 0.51000 +v 2.35100 2.67170 0.51000 +v 2.69637 2.10924 0.51000 +v 2.87133 2.26476 0.51000 +v 2.95840 2.32281 0.51000 +v 2.77933 3.32761 0.51000 +v 2.73423 3.39075 0.51000 +v 2.68989 3.34640 0.52000 +v 2.47994 2.94650 0.52000 +v 2.35100 2.80764 0.52000 +v 2.35100 2.67170 0.52000 +v 2.69637 2.10924 0.52000 +v 2.87133 2.26476 0.52000 +v 2.95840 2.32281 0.52000 +v 2.77933 3.32761 0.52000 +v 2.73423 3.39075 0.52000 +v 2.71433 3.32842 0.55000 +v 2.50464 2.92902 0.55000 +v 2.38100 2.79586 0.55000 +v 2.38100 2.68017 0.55000 +v 2.70320 2.15545 0.55000 +v 2.85295 2.28857 0.55000 +v 2.92542 2.33688 0.55000 +v 2.75098 3.31569 0.55000 +v 2.73041 3.34449 0.55000 +v 2.71433 3.32842 -0.55000 +v 2.50464 2.92902 -0.55000 +v 2.38100 2.79586 -0.55000 +v 2.38100 2.68017 -0.55000 +v 2.70320 2.15545 -0.55000 +v 2.85295 2.28857 -0.55000 +v 2.92542 2.33688 -0.55000 +v 2.75098 3.31569 -0.55000 +v 2.73041 3.34449 -0.55000 +v 2.68989 3.34640 -0.52000 +v 2.47994 2.94650 -0.52000 +v 2.35100 2.80764 -0.52000 +v 2.35100 2.67170 -0.52000 +v 2.69637 2.10924 -0.52000 +v 2.87133 2.26476 -0.52000 +v 2.95840 2.32281 -0.52000 +v 2.77933 3.32761 -0.52000 +v 2.73423 3.39075 -0.52000 +v 2.68989 3.34640 -0.51000 +v 2.47994 2.94650 -0.51000 +v 2.35100 2.80764 -0.51000 +v 2.35100 2.67170 -0.51000 +v 2.69637 2.10924 -0.51000 +v 2.87133 2.26476 -0.51000 +v 2.95840 2.32281 -0.51000 +v 2.77933 3.32761 -0.51000 +v 2.73423 3.39075 -0.51000 +v 1.93103 3.26755 0.51000 +v 1.94103 2.50755 0.51000 +v 1.88100 2.44751 0.51000 +v 1.88100 2.42106 0.51000 +v 1.96100 2.20106 0.51000 +v 1.96100 1.79979 0.51000 +v 1.94137 1.52502 0.51000 +v 1.99323 1.51638 0.51000 +v 2.67720 2.10123 0.51000 +v 2.32900 2.66830 0.51000 +v 2.32900 2.79751 0.51000 +v 2.22024 2.90628 0.51000 +v 2.01024 3.25628 0.51000 +v 1.96500 3.30151 0.51000 +v 1.93103 3.26755 0.52000 +v 1.94103 2.50755 0.52000 +v 1.88100 2.44751 0.52000 +v 1.88100 2.42106 0.52000 +v 1.96100 2.20106 0.52000 +v 1.96100 1.79979 0.52000 +v 1.94137 1.52502 0.52000 +v 1.99323 1.51638 0.52000 +v 2.67720 2.10123 0.52000 +v 2.32900 2.66830 0.52000 +v 2.32900 2.79751 0.52000 +v 2.22024 2.90628 0.52000 +v 2.01024 3.25628 0.52000 +v 1.96500 3.30151 0.52000 +v 1.96120 3.25529 0.55000 +v 1.97120 2.49529 0.55000 +v 1.91100 2.43509 0.55000 +v 1.91100 2.42634 0.55000 +v 1.99100 2.20634 0.55000 +v 1.99100 1.79872 0.55000 +v 1.97324 1.55012 0.55000 +v 1.98436 1.54827 0.55000 +v 2.63823 2.10737 0.55000 +v 2.29900 2.65983 0.55000 +v 2.29900 2.78509 0.55000 +v 2.19641 2.88768 0.55000 +v 1.98641 3.23768 0.55000 +v 1.96500 3.25909 0.55000 +v 1.96120 3.25529 -0.55000 +v 1.97120 2.49529 -0.55000 +v 1.91100 2.43509 -0.55000 +v 1.91100 2.42634 -0.55000 +v 1.99100 2.20634 -0.55000 +v 1.99100 1.79872 -0.55000 +v 1.97324 1.55012 -0.55000 +v 1.98436 1.54827 -0.55000 +v 2.63823 2.10737 -0.55000 +v 2.29900 2.65983 -0.55000 +v 2.29900 2.78509 -0.55000 +v 2.19641 2.88768 -0.55000 +v 1.98641 3.23768 -0.55000 +v 1.96500 3.25909 -0.55000 +v 1.93103 3.26755 -0.52000 +v 1.94103 2.50755 -0.52000 +v 1.88100 2.44751 -0.52000 +v 1.88100 2.42106 -0.52000 +v 1.96100 2.20106 -0.52000 +v 1.96100 1.79979 -0.52000 +v 1.94137 1.52502 -0.52000 +v 1.99323 1.51638 -0.52000 +v 2.67720 2.10123 -0.52000 +v 2.32900 2.66830 -0.52000 +v 2.32900 2.79751 -0.52000 +v 2.22024 2.90628 -0.52000 +v 2.01024 3.25628 -0.52000 +v 1.96500 3.30151 -0.52000 +v 1.93103 3.26755 -0.51000 +v 1.94103 2.50755 -0.51000 +v 1.88100 2.44751 -0.51000 +v 1.88100 2.42106 -0.51000 +v 1.96100 2.20106 -0.51000 +v 1.96100 1.79979 -0.51000 +v 1.94137 1.52502 -0.51000 +v 1.99323 1.51638 -0.51000 +v 2.67720 2.10123 -0.51000 +v 2.32900 2.66830 -0.51000 +v 2.32900 2.79751 -0.51000 +v 2.22024 2.90628 -0.51000 +v 2.01024 3.25628 -0.51000 +v 1.96500 3.30151 -0.51000 +v 0.89100 2.24976 0.51000 +v 0.87103 0.81245 0.51000 +v 0.91763 0.76585 0.51000 +v 2.56267 0.66615 0.51000 +v 2.59670 0.70018 0.51000 +v 1.94026 1.41629 0.51000 +v 1.91900 1.44818 0.51000 +v 1.91900 1.50400 0.51000 +v 1.88275 1.50400 0.51000 +v 1.80075 1.57574 0.51000 +v 1.01062 2.47590 0.51000 +v 0.94251 2.54400 0.51000 +v 0.91061 2.54400 0.51000 +v 0.89100 2.24976 0.53500 +v 0.87103 0.81245 0.53500 +v 0.91763 0.76585 0.53500 +v 2.56267 0.66615 0.53500 +v 2.59670 0.70018 0.53500 +v 1.94026 1.41629 0.53500 +v 1.91900 1.44818 0.53500 +v 1.91900 1.50400 0.53500 +v 1.88275 1.50400 0.53500 +v 1.80075 1.57574 0.53500 +v 1.01062 2.47590 0.53500 +v 0.94251 2.54400 0.53500 +v 0.91061 2.54400 0.53500 +v 0.90599 2.24916 0.55000 +v 0.88612 0.81858 0.55000 +v 0.92422 0.78048 0.55000 +v 2.55683 0.68153 0.55000 +v 2.57593 0.70063 0.55000 +v 1.92843 1.40700 0.55000 +v 1.90400 1.44364 0.55000 +v 1.90400 1.48900 0.55000 +v 1.87711 1.48900 0.55000 +v 1.79013 1.56511 0.55000 +v 0.99967 2.46563 0.55000 +v 0.93630 2.52900 0.55000 +v 0.92465 2.52900 0.55000 +v 0.90599 2.24916 -0.55000 +v 0.88612 0.81858 -0.55000 +v 0.92422 0.78048 -0.55000 +v 2.55683 0.68153 -0.55000 +v 2.57593 0.70063 -0.55000 +v 1.92843 1.40700 -0.55000 +v 1.90400 1.44364 -0.55000 +v 1.90400 1.48900 -0.55000 +v 1.87711 1.48900 -0.55000 +v 1.79013 1.56511 -0.55000 +v 0.99967 2.46563 -0.55000 +v 0.93630 2.52900 -0.55000 +v 0.92465 2.52900 -0.55000 +v 0.89100 2.24976 -0.53500 +v 0.87103 0.81245 -0.53500 +v 0.91763 0.76585 -0.53500 +v 2.56267 0.66615 -0.53500 +v 2.59670 0.70018 -0.53500 +v 1.94026 1.41629 -0.53500 +v 1.91900 1.44818 -0.53500 +v 1.91900 1.50400 -0.53500 +v 1.88275 1.50400 -0.53500 +v 1.80075 1.57574 -0.53500 +v 1.01062 2.47590 -0.53500 +v 0.94251 2.54400 -0.53500 +v 0.91061 2.54400 -0.53500 +v 0.89100 2.24976 -0.51000 +v 0.87103 0.81245 -0.51000 +v 0.91763 0.76585 -0.51000 +v 2.56267 0.66615 -0.51000 +v 2.59670 0.70018 -0.51000 +v 1.94026 1.41629 -0.51000 +v 1.91900 1.44818 -0.51000 +v 1.91900 1.50400 -0.51000 +v 1.88275 1.50400 -0.51000 +v 1.80075 1.57574 -0.51000 +v 1.01062 2.47590 -0.51000 +v 0.94251 2.54400 -0.51000 +v 0.91061 2.54400 -0.51000 +v 1.99676 1.49362 0.51000 +v 1.94100 1.50292 0.51000 +v 1.94100 1.45182 0.51000 +v 1.95975 1.42368 0.51000 +v 2.38921 0.94430 0.51000 +v 2.45419 0.88859 0.51000 +v 2.48965 0.93291 0.51000 +v 2.84932 2.01193 0.51000 +v 2.92978 2.24327 0.51000 +v 2.94487 2.25836 0.51000 +v 2.91586 2.27286 0.51000 +v 1.99676 1.49362 0.53500 +v 1.94100 1.50292 0.53500 +v 1.94100 1.45182 0.53500 +v 1.95975 1.42368 0.53500 +v 2.38921 0.94430 0.53500 +v 2.45419 0.88859 0.53500 +v 2.48965 0.93291 0.53500 +v 2.84932 2.01193 0.53500 +v 2.92978 2.24327 0.53500 +v 2.94487 2.25836 0.53500 +v 2.91586 2.27286 0.53500 +v 2.00115 1.47768 0.55000 +v 1.95600 1.48521 0.55000 +v 1.95600 1.45636 0.55000 +v 1.97164 1.43290 0.55000 +v 2.39972 0.95504 0.55000 +v 2.45217 0.91008 0.55000 +v 2.47626 0.94019 0.55000 +v 2.83512 2.01677 0.55000 +v 2.91675 2.25145 0.55000 +v 2.91955 2.25425 0.55000 +v 2.91801 2.25502 0.55000 +v 2.00115 1.47768 -0.55000 +v 1.95600 1.48521 -0.55000 +v 1.95600 1.45636 -0.55000 +v 1.97164 1.43290 -0.55000 +v 2.39972 0.95504 -0.55000 +v 2.45217 0.91008 -0.55000 +v 2.47626 0.94019 -0.55000 +v 2.83512 2.01677 -0.55000 +v 2.91675 2.25145 -0.55000 +v 2.91955 2.25425 -0.55000 +v 2.91801 2.25502 -0.55000 +v 1.99676 1.49362 -0.53500 +v 1.94100 1.50292 -0.53500 +v 1.94100 1.45182 -0.53500 +v 1.95975 1.42368 -0.53500 +v 2.38921 0.94430 -0.53500 +v 2.45419 0.88859 -0.53500 +v 2.48965 0.93291 -0.53500 +v 2.84932 2.01193 -0.53500 +v 2.92978 2.24327 -0.53500 +v 2.94487 2.25836 -0.53500 +v 2.91586 2.27286 -0.53500 +v 1.99676 1.49362 -0.51000 +v 1.94100 1.50292 -0.51000 +v 1.94100 1.45182 -0.51000 +v 1.95975 1.42368 -0.51000 +v 2.38921 0.94430 -0.51000 +v 2.45419 0.88859 -0.51000 +v 2.48965 0.93291 -0.51000 +v 2.84932 2.01193 -0.51000 +v 2.92978 2.24327 -0.51000 +v 2.94487 2.25836 -0.51000 +v 2.91586 2.27286 -0.51000 +v -4.19500 2.90000 -0.22000 +v -4.23500 2.93000 -0.22000 +v -4.27500 2.89000 -0.22000 +v -4.41500 1.39000 -0.22000 +v -4.37500 1.35000 -0.22000 +v -4.39500 1.32000 -0.22000 +v -3.17500 0.13000 -0.22000 +v 0.22500 0.07000 -0.22000 +v 0.26500 0.11000 -0.22000 +v 0.23500 0.15000 -0.22000 +v -0.50500 1.02000 -0.22000 +v -0.53500 1.00000 -0.22000 +v -0.58500 1.05000 -0.22000 +v -2.16500 0.77000 -0.22000 +v -2.26500 0.67000 -0.22000 +v -3.41500 1.45000 -0.22000 +v -3.55500 1.59000 -0.22000 +v -3.68500 2.17000 -0.22000 +v -3.08500 2.84000 -0.22000 +v -2.86500 3.85000 -0.22000 +v -2.91500 3.89000 -0.22000 +v -4.19536 2.94902 0.00000 +v -4.23866 2.98149 0.00000 +v -4.31252 2.90763 0.00000 +v -4.45553 1.37538 0.00000 +v -4.42519 1.34503 0.00000 +v -4.44541 1.31469 0.00000 +v -3.19116 0.09128 0.00000 +v 0.24087 0.03071 0.00000 +v 0.31649 0.10634 0.00000 +v 0.26548 0.17436 0.00000 +v -0.49777 1.07169 0.00000 +v -0.53003 1.05019 0.00000 +v -0.57179 1.09195 0.00000 +v -2.18390 0.80626 0.00000 +v -2.26978 0.72037 0.00000 +v -3.39004 1.48020 0.00000 +v -3.51942 1.60957 0.00000 +v -3.64255 2.15894 0.00000 +v -3.04908 2.82165 0.00000 +v -2.82174 3.86534 0.00000 +v -2.91459 3.93962 0.00000 +v -4.19500 2.90000 0.22000 +v -4.23500 2.93000 0.22000 +v -4.27500 2.89000 0.22000 +v -4.41500 1.39000 0.22000 +v -4.37500 1.35000 0.22000 +v -4.39500 1.32000 0.22000 +v -3.17500 0.13000 0.22000 +v 0.22500 0.07000 0.22000 +v 0.26500 0.11000 0.22000 +v 0.23500 0.15000 0.22000 +v -0.50500 1.02000 0.22000 +v -0.53500 1.00000 0.22000 +v -0.58500 1.05000 0.22000 +v -2.16500 0.77000 0.22000 +v -2.26500 0.67000 0.22000 +v -3.41500 1.45000 0.22000 +v -3.55500 1.59000 0.22000 +v -3.68500 2.17000 0.22000 +v -3.08500 2.84000 0.22000 +v -2.86500 3.85000 0.22000 +v -2.91500 3.89000 0.22000 +v -1.43138 0.28521 0.22000 +v -1.54305 0.20400 0.22000 +v -1.55900 0.20400 0.22000 +v -1.55900 0.16492 0.22000 +v -1.51249 0.15562 0.22000 +v -1.47292 0.12594 0.22000 +v 0.22259 0.07607 0.22000 +v 0.25708 0.11056 0.22000 +v 0.23031 0.14625 0.22000 +v -0.50583 1.01172 0.22000 +v -1.43138 0.28521 0.24500 +v -1.54305 0.20400 0.24500 +v -1.55900 0.20400 0.24500 +v -1.55900 0.16492 0.24500 +v -1.51249 0.15562 0.24500 +v -1.47292 0.12594 0.24500 +v 0.22259 0.07607 0.24500 +v 0.25708 0.11056 0.24500 +v 0.23031 0.14625 0.24500 +v -0.50583 1.01172 0.24500 +v -1.42234 0.27324 0.26000 +v -1.53817 0.18900 0.26000 +v -1.54400 0.18900 0.26000 +v -1.54400 0.17722 0.26000 +v -1.50621 0.16966 0.26000 +v -1.46773 0.14080 0.26000 +v 0.21656 0.09126 0.26000 +v 0.23727 0.11197 0.26000 +v 0.21859 0.13688 0.26000 +v -0.50792 0.99101 0.26000 +v -1.42234 0.27324 -0.26000 +v -1.53817 0.18900 -0.26000 +v -1.54400 0.18900 -0.26000 +v -1.54400 0.17722 -0.26000 +v -1.50621 0.16966 -0.26000 +v -1.46773 0.14080 -0.26000 +v 0.21656 0.09126 -0.26000 +v 0.23727 0.11197 -0.26000 +v 0.21859 0.13688 -0.26000 +v -0.50792 0.99101 -0.26000 +v -1.43138 0.28521 -0.24500 +v -1.54305 0.20400 -0.24500 +v -1.55900 0.20400 -0.24500 +v -1.55900 0.16492 -0.24500 +v -1.51249 0.15562 -0.24500 +v -1.47292 0.12594 -0.24500 +v 0.22259 0.07607 -0.24500 +v 0.25708 0.11056 -0.24500 +v 0.23031 0.14625 -0.24500 +v -0.50583 1.01172 -0.24500 +v -1.43138 0.28521 -0.22000 +v -1.54305 0.20400 -0.22000 +v -1.55900 0.20400 -0.22000 +v -1.55900 0.16492 -0.22000 +v -1.51249 0.15562 -0.22000 +v -1.47292 0.12594 -0.22000 +v 0.22259 0.07607 -0.22000 +v 0.25708 0.11056 -0.22000 +v 0.23031 0.14625 -0.22000 +v -0.50583 1.01172 -0.22000 +v -2.16184 0.76447 0.22000 +v -2.21629 0.70094 0.22000 +v -1.69147 0.30485 0.22000 +v -1.59259 0.23564 0.22000 +v -1.54652 0.22642 0.22000 +v -0.59898 0.96451 0.22000 +v -0.55349 1.01000 0.22000 +v -0.58703 1.04355 0.22000 +v -2.16184 0.76447 0.23000 +v -2.21629 0.70094 0.23000 +v -1.69147 0.30485 0.23000 +v -1.59259 0.23564 0.23000 +v -1.54652 0.22642 0.23000 +v -0.59898 0.96451 0.23000 +v -0.55349 1.01000 0.23000 +v -0.58703 1.04355 0.23000 +v -2.14604 0.73680 0.26000 +v -2.17274 0.70566 0.26000 +v -1.67383 0.32912 0.26000 +v -1.58054 0.26382 0.26000 +v -1.55411 0.25854 0.26000 +v -0.61888 0.98703 0.26000 +v -0.59591 1.01000 0.26000 +v -0.59719 1.01128 0.26000 +v -2.14604 0.73680 -0.26000 +v -2.17274 0.70566 -0.26000 +v -1.67383 0.32912 -0.26000 +v -1.58054 0.26382 -0.26000 +v -1.55411 0.25854 -0.26000 +v -0.61888 0.98703 -0.26000 +v -0.59591 1.01000 -0.26000 +v -0.59719 1.01128 -0.26000 +v -2.16184 0.76447 -0.23000 +v -2.21629 0.70094 -0.23000 +v -1.69147 0.30485 -0.23000 +v -1.59259 0.23564 -0.23000 +v -1.54652 0.22642 -0.23000 +v -0.59898 0.96451 -0.23000 +v -0.55349 1.01000 -0.23000 +v -0.58703 1.04355 -0.23000 +v -2.16184 0.76447 -0.22000 +v -2.21629 0.70094 -0.22000 +v -1.69147 0.30485 -0.22000 +v -1.59259 0.23564 -0.22000 +v -1.54652 0.22642 -0.22000 +v -0.59898 0.96451 -0.22000 +v -0.55349 1.01000 -0.22000 +v -0.58703 1.04355 -0.22000 +v -3.51881 1.45001 0.22000 +v -3.21875 0.25977 0.22000 +v -3.23814 0.20162 0.22000 +v -3.17244 0.13593 0.22000 +v -1.51741 0.08608 0.22000 +v -1.49230 0.11118 0.22000 +v -1.49931 0.12521 0.22000 +v -1.58035 0.15560 0.22000 +v -1.59051 0.21656 0.22000 +v -2.10868 0.58526 0.22000 +v -2.23466 0.69185 0.22000 +v -2.26382 0.66270 0.22000 +v -2.28804 0.67481 0.22000 +v -3.41870 1.44526 0.22000 +v -3.50158 1.51893 0.22000 +v -3.51881 1.45001 0.25250 +v -3.21875 0.25977 0.25250 +v -3.23814 0.20162 0.25250 +v -3.17244 0.13593 0.25250 +v -1.51741 0.08608 0.25250 +v -1.49230 0.11118 0.25250 +v -1.49931 0.12521 0.25250 +v -1.58035 0.15560 0.25250 +v -1.59051 0.21656 0.25250 +v -2.10868 0.58526 0.25250 +v -2.23466 0.69185 0.25250 +v -2.26382 0.66270 0.25250 +v -2.28804 0.67481 0.25250 +v -3.41870 1.44526 0.25250 +v -3.50158 1.51893 0.25250 +v -3.51108 1.45001 0.26000 +v -3.21095 0.25947 0.26000 +v -3.22955 0.20365 0.26000 +v -3.16924 0.14333 0.26000 +v -1.52042 0.09367 0.26000 +v -1.50143 0.11267 0.26000 +v -1.50470 0.11922 0.26000 +v -1.58704 0.15010 0.26000 +v -1.59739 0.21225 0.26000 +v -2.11329 0.57933 0.26000 +v -2.23424 0.68167 0.26000 +v -2.26233 0.65357 0.26000 +v -2.29185 0.66833 0.26000 +v -3.42332 1.43933 0.26000 +v -3.49731 1.50510 0.26000 +v -3.51108 1.45001 -0.26000 +v -3.21095 0.25947 -0.26000 +v -3.22955 0.20365 -0.26000 +v -3.16924 0.14333 -0.26000 +v -1.52042 0.09367 -0.26000 +v -1.50143 0.11267 -0.26000 +v -1.50470 0.11922 -0.26000 +v -1.58704 0.15010 -0.26000 +v -1.59739 0.21225 -0.26000 +v -2.11329 0.57933 -0.26000 +v -2.23424 0.68167 -0.26000 +v -2.26233 0.65357 -0.26000 +v -2.29185 0.66833 -0.26000 +v -3.42332 1.43933 -0.26000 +v -3.49731 1.50510 -0.26000 +v -3.51881 1.45001 -0.25250 +v -3.21875 0.25977 -0.25250 +v -3.23814 0.20162 -0.25250 +v -3.17244 0.13593 -0.25250 +v -1.51741 0.08608 -0.25250 +v -1.49230 0.11118 -0.25250 +v -1.49931 0.12521 -0.25250 +v -1.58035 0.15560 -0.25250 +v -1.59051 0.21656 -0.25250 +v -2.10868 0.58526 -0.25250 +v -2.23466 0.69185 -0.25250 +v -2.26382 0.66270 -0.25250 +v -2.28804 0.67481 -0.25250 +v -3.41870 1.44526 -0.25250 +v -3.50158 1.51893 -0.25250 +v -3.51881 1.45001 -0.22000 +v -3.21875 0.25977 -0.22000 +v -3.23814 0.20162 -0.22000 +v -3.17244 0.13593 -0.22000 +v -1.51741 0.08608 -0.22000 +v -1.49230 0.11118 -0.22000 +v -1.49931 0.12521 -0.22000 +v -1.58035 0.15560 -0.22000 +v -1.59051 0.21656 -0.22000 +v -2.10868 0.58526 -0.22000 +v -2.23466 0.69185 -0.22000 +v -2.26382 0.66270 -0.22000 +v -2.28804 0.67481 -0.22000 +v -3.41870 1.44526 -0.22000 +v -3.50158 1.51893 -0.22000 +v -3.77922 2.09785 0.22000 +v -3.80012 2.06651 0.22000 +v -4.32048 1.38604 0.22000 +v -4.38649 1.32003 0.22000 +v -3.55081 0.49429 0.22000 +v -3.27181 0.22526 0.22000 +v -3.24955 0.21784 0.22000 +v -3.24115 0.25985 0.22000 +v -3.54111 1.44968 0.22000 +v -3.53066 1.52283 0.22000 +v -3.51349 1.54000 0.22000 +v -3.56048 1.58699 0.22000 +v -3.68040 2.12664 0.22000 +v -3.72761 2.16440 0.22000 +v -3.76985 2.17285 0.22000 +v -3.77922 2.09785 0.24500 +v -3.80012 2.06651 0.24500 +v -4.32048 1.38604 0.24500 +v -4.38649 1.32003 0.24500 +v -3.55081 0.49429 0.24500 +v -3.27181 0.22526 0.24500 +v -3.24955 0.21784 0.24500 +v -3.24115 0.25985 0.24500 +v -3.54111 1.44968 0.24500 +v -3.53066 1.52283 0.24500 +v -3.51349 1.54000 0.24500 +v -3.56048 1.58699 0.24500 +v -3.68040 2.12664 0.24500 +v -3.72761 2.16440 0.24500 +v -3.76985 2.17285 0.24500 +v -3.76478 2.09248 0.26000 +v -3.78790 2.05778 0.26000 +v -4.30917 1.37614 0.26000 +v -4.36521 1.32009 0.26000 +v -3.54033 0.50503 0.26000 +v -3.26385 0.23842 0.26000 +v -3.26093 0.23744 0.26000 +v -3.25652 0.25947 0.26000 +v -3.55637 1.44888 0.26000 +v -3.54480 1.52990 0.26000 +v -3.53470 1.54000 0.26000 +v -3.57417 1.57947 0.26000 +v -3.69390 2.11822 0.26000 +v -3.73413 2.15041 0.26000 +v -3.75696 2.15498 0.26000 +v -3.76478 2.09248 -0.26000 +v -3.78790 2.05778 -0.26000 +v -4.30917 1.37614 -0.26000 +v -4.36521 1.32009 -0.26000 +v -3.54033 0.50503 -0.26000 +v -3.26385 0.23842 -0.26000 +v -3.26093 0.23744 -0.26000 +v -3.25652 0.25947 -0.26000 +v -3.55637 1.44888 -0.26000 +v -3.54480 1.52990 -0.26000 +v -3.53470 1.54000 -0.26000 +v -3.57417 1.57947 -0.26000 +v -3.69390 2.11822 -0.26000 +v -3.73413 2.15041 -0.26000 +v -3.75696 2.15498 -0.26000 +v -3.77922 2.09785 -0.24500 +v -3.80012 2.06651 -0.24500 +v -4.32048 1.38604 -0.24500 +v -4.38649 1.32003 -0.24500 +v -3.55081 0.49429 -0.24500 +v -3.27181 0.22526 -0.24500 +v -3.24955 0.21784 -0.24500 +v -3.24115 0.25985 -0.24500 +v -3.54111 1.44968 -0.24500 +v -3.53066 1.52283 -0.24500 +v -3.51349 1.54000 -0.24500 +v -3.56048 1.58699 -0.24500 +v -3.68040 2.12664 -0.24500 +v -3.72761 2.16440 -0.24500 +v -3.76985 2.17285 -0.24500 +v -3.77922 2.09785 -0.22000 +v -3.80012 2.06651 -0.22000 +v -4.32048 1.38604 -0.22000 +v -4.38649 1.32003 -0.22000 +v -3.55081 0.49429 -0.22000 +v -3.27181 0.22526 -0.22000 +v -3.24955 0.21784 -0.22000 +v -3.24115 0.25985 -0.22000 +v -3.54111 1.44968 -0.22000 +v -3.53066 1.52283 -0.22000 +v -3.51349 1.54000 -0.22000 +v -3.56048 1.58699 -0.22000 +v -3.68040 2.12664 -0.22000 +v -3.72761 2.16440 -0.22000 +v -3.76985 2.17285 -0.22000 +v -4.26940 2.88712 0.22000 +v -4.27904 2.82927 0.22000 +v -4.38908 1.44880 0.22000 +v -4.40814 1.39162 0.22000 +v -4.38251 1.36600 0.22000 +v -4.36749 1.36600 0.22000 +v -4.33952 1.39396 0.22000 +v -3.82022 2.07305 0.22000 +v -3.79118 2.14082 0.22000 +v -3.80077 2.20797 0.22000 +v -3.99024 2.52707 0.22000 +v -4.18757 2.90201 0.22000 +v -4.20618 2.89270 0.22000 +v -4.23500 2.92151 0.22000 +v -4.26940 2.88712 0.24500 +v -4.27904 2.82927 0.24500 +v -4.38908 1.44880 0.24500 +v -4.40814 1.39162 0.24500 +v -4.38251 1.36600 0.24500 +v -4.36749 1.36600 0.24500 +v -4.33952 1.39396 0.24500 +v -3.82022 2.07305 0.24500 +v -3.79118 2.14082 0.24500 +v -3.80077 2.20797 0.24500 +v -3.99024 2.52707 0.24500 +v -4.18757 2.90201 0.24500 +v -4.20618 2.89270 0.24500 +v -4.23500 2.92151 0.24500 +v -4.25539 2.87991 0.26000 +v -4.26414 2.82743 0.26000 +v -4.37427 1.44578 0.26000 +v -4.39097 1.39567 0.26000 +v -4.37630 1.38100 0.26000 +v -4.37370 1.38100 0.26000 +v -4.35083 1.40386 0.26000 +v -3.83327 2.08068 0.26000 +v -3.80662 2.14286 0.26000 +v -3.81520 2.20288 0.26000 +v -4.00333 2.51974 0.26000 +v -4.19400 2.88202 0.26000 +v -4.20915 2.87445 0.26000 +v -4.23500 2.90030 0.26000 +v -4.25539 2.87991 -0.26000 +v -4.26414 2.82743 -0.26000 +v -4.37427 1.44578 -0.26000 +v -4.39097 1.39567 -0.26000 +v -4.37630 1.38100 -0.26000 +v -4.37370 1.38100 -0.26000 +v -4.35083 1.40386 -0.26000 +v -3.83327 2.08068 -0.26000 +v -3.80662 2.14286 -0.26000 +v -3.81520 2.20288 -0.26000 +v -4.00333 2.51974 -0.26000 +v -4.19400 2.88202 -0.26000 +v -4.20915 2.87445 -0.26000 +v -4.23500 2.90030 -0.26000 +v -4.26940 2.88712 -0.24500 +v -4.27904 2.82927 -0.24500 +v -4.38908 1.44880 -0.24500 +v -4.40814 1.39162 -0.24500 +v -4.38251 1.36600 -0.24500 +v -4.36749 1.36600 -0.24500 +v -4.33952 1.39396 -0.24500 +v -3.82022 2.07305 -0.24500 +v -3.79118 2.14082 -0.24500 +v -3.80077 2.20797 -0.24500 +v -3.99024 2.52707 -0.24500 +v -4.18757 2.90201 -0.24500 +v -4.20618 2.89270 -0.24500 +v -4.23500 2.92151 -0.24500 +v -4.26940 2.88712 -0.22000 +v -4.27904 2.82927 -0.22000 +v -4.38908 1.44880 -0.22000 +v -4.40814 1.39162 -0.22000 +v -4.38251 1.36600 -0.22000 +v -4.36749 1.36600 -0.22000 +v -4.33952 1.39396 -0.22000 +v -3.82022 2.07305 -0.22000 +v -3.79118 2.14082 -0.22000 +v -3.80077 2.20797 -0.22000 +v -3.99024 2.52707 -0.22000 +v -4.18757 2.90201 -0.22000 +v -4.20618 2.89270 -0.22000 +v -4.23500 2.92151 -0.22000 +v -4.16738 2.91829 0.22000 +v -3.96975 2.53292 0.22000 +v -3.76113 2.18522 0.22000 +v -3.68708 2.16671 0.22000 +v -3.09056 2.84277 0.22000 +v -2.89064 3.82235 0.22000 +v -2.87304 3.84875 0.22000 +v -2.91507 3.88237 0.22000 +v -4.16738 2.91829 0.23000 +v -3.96975 2.53292 0.23000 +v -3.76113 2.18522 0.23000 +v -3.68708 2.16671 0.23000 +v -3.09056 2.84277 0.23000 +v -2.89064 3.82235 0.23000 +v -2.87304 3.84875 0.23000 +v -2.91507 3.88237 0.23000 +v -4.12929 2.90976 0.26000 +v -3.94351 2.54749 0.26000 +v -3.74180 2.21131 0.26000 +v -3.69751 2.20024 0.26000 +v -3.11835 2.85661 0.26000 +v -2.91886 3.83410 0.26000 +v -2.91326 3.84251 0.26000 +v -2.91543 3.84424 0.26000 +v -4.12929 2.90976 -0.26000 +v -3.94351 2.54749 -0.26000 +v -3.74180 2.21131 -0.26000 +v -3.69751 2.20024 -0.26000 +v -3.11835 2.85661 -0.26000 +v -2.91886 3.83410 -0.26000 +v -2.91326 3.84251 -0.26000 +v -2.91543 3.84424 -0.26000 +v -4.16738 2.91829 -0.23000 +v -3.96975 2.53292 -0.23000 +v -3.76113 2.18522 -0.23000 +v -3.68708 2.16671 -0.23000 +v -3.09056 2.84277 -0.23000 +v -2.89064 3.82235 -0.23000 +v -2.87304 3.84875 -0.23000 +v -2.91507 3.88237 -0.23000 +v -4.16738 2.91829 -0.22000 +v -3.96975 2.53292 -0.22000 +v -3.76113 2.18522 -0.22000 +v -3.68708 2.16671 -0.22000 +v -3.09056 2.84277 -0.22000 +v -2.89064 3.82235 -0.22000 +v -2.87304 3.84875 -0.22000 +v -2.91507 3.88237 -0.22000 +v 3.54500 1.01000 -0.51000 +v 2.45500 0.95000 -0.51000 +v 2.41500 0.91000 -0.51000 +v 2.45500 0.84000 -0.51000 +v 2.34500 0.73000 -0.51000 +v 1.88500 0.73000 -0.51000 +v 1.84500 0.69000 -0.51000 +v 2.96500 0.05000 -0.51000 +v 3.07500 0.16000 -0.51000 +v 4.35500 0.09000 -0.51000 +v 4.39500 0.13000 -0.51000 +v 4.35500 0.18000 -0.51000 +v 3.57500 1.04000 -0.51000 +v 3.52797 1.04812 -0.29000 +v 2.43797 0.98812 -0.29000 +v 2.36636 0.91651 -0.29000 +v 2.40636 0.84651 -0.29000 +v 2.32885 0.76900 -0.29000 +v 1.86885 0.76900 -0.29000 +v 1.78132 0.68147 -0.29000 +v 2.97151 0.00136 -0.29000 +v 3.09026 0.12011 -0.29000 +v 4.37026 0.05011 -0.29000 +v 4.44726 0.12711 -0.29000 +v 4.38470 0.20531 -0.29000 +v 3.57638 1.09653 -0.29000 +v 3.54500 1.01000 -0.07000 +v 2.45500 0.95000 -0.07000 +v 2.41500 0.91000 -0.07000 +v 2.45500 0.84000 -0.07000 +v 2.34500 0.73000 -0.07000 +v 1.88500 0.73000 -0.07000 +v 1.84500 0.69000 -0.07000 +v 2.96500 0.05000 -0.07000 +v 3.07500 0.16000 -0.07000 +v 4.35500 0.09000 -0.07000 +v 4.39500 0.13000 -0.07000 +v 4.35500 0.18000 -0.07000 +v 3.57500 1.04000 -0.07000 +v 2.45765 0.94416 -0.07000 +v 2.42248 0.90900 -0.07000 +v 2.46223 0.83944 -0.07000 +v 2.42432 0.79205 -0.07000 +v 2.51834 0.74504 -0.07000 +v 2.58957 0.68399 -0.07000 +v 2.89827 0.20600 -0.07000 +v 2.92210 0.20600 -0.07000 +v 3.52857 0.97155 -0.07000 +v 3.52015 1.01367 -0.07000 +v 2.45765 0.94416 -0.04500 +v 2.42248 0.90900 -0.04500 +v 2.46223 0.83944 -0.04500 +v 2.42432 0.79205 -0.04500 +v 2.51834 0.74504 -0.04500 +v 2.58957 0.68399 -0.04500 +v 2.89827 0.20600 -0.04500 +v 2.92210 0.20600 -0.04500 +v 3.52857 0.97155 -0.04500 +v 3.52015 1.01367 -0.04500 +v 2.46426 0.92956 -0.03000 +v 2.44119 0.90649 -0.03000 +v 2.48031 0.83803 -0.03000 +v 2.44763 0.79717 -0.03000 +v 2.52670 0.75763 -0.03000 +v 2.60098 0.69396 -0.03000 +v 2.90644 0.22100 -0.03000 +v 2.91485 0.22100 -0.03000 +v 3.51250 0.97542 -0.03000 +v 3.50802 0.99784 -0.03000 +v 2.46426 0.92956 -0.55000 +v 2.44119 0.90649 -0.55000 +v 2.48031 0.83803 -0.55000 +v 2.44763 0.79717 -0.55000 +v 2.52670 0.75763 -0.55000 +v 2.60098 0.69396 -0.55000 +v 2.90644 0.22100 -0.55000 +v 2.91485 0.22100 -0.55000 +v 3.51250 0.97542 -0.55000 +v 3.50802 0.99784 -0.55000 +v 2.45765 0.94416 -0.53500 +v 2.42248 0.90900 -0.53500 +v 2.46223 0.83944 -0.53500 +v 2.42432 0.79205 -0.53500 +v 2.51834 0.74504 -0.53500 +v 2.58957 0.68399 -0.53500 +v 2.89827 0.20600 -0.53500 +v 2.92210 0.20600 -0.53500 +v 3.52857 0.97155 -0.53500 +v 3.52015 1.01367 -0.53500 +v 2.45765 0.94416 -0.51000 +v 2.42248 0.90900 -0.51000 +v 2.46223 0.83944 -0.51000 +v 2.42432 0.79205 -0.51000 +v 2.51834 0.74504 -0.51000 +v 2.58957 0.68399 -0.51000 +v 2.89827 0.20600 -0.51000 +v 2.92210 0.20600 -0.51000 +v 3.52857 0.97155 -0.51000 +v 3.52015 1.01367 -0.51000 +v 2.34749 0.72400 -0.07000 +v 1.88749 0.72400 -0.07000 +v 1.85405 0.69056 -0.07000 +v 1.93831 0.62502 -0.07000 +v 2.96395 0.05744 -0.07000 +v 3.02503 0.11852 -0.07000 +v 2.88080 0.19544 -0.07000 +v 2.57066 0.67566 -0.07000 +v 2.40581 0.78233 -0.07000 +v 2.34749 0.72400 -0.06000 +v 1.88749 0.72400 -0.06000 +v 1.85405 0.69056 -0.06000 +v 1.93831 0.62502 -0.06000 +v 2.96395 0.05744 -0.06000 +v 3.02503 0.11852 -0.06000 +v 2.88080 0.19544 -0.06000 +v 2.57066 0.67566 -0.06000 +v 2.40581 0.78233 -0.06000 +v 2.35991 0.69400 -0.03000 +v 1.89991 0.69400 -0.03000 +v 1.89929 0.69338 -0.03000 +v 1.95489 0.65014 -0.03000 +v 2.95871 0.09462 -0.03000 +v 2.97519 0.11110 -0.03000 +v 2.85982 0.17263 -0.03000 +v 2.54895 0.65398 -0.03000 +v 2.40988 0.74397 -0.03000 +v 2.35991 0.69400 -0.55000 +v 1.89991 0.69400 -0.55000 +v 1.89929 0.69338 -0.55000 +v 1.95489 0.65014 -0.55000 +v 2.95871 0.09462 -0.55000 +v 2.97519 0.11110 -0.55000 +v 2.85982 0.17263 -0.55000 +v 2.54895 0.65398 -0.55000 +v 2.40988 0.74397 -0.55000 +v 2.34749 0.72400 -0.52000 +v 1.88749 0.72400 -0.52000 +v 1.85405 0.69056 -0.52000 +v 1.93831 0.62502 -0.52000 +v 2.96395 0.05744 -0.52000 +v 3.02503 0.11852 -0.52000 +v 2.88080 0.19544 -0.52000 +v 2.57066 0.67566 -0.52000 +v 2.40581 0.78233 -0.52000 +v 2.34749 0.72400 -0.51000 +v 1.88749 0.72400 -0.51000 +v 1.85405 0.69056 -0.51000 +v 1.93831 0.62502 -0.51000 +v 2.96395 0.05744 -0.51000 +v 3.02503 0.11852 -0.51000 +v 2.88080 0.19544 -0.51000 +v 2.57066 0.67566 -0.51000 +v 2.40581 0.78233 -0.51000 +v 3.55100 1.00751 -0.07000 +v 3.55100 0.96800 -0.07000 +v 3.51974 0.92632 -0.07000 +v 2.94100 0.20788 -0.07000 +v 2.94100 0.19321 -0.07000 +v 3.02682 0.13600 -0.07000 +v 3.04251 0.13600 -0.07000 +v 3.07251 0.16600 -0.07000 +v 3.14517 0.16600 -0.07000 +v 4.35266 0.09615 -0.07000 +v 4.38696 0.13045 -0.07000 +v 4.35043 0.17611 -0.07000 +v 3.57479 1.03130 -0.07000 +v 3.55100 1.00751 -0.04500 +v 3.55100 0.96800 -0.04500 +v 3.51974 0.92632 -0.04500 +v 2.94100 0.20788 -0.04500 +v 2.94100 0.19321 -0.04500 +v 3.02682 0.13600 -0.04500 +v 3.04251 0.13600 -0.04500 +v 3.07251 0.16600 -0.04500 +v 3.14517 0.16600 -0.04500 +v 4.35266 0.09615 -0.04500 +v 4.38696 0.13045 -0.04500 +v 4.35043 0.17611 -0.04500 +v 3.57479 1.03130 -0.04500 +v 3.56600 1.00130 -0.03000 +v 3.56600 0.96300 -0.03000 +v 3.53158 0.91711 -0.03000 +v 2.95600 0.20259 -0.03000 +v 2.95600 0.20124 -0.03000 +v 3.03136 0.15100 -0.03000 +v 3.03630 0.15100 -0.03000 +v 3.06630 0.18100 -0.03000 +v 3.14561 0.18100 -0.03000 +v 4.34681 0.11151 -0.03000 +v 4.36686 0.13156 -0.03000 +v 4.33901 0.16637 -0.03000 +v 3.57426 1.00956 -0.03000 +v 3.56600 1.00130 -0.55000 +v 3.56600 0.96300 -0.55000 +v 3.53158 0.91711 -0.55000 +v 2.95600 0.20259 -0.55000 +v 2.95600 0.20124 -0.55000 +v 3.03136 0.15100 -0.55000 +v 3.03630 0.15100 -0.55000 +v 3.06630 0.18100 -0.55000 +v 3.14561 0.18100 -0.55000 +v 4.34681 0.11151 -0.55000 +v 4.36686 0.13156 -0.55000 +v 4.33901 0.16637 -0.55000 +v 3.57426 1.00956 -0.55000 +v 3.55100 1.00751 -0.53500 +v 3.55100 0.96800 -0.53500 +v 3.51974 0.92632 -0.53500 +v 2.94100 0.20788 -0.53500 +v 2.94100 0.19321 -0.53500 +v 3.02682 0.13600 -0.53500 +v 3.04251 0.13600 -0.53500 +v 3.07251 0.16600 -0.53500 +v 3.14517 0.16600 -0.53500 +v 4.35266 0.09615 -0.53500 +v 4.38696 0.13045 -0.53500 +v 4.35043 0.17611 -0.53500 +v 3.57479 1.03130 -0.53500 +v 3.55100 1.00751 -0.51000 +v 3.55100 0.96800 -0.51000 +v 3.51974 0.92632 -0.51000 +v 2.94100 0.20788 -0.51000 +v 2.94100 0.19321 -0.51000 +v 3.02682 0.13600 -0.51000 +v 3.04251 0.13600 -0.51000 +v 3.07251 0.16600 -0.51000 +v 3.14517 0.16600 -0.51000 +v 4.35266 0.09615 -0.51000 +v 4.38696 0.13045 -0.51000 +v 4.35043 0.17611 -0.51000 +v 3.57479 1.03130 -0.51000 +v -2.58500 2.28000 -0.45000 +v -2.88500 1.16000 -0.45000 +v -2.23500 0.66000 -0.45000 +v -0.94500 2.00000 -0.45000 +v -1.51500 3.09000 -0.45000 +v -1.64500 3.16000 -0.45000 +v -2.61444 2.29765 -0.27000 +v -2.92257 1.14727 -0.27000 +v -2.23171 0.61584 -0.27000 +v -0.90474 1.99423 -0.27000 +v -1.49045 3.11426 -0.27000 +v -1.65024 3.20030 -0.27000 +v -2.58500 2.28000 -0.09000 +v -2.88500 1.16000 -0.09000 +v -2.23500 0.66000 -0.09000 +v -0.94500 2.00000 -0.09000 +v -1.51500 3.09000 -0.09000 +v -1.64500 3.16000 -0.09000 +v -2.57965 2.27679 -0.09000 +v -2.84918 1.27854 -0.09000 +v -2.87841 1.15189 -0.09000 +v -2.83117 1.10465 -0.09000 +v -2.40139 0.81480 -0.09000 +v -2.28350 0.71656 -0.09000 +v -2.24965 0.72502 -0.09000 +v -2.21077 0.84167 -0.09000 +v -2.05081 1.49151 -0.09000 +v -1.63083 3.03144 -0.09000 +v -1.61157 3.11809 -0.09000 +v -1.64514 3.15165 -0.09000 +v -2.57965 2.27679 -0.08000 +v -2.84918 1.27854 -0.08000 +v -2.87841 1.15189 -0.08000 +v -2.83117 1.10465 -0.08000 +v -2.40139 0.81480 -0.08000 +v -2.28350 0.71656 -0.08000 +v -2.24965 0.72502 -0.08000 +v -2.21077 0.84167 -0.08000 +v -2.05081 1.49151 -0.08000 +v -1.63083 3.03144 -0.08000 +v -1.61157 3.11809 -0.08000 +v -1.64514 3.15165 -0.08000 +v -2.55292 2.26072 -0.05000 +v -2.82007 1.27126 -0.05000 +v -2.84544 1.16135 -0.05000 +v -2.81200 1.12791 -0.05000 +v -2.38336 0.83883 -0.05000 +v -2.27600 0.74936 -0.05000 +v -2.27290 0.75013 -0.05000 +v -2.23961 0.85001 -0.05000 +v -2.07985 1.49904 -0.05000 +v -1.65996 3.03865 -0.05000 +v -1.64443 3.10852 -0.05000 +v -1.64583 3.10991 -0.05000 +v -2.55292 2.26072 -0.49000 +v -2.82007 1.27126 -0.49000 +v -2.84544 1.16135 -0.49000 +v -2.81200 1.12791 -0.49000 +v -2.38336 0.83883 -0.49000 +v -2.27600 0.74936 -0.49000 +v -2.27290 0.75013 -0.49000 +v -2.23961 0.85001 -0.49000 +v -2.07985 1.49904 -0.49000 +v -1.65996 3.03865 -0.49000 +v -1.64443 3.10852 -0.49000 +v -1.64583 3.10991 -0.49000 +v -2.57965 2.27679 -0.46000 +v -2.84918 1.27854 -0.46000 +v -2.87841 1.15189 -0.46000 +v -2.83117 1.10465 -0.46000 +v -2.40139 0.81480 -0.46000 +v -2.28350 0.71656 -0.46000 +v -2.24965 0.72502 -0.46000 +v -2.21077 0.84167 -0.46000 +v -2.05081 1.49151 -0.46000 +v -1.63083 3.03144 -0.46000 +v -1.61157 3.11809 -0.46000 +v -1.64514 3.15165 -0.46000 +v -2.57965 2.27679 -0.45000 +v -2.84918 1.27854 -0.45000 +v -2.87841 1.15189 -0.45000 +v -2.83117 1.10465 -0.45000 +v -2.40139 0.81480 -0.45000 +v -2.28350 0.71656 -0.45000 +v -2.24965 0.72502 -0.45000 +v -2.21077 0.84167 -0.45000 +v -2.05081 1.49151 -0.45000 +v -1.63083 3.03144 -0.45000 +v -1.61157 3.11809 -0.45000 +v -1.64514 3.15165 -0.45000 +v -1.59961 3.09691 -0.09000 +v -2.20929 0.76812 -0.09000 +v -2.23028 0.71565 -0.09000 +v -2.26487 0.69836 -0.09000 +v -2.23486 0.66834 -0.09000 +v -2.08923 0.80426 -0.09000 +v -0.95231 2.00102 -0.09000 +v -1.51989 3.08641 -0.09000 +v -1.56500 3.13151 -0.09000 +v -1.59961 3.09691 -0.06500 +v -2.20929 0.76812 -0.06500 +v -2.23028 0.71565 -0.06500 +v -2.26487 0.69836 -0.06500 +v -2.23486 0.66834 -0.06500 +v -2.08923 0.80426 -0.06500 +v -0.95231 2.00102 -0.06500 +v -1.51989 3.08641 -0.06500 +v -1.56500 3.13151 -0.06500 +v -1.58613 3.08918 -0.05000 +v -2.19502 0.76342 -0.05000 +v -2.21847 0.70479 -0.05000 +v -2.23955 0.69425 -0.05000 +v -2.23450 0.68920 -0.05000 +v -2.09979 0.81492 -0.05000 +v -0.97057 2.00358 -0.05000 +v -1.53212 3.07742 -0.05000 +v -1.56500 3.11030 -0.05000 +v -1.58613 3.08918 -0.49000 +v -2.19502 0.76342 -0.49000 +v -2.21847 0.70479 -0.49000 +v -2.23955 0.69425 -0.49000 +v -2.23450 0.68920 -0.49000 +v -2.09979 0.81492 -0.49000 +v -0.97057 2.00358 -0.49000 +v -1.53212 3.07742 -0.49000 +v -1.56500 3.11030 -0.49000 +v -1.59961 3.09691 -0.47500 +v -2.20929 0.76812 -0.47500 +v -2.23028 0.71565 -0.47500 +v -2.26487 0.69836 -0.47500 +v -2.23486 0.66834 -0.47500 +v -2.08923 0.80426 -0.47500 +v -0.95231 2.00102 -0.47500 +v -1.51989 3.08641 -0.47500 +v -1.56500 3.13151 -0.47500 +v -1.59961 3.09691 -0.45000 +v -2.20929 0.76812 -0.45000 +v -2.23028 0.71565 -0.45000 +v -2.26487 0.69836 -0.45000 +v -2.23486 0.66834 -0.45000 +v -2.08923 0.80426 -0.45000 +v -0.95231 2.00102 -0.45000 +v -1.51989 3.08641 -0.45000 +v -1.56500 3.13151 -0.45000 +v -0.89500 7.14000 -0.29000 +v 0.02500 6.23000 -0.29000 +v 1.39500 6.18000 -0.29000 +v 1.44500 6.22000 -0.29000 +v -0.85500 7.18000 -0.29000 +v -0.90989 7.13996 -0.26000 +v 0.02053 6.21966 -0.26000 +v 1.39851 6.16936 -0.26000 +v 1.46539 6.22287 -0.26000 +v -0.85745 7.19240 -0.26000 +v -0.89500 7.14000 -0.23000 +v 0.02500 6.23000 -0.23000 +v 1.39500 6.18000 -0.23000 +v 1.44500 6.22000 -0.23000 +v -0.85500 7.18000 -0.23000 +v -0.88649 7.14002 -0.23000 +v 0.02756 6.23591 -0.23000 +v 1.39299 6.18608 -0.23000 +v 1.43551 6.22009 -0.23000 +v 1.38188 6.26479 -0.23000 +v -0.85357 7.17294 -0.23000 +v -0.88649 7.14002 -0.20500 +v 0.02756 6.23591 -0.20500 +v 1.39299 6.18608 -0.20500 +v 1.43551 6.22009 -0.20500 +v 1.38188 6.26479 -0.20500 +v -0.85357 7.17294 -0.20500 +v -0.86522 7.14008 -0.19000 +v 0.03395 6.25069 -0.19000 +v 1.38797 6.20127 -0.19000 +v 1.41180 6.22033 -0.19000 +v 1.37407 6.25177 -0.19000 +v -0.85000 7.15530 -0.19000 +v -0.86522 7.14008 -0.33000 +v 0.03395 6.25069 -0.33000 +v 1.38797 6.20127 -0.33000 +v 1.41180 6.22033 -0.33000 +v 1.37407 6.25177 -0.33000 +v -0.85000 7.15530 -0.33000 +v -0.88649 7.14002 -0.31500 +v 0.02756 6.23591 -0.31500 +v 1.39299 6.18608 -0.31500 +v 1.43551 6.22009 -0.31500 +v 1.38188 6.26479 -0.31500 +v -0.85357 7.17294 -0.31500 +v -0.88649 7.14002 -0.29000 +v 0.02756 6.23591 -0.29000 +v 1.39299 6.18608 -0.29000 +v 1.43551 6.22009 -0.29000 +v 1.38188 6.26479 -0.29000 +v -0.85357 7.17294 -0.29000 +v 3.54500 1.01000 0.07000 +v 2.45500 0.95000 0.07000 +v 2.41500 0.91000 0.07000 +v 2.45500 0.84000 0.07000 +v 2.34500 0.73000 0.07000 +v 1.88500 0.73000 0.07000 +v 1.84500 0.69000 0.07000 +v 2.96500 0.05000 0.07000 +v 3.07500 0.16000 0.07000 +v 4.35500 0.09000 0.07000 +v 4.39500 0.13000 0.07000 +v 4.35500 0.18000 0.07000 +v 3.57500 1.04000 0.07000 +v 3.52797 1.04812 0.29000 +v 2.43797 0.98812 0.29000 +v 2.36636 0.91651 0.29000 +v 2.40636 0.84651 0.29000 +v 2.32885 0.76900 0.29000 +v 1.86885 0.76900 0.29000 +v 1.78132 0.68147 0.29000 +v 2.97151 0.00136 0.29000 +v 3.09026 0.12011 0.29000 +v 4.37026 0.05011 0.29000 +v 4.44726 0.12711 0.29000 +v 4.38470 0.20531 0.29000 +v 3.57638 1.09653 0.29000 +v 3.54500 1.01000 0.51000 +v 2.45500 0.95000 0.51000 +v 2.41500 0.91000 0.51000 +v 2.45500 0.84000 0.51000 +v 2.34500 0.73000 0.51000 +v 1.88500 0.73000 0.51000 +v 1.84500 0.69000 0.51000 +v 2.96500 0.05000 0.51000 +v 3.07500 0.16000 0.51000 +v 4.35500 0.09000 0.51000 +v 4.39500 0.13000 0.51000 +v 4.35500 0.18000 0.51000 +v 3.57500 1.04000 0.51000 +v 2.45765 0.94416 0.51000 +v 2.42248 0.90900 0.51000 +v 2.46223 0.83944 0.51000 +v 2.42432 0.79205 0.51000 +v 2.51834 0.74504 0.51000 +v 2.58957 0.68399 0.51000 +v 2.89827 0.20600 0.51000 +v 2.92210 0.20600 0.51000 +v 3.52857 0.97155 0.51000 +v 3.52015 1.01367 0.51000 +v 2.45765 0.94416 0.53500 +v 2.42248 0.90900 0.53500 +v 2.46223 0.83944 0.53500 +v 2.42432 0.79205 0.53500 +v 2.51834 0.74504 0.53500 +v 2.58957 0.68399 0.53500 +v 2.89827 0.20600 0.53500 +v 2.92210 0.20600 0.53500 +v 3.52857 0.97155 0.53500 +v 3.52015 1.01367 0.53500 +v 2.46426 0.92956 0.55000 +v 2.44119 0.90649 0.55000 +v 2.48031 0.83803 0.55000 +v 2.44763 0.79717 0.55000 +v 2.52670 0.75763 0.55000 +v 2.60098 0.69396 0.55000 +v 2.90644 0.22100 0.55000 +v 2.91485 0.22100 0.55000 +v 3.51250 0.97542 0.55000 +v 3.50802 0.99784 0.55000 +v 2.46426 0.92956 0.03000 +v 2.44119 0.90649 0.03000 +v 2.48031 0.83803 0.03000 +v 2.44763 0.79717 0.03000 +v 2.52670 0.75763 0.03000 +v 2.60098 0.69396 0.03000 +v 2.90644 0.22100 0.03000 +v 2.91485 0.22100 0.03000 +v 3.51250 0.97542 0.03000 +v 3.50802 0.99784 0.03000 +v 2.45765 0.94416 0.04500 +v 2.42248 0.90900 0.04500 +v 2.46223 0.83944 0.04500 +v 2.42432 0.79205 0.04500 +v 2.51834 0.74504 0.04500 +v 2.58957 0.68399 0.04500 +v 2.89827 0.20600 0.04500 +v 2.92210 0.20600 0.04500 +v 3.52857 0.97155 0.04500 +v 3.52015 1.01367 0.04500 +v 2.45765 0.94416 0.07000 +v 2.42248 0.90900 0.07000 +v 2.46223 0.83944 0.07000 +v 2.42432 0.79205 0.07000 +v 2.51834 0.74504 0.07000 +v 2.58957 0.68399 0.07000 +v 2.89827 0.20600 0.07000 +v 2.92210 0.20600 0.07000 +v 3.52857 0.97155 0.07000 +v 3.52015 1.01367 0.07000 +v 2.34749 0.72400 0.51000 +v 1.88749 0.72400 0.51000 +v 1.85405 0.69056 0.51000 +v 1.93831 0.62502 0.51000 +v 2.96395 0.05744 0.51000 +v 3.02503 0.11852 0.51000 +v 2.88080 0.19544 0.51000 +v 2.57066 0.67566 0.51000 +v 2.40581 0.78233 0.51000 +v 2.34749 0.72400 0.52000 +v 1.88749 0.72400 0.52000 +v 1.85405 0.69056 0.52000 +v 1.93831 0.62502 0.52000 +v 2.96395 0.05744 0.52000 +v 3.02503 0.11852 0.52000 +v 2.88080 0.19544 0.52000 +v 2.57066 0.67566 0.52000 +v 2.40581 0.78233 0.52000 +v 2.35991 0.69400 0.55000 +v 1.89991 0.69400 0.55000 +v 1.89929 0.69338 0.55000 +v 1.95489 0.65014 0.55000 +v 2.95871 0.09462 0.55000 +v 2.97519 0.11110 0.55000 +v 2.85982 0.17263 0.55000 +v 2.54895 0.65398 0.55000 +v 2.40988 0.74397 0.55000 +v 2.35991 0.69400 0.03000 +v 1.89991 0.69400 0.03000 +v 1.89929 0.69338 0.03000 +v 1.95489 0.65014 0.03000 +v 2.95871 0.09462 0.03000 +v 2.97519 0.11110 0.03000 +v 2.85982 0.17263 0.03000 +v 2.54895 0.65398 0.03000 +v 2.40988 0.74397 0.03000 +v 2.34749 0.72400 0.06000 +v 1.88749 0.72400 0.06000 +v 1.85405 0.69056 0.06000 +v 1.93831 0.62502 0.06000 +v 2.96395 0.05744 0.06000 +v 3.02503 0.11852 0.06000 +v 2.88080 0.19544 0.06000 +v 2.57066 0.67566 0.06000 +v 2.40581 0.78233 0.06000 +v 2.34749 0.72400 0.07000 +v 1.88749 0.72400 0.07000 +v 1.85405 0.69056 0.07000 +v 1.93831 0.62502 0.07000 +v 2.96395 0.05744 0.07000 +v 3.02503 0.11852 0.07000 +v 2.88080 0.19544 0.07000 +v 2.57066 0.67566 0.07000 +v 2.40581 0.78233 0.07000 +v 3.55100 1.00751 0.51000 +v 3.55100 0.96800 0.51000 +v 3.51974 0.92632 0.51000 +v 2.94100 0.20788 0.51000 +v 2.94100 0.19321 0.51000 +v 3.02682 0.13600 0.51000 +v 3.04251 0.13600 0.51000 +v 3.07251 0.16600 0.51000 +v 3.14517 0.16600 0.51000 +v 4.35266 0.09615 0.51000 +v 4.38696 0.13045 0.51000 +v 4.35043 0.17611 0.51000 +v 3.57479 1.03130 0.51000 +v 3.55100 1.00751 0.53500 +v 3.55100 0.96800 0.53500 +v 3.51974 0.92632 0.53500 +v 2.94100 0.20788 0.53500 +v 2.94100 0.19321 0.53500 +v 3.02682 0.13600 0.53500 +v 3.04251 0.13600 0.53500 +v 3.07251 0.16600 0.53500 +v 3.14517 0.16600 0.53500 +v 4.35266 0.09615 0.53500 +v 4.38696 0.13045 0.53500 +v 4.35043 0.17611 0.53500 +v 3.57479 1.03130 0.53500 +v 3.56600 1.00130 0.55000 +v 3.56600 0.96300 0.55000 +v 3.53158 0.91711 0.55000 +v 2.95600 0.20259 0.55000 +v 2.95600 0.20124 0.55000 +v 3.03136 0.15100 0.55000 +v 3.03630 0.15100 0.55000 +v 3.06630 0.18100 0.55000 +v 3.14561 0.18100 0.55000 +v 4.34681 0.11151 0.55000 +v 4.36686 0.13156 0.55000 +v 4.33901 0.16637 0.55000 +v 3.57426 1.00956 0.55000 +v 3.56600 1.00130 0.03000 +v 3.56600 0.96300 0.03000 +v 3.53158 0.91711 0.03000 +v 2.95600 0.20259 0.03000 +v 2.95600 0.20124 0.03000 +v 3.03136 0.15100 0.03000 +v 3.03630 0.15100 0.03000 +v 3.06630 0.18100 0.03000 +v 3.14561 0.18100 0.03000 +v 4.34681 0.11151 0.03000 +v 4.36686 0.13156 0.03000 +v 4.33901 0.16637 0.03000 +v 3.57426 1.00956 0.03000 +v 3.55100 1.00751 0.04500 +v 3.55100 0.96800 0.04500 +v 3.51974 0.92632 0.04500 +v 2.94100 0.20788 0.04500 +v 2.94100 0.19321 0.04500 +v 3.02682 0.13600 0.04500 +v 3.04251 0.13600 0.04500 +v 3.07251 0.16600 0.04500 +v 3.14517 0.16600 0.04500 +v 4.35266 0.09615 0.04500 +v 4.38696 0.13045 0.04500 +v 4.35043 0.17611 0.04500 +v 3.57479 1.03130 0.04500 +v 3.55100 1.00751 0.07000 +v 3.55100 0.96800 0.07000 +v 3.51974 0.92632 0.07000 +v 2.94100 0.20788 0.07000 +v 2.94100 0.19321 0.07000 +v 3.02682 0.13600 0.07000 +v 3.04251 0.13600 0.07000 +v 3.07251 0.16600 0.07000 +v 3.14517 0.16600 0.07000 +v 4.35266 0.09615 0.07000 +v 4.38696 0.13045 0.07000 +v 4.35043 0.17611 0.07000 +v 3.57479 1.03130 0.07000 +v -2.58500 2.28000 0.09000 +v -2.88500 1.16000 0.09000 +v -2.23500 0.66000 0.09000 +v -0.94500 2.00000 0.09000 +v -1.51500 3.09000 0.09000 +v -1.64500 3.16000 0.09000 +v -2.61444 2.29765 0.27000 +v -2.92257 1.14727 0.27000 +v -2.23171 0.61584 0.27000 +v -0.90474 1.99423 0.27000 +v -1.49045 3.11426 0.27000 +v -1.65024 3.20030 0.27000 +v -2.58500 2.28000 0.45000 +v -2.88500 1.16000 0.45000 +v -2.23500 0.66000 0.45000 +v -0.94500 2.00000 0.45000 +v -1.51500 3.09000 0.45000 +v -1.64500 3.16000 0.45000 +v -2.57965 2.27679 0.45000 +v -2.84918 1.27854 0.45000 +v -2.87841 1.15189 0.45000 +v -2.83117 1.10465 0.45000 +v -2.40139 0.81480 0.45000 +v -2.28350 0.71656 0.45000 +v -2.24965 0.72502 0.45000 +v -2.21077 0.84167 0.45000 +v -2.05081 1.49151 0.45000 +v -1.63083 3.03144 0.45000 +v -1.61157 3.11809 0.45000 +v -1.64514 3.15165 0.45000 +v -2.57965 2.27679 0.46000 +v -2.84918 1.27854 0.46000 +v -2.87841 1.15189 0.46000 +v -2.83117 1.10465 0.46000 +v -2.40139 0.81480 0.46000 +v -2.28350 0.71656 0.46000 +v -2.24965 0.72502 0.46000 +v -2.21077 0.84167 0.46000 +v -2.05081 1.49151 0.46000 +v -1.63083 3.03144 0.46000 +v -1.61157 3.11809 0.46000 +v -1.64514 3.15165 0.46000 +v -2.55292 2.26072 0.49000 +v -2.82007 1.27126 0.49000 +v -2.84544 1.16135 0.49000 +v -2.81200 1.12791 0.49000 +v -2.38336 0.83883 0.49000 +v -2.27600 0.74936 0.49000 +v -2.27290 0.75013 0.49000 +v -2.23961 0.85001 0.49000 +v -2.07985 1.49904 0.49000 +v -1.65996 3.03865 0.49000 +v -1.64443 3.10852 0.49000 +v -1.64583 3.10991 0.49000 +v -2.55292 2.26072 0.05000 +v -2.82007 1.27126 0.05000 +v -2.84544 1.16135 0.05000 +v -2.81200 1.12791 0.05000 +v -2.38336 0.83883 0.05000 +v -2.27600 0.74936 0.05000 +v -2.27290 0.75013 0.05000 +v -2.23961 0.85001 0.05000 +v -2.07985 1.49904 0.05000 +v -1.65996 3.03865 0.05000 +v -1.64443 3.10852 0.05000 +v -1.64583 3.10991 0.05000 +v -2.57965 2.27679 0.08000 +v -2.84918 1.27854 0.08000 +v -2.87841 1.15189 0.08000 +v -2.83117 1.10465 0.08000 +v -2.40139 0.81480 0.08000 +v -2.28350 0.71656 0.08000 +v -2.24965 0.72502 0.08000 +v -2.21077 0.84167 0.08000 +v -2.05081 1.49151 0.08000 +v -1.63083 3.03144 0.08000 +v -1.61157 3.11809 0.08000 +v -1.64514 3.15165 0.08000 +v -2.57965 2.27679 0.09000 +v -2.84918 1.27854 0.09000 +v -2.87841 1.15189 0.09000 +v -2.83117 1.10465 0.09000 +v -2.40139 0.81480 0.09000 +v -2.28350 0.71656 0.09000 +v -2.24965 0.72502 0.09000 +v -2.21077 0.84167 0.09000 +v -2.05081 1.49151 0.09000 +v -1.63083 3.03144 0.09000 +v -1.61157 3.11809 0.09000 +v -1.64514 3.15165 0.09000 +v -1.59961 3.09691 0.45000 +v -2.20929 0.76812 0.45000 +v -2.23028 0.71565 0.45000 +v -2.26487 0.69836 0.45000 +v -2.23486 0.66834 0.45000 +v -2.08923 0.80426 0.45000 +v -0.95231 2.00102 0.45000 +v -1.51989 3.08641 0.45000 +v -1.56500 3.13151 0.45000 +v -1.59961 3.09691 0.47500 +v -2.20929 0.76812 0.47500 +v -2.23028 0.71565 0.47500 +v -2.26487 0.69836 0.47500 +v -2.23486 0.66834 0.47500 +v -2.08923 0.80426 0.47500 +v -0.95231 2.00102 0.47500 +v -1.51989 3.08641 0.47500 +v -1.56500 3.13151 0.47500 +v -1.58613 3.08918 0.49000 +v -2.19502 0.76342 0.49000 +v -2.21847 0.70479 0.49000 +v -2.23955 0.69425 0.49000 +v -2.23450 0.68920 0.49000 +v -2.09979 0.81492 0.49000 +v -0.97057 2.00358 0.49000 +v -1.53212 3.07742 0.49000 +v -1.56500 3.11030 0.49000 +v -1.58613 3.08918 0.05000 +v -2.19502 0.76342 0.05000 +v -2.21847 0.70479 0.05000 +v -2.23955 0.69425 0.05000 +v -2.23450 0.68920 0.05000 +v -2.09979 0.81492 0.05000 +v -0.97057 2.00358 0.05000 +v -1.53212 3.07742 0.05000 +v -1.56500 3.11030 0.05000 +v -1.59961 3.09691 0.06500 +v -2.20929 0.76812 0.06500 +v -2.23028 0.71565 0.06500 +v -2.26487 0.69836 0.06500 +v -2.23486 0.66834 0.06500 +v -2.08923 0.80426 0.06500 +v -0.95231 2.00102 0.06500 +v -1.51989 3.08641 0.06500 +v -1.56500 3.13151 0.06500 +v -1.59961 3.09691 0.09000 +v -2.20929 0.76812 0.09000 +v -2.23028 0.71565 0.09000 +v -2.26487 0.69836 0.09000 +v -2.23486 0.66834 0.09000 +v -2.08923 0.80426 0.09000 +v -0.95231 2.00102 0.09000 +v -1.51989 3.08641 0.09000 +v -1.56500 3.13151 0.09000 +v -0.89500 7.14000 0.23000 +v 0.02500 6.23000 0.23000 +v 1.39500 6.18000 0.23000 +v 1.44500 6.22000 0.23000 +v -0.85500 7.18000 0.23000 +v -0.90989 7.13996 0.26000 +v 0.02053 6.21966 0.26000 +v 1.39851 6.16936 0.26000 +v 1.46539 6.22287 0.26000 +v -0.85745 7.19240 0.26000 +v -0.89500 7.14000 0.29000 +v 0.02500 6.23000 0.29000 +v 1.39500 6.18000 0.29000 +v 1.44500 6.22000 0.29000 +v -0.85500 7.18000 0.29000 +v -0.88649 7.14002 0.29000 +v 0.02756 6.23591 0.29000 +v 1.39299 6.18608 0.29000 +v 1.43551 6.22009 0.29000 +v 1.38188 6.26479 0.29000 +v -0.85357 7.17294 0.29000 +v -0.88649 7.14002 0.31500 +v 0.02756 6.23591 0.31500 +v 1.39299 6.18608 0.31500 +v 1.43551 6.22009 0.31500 +v 1.38188 6.26479 0.31500 +v -0.85357 7.17294 0.31500 +v -0.86522 7.14008 0.33000 +v 0.03395 6.25069 0.33000 +v 1.38797 6.20127 0.33000 +v 1.41180 6.22033 0.33000 +v 1.37407 6.25177 0.33000 +v -0.85000 7.15530 0.33000 +v -0.86522 7.14008 0.19000 +v 0.03395 6.25069 0.19000 +v 1.38797 6.20127 0.19000 +v 1.41180 6.22033 0.19000 +v 1.37407 6.25177 0.19000 +v -0.85000 7.15530 0.19000 +v -0.88649 7.14002 0.20500 +v 0.02756 6.23591 0.20500 +v 1.39299 6.18608 0.20500 +v 1.43551 6.22009 0.20500 +v 1.38188 6.26479 0.20500 +v -0.85357 7.17294 0.20500 +v -0.88649 7.14002 0.23000 +v 0.02756 6.23591 0.23000 +v 1.39299 6.18608 0.23000 +v 1.43551 6.22009 0.23000 +v 1.38188 6.26479 0.23000 +v -0.85357 7.17294 0.23000 +v 0.12500 7.39000 -0.03000 +v 0.62500 6.67000 -0.03000 +v 1.38500 6.60000 -0.03000 +v 1.43500 6.64000 -0.03000 +v 0.15500 7.45000 -0.03000 +v 0.11282 7.38913 0.00000 +v 0.61917 6.65999 0.00000 +v 1.38825 6.58916 0.00000 +v 1.45306 6.64100 0.00000 +v 0.15080 7.46508 0.00000 +v 0.12500 7.39000 0.03000 +v 0.62500 6.67000 0.03000 +v 1.38500 6.60000 0.03000 +v 1.43500 6.64000 0.03000 +v 0.15500 7.45000 0.03000 +v 0.12230 7.40882 0.03000 +v 0.13019 7.39303 0.03000 +v 0.57966 6.72383 0.03000 +v 0.62771 6.67578 0.03000 +v 1.38314 6.60620 0.03000 +v 1.42540 6.64000 0.03000 +v 1.33153 6.71509 0.03000 +v 0.15588 7.44240 0.03000 +v 0.12230 7.40882 0.05500 +v 0.13019 7.39303 0.05500 +v 0.57966 6.72383 0.05500 +v 0.62771 6.67578 0.05500 +v 1.38314 6.60620 0.05500 +v 1.42540 6.64000 0.05500 +v 1.33153 6.71509 0.05500 +v 0.15588 7.44240 0.05500 +v 0.14055 7.40585 0.07000 +v 0.14318 7.40059 0.07000 +v 0.59129 6.73340 0.07000 +v 0.63448 6.69022 0.07000 +v 1.37849 6.62169 0.07000 +v 1.40138 6.64000 0.07000 +v 1.32287 6.70281 0.07000 +v 0.15809 7.42339 0.07000 +v 0.14055 7.40585 -0.07000 +v 0.14318 7.40059 -0.07000 +v 0.59129 6.73340 -0.07000 +v 0.63448 6.69022 -0.07000 +v 1.37849 6.62169 -0.07000 +v 1.40138 6.64000 -0.07000 +v 1.32287 6.70281 -0.07000 +v 0.15809 7.42339 -0.07000 +v 0.12230 7.40882 -0.05500 +v 0.13019 7.39303 -0.05500 +v 0.57966 6.72383 -0.05500 +v 0.62771 6.67578 -0.05500 +v 1.38314 6.60620 -0.05500 +v 1.42540 6.64000 -0.05500 +v 1.33153 6.71509 -0.05500 +v 0.15588 7.44240 -0.05500 +v 0.12230 7.40882 -0.03000 +v 0.13019 7.39303 -0.03000 +v 0.57966 6.72383 -0.03000 +v 0.62771 6.67578 -0.03000 +v 1.38314 6.60620 -0.03000 +v 1.42540 6.64000 -0.03000 +v 1.33153 6.71509 -0.03000 +v 0.15588 7.44240 -0.03000 +v -2.82064 6.36000 -2.10075 +v -2.80252 6.33000 -2.09230 +v -2.04122 5.28000 -1.73730 +v -2.14091 5.17000 -1.78379 +v -2.96565 5.51000 -2.16837 +v -3.01097 5.47000 -2.18950 +v -1.71495 3.61000 -1.58516 +v 0.09767 3.18000 -0.73992 +v 0.64145 3.11000 -0.48635 +v 0.67770 3.15000 -0.46945 +v -0.31923 4.99000 -0.93433 +v -0.29204 5.02000 -0.92165 +v -0.32830 5.06000 -0.93855 +v -2.78439 6.40000 -2.08385 +v -2.84557 6.36134 -2.07928 +v -2.82288 6.32380 -2.06870 +v -2.06665 5.28078 -1.71606 +v -2.15599 5.18220 -1.75772 +v -2.98006 5.52192 -2.14199 +v -3.03688 5.47177 -2.16849 +v -1.73304 3.60054 -1.56050 +v 0.08343 3.16963 -0.71346 +v 0.63225 3.09898 -0.45754 +v 0.67700 3.14836 -0.43668 +v -0.31994 4.98836 -0.90156 +v -0.29127 5.02000 -0.88818 +v -0.33535 5.06865 -0.90874 +v -2.79897 6.41275 -2.05755 +v -2.84600 6.36000 -2.04638 +v -2.82787 6.33000 -2.03792 +v -2.06658 5.28000 -1.68292 +v -2.16627 5.17000 -1.72941 +v -2.99101 5.51000 -2.11399 +v -3.03633 5.47000 -2.13513 +v -1.74031 3.61000 -1.53078 +v 0.07231 3.18000 -0.68555 +v 0.61610 3.11000 -0.43197 +v 0.65235 3.15000 -0.41507 +v -0.34459 4.99000 -0.87995 +v -0.31740 5.02000 -0.86727 +v -0.35365 5.06000 -0.88418 +v -2.80975 6.40000 -2.02947 +v -2.83900 6.35924 -2.04311 +v -2.82348 6.33354 -2.03587 +v -2.05929 5.27955 -1.67953 +v -2.10370 5.23055 -1.70024 +v -1.90063 5.05519 -1.60554 +v -0.96738 4.60531 -1.17036 +v -0.86867 4.53600 -1.12433 +v -0.84471 4.53600 -1.11316 +v -0.79194 4.57481 -1.08855 +v -0.35756 4.97422 -0.88600 +v -0.32469 5.01955 -0.87067 +v -0.35687 5.05506 -0.88567 +v -2.80866 6.39271 -2.02896 +v -2.84323 6.35924 -2.03405 +v -2.82771 6.33354 -2.02681 +v -2.06352 5.27955 -1.67046 +v -2.10793 5.23055 -1.69117 +v -1.90486 5.05519 -1.59648 +v -0.97161 4.60531 -1.16130 +v -0.87289 4.53600 -1.11527 +v -0.84893 4.53600 -1.10409 +v -0.79617 4.57481 -1.07949 +v -0.36178 4.97422 -0.87693 +v -0.32891 5.01955 -0.86161 +v -0.36109 5.05506 -0.87661 +v -2.81289 6.39271 -2.01990 +v -2.82092 6.35541 -1.99055 +v -2.81842 6.35127 -1.98938 +v -2.03976 5.27733 -1.62628 +v -2.07967 5.23329 -1.64489 +v -1.90349 5.08116 -1.56274 +v -0.97147 4.63187 -1.12813 +v -0.87765 4.56600 -1.08439 +v -0.86984 4.56600 -1.08074 +v -0.82514 4.59889 -1.05990 +v -0.39398 4.99533 -0.85884 +v -0.37803 5.01733 -0.85141 +v -0.38983 5.03036 -0.85691 +v -2.82013 6.35628 -1.99018 +v -2.76175 6.35541 -2.11743 +v -2.75925 6.35127 -2.11626 +v -1.98059 5.27733 -1.75317 +v -2.02050 5.23329 -1.77178 +v -1.84433 5.08116 -1.68963 +v -0.91230 4.63187 -1.25501 +v -0.81849 4.56600 -1.21127 +v -0.81068 4.56600 -1.20763 +v -0.76597 4.59889 -1.18678 +v -0.33481 4.99533 -0.98573 +v -0.31886 5.01733 -0.97829 +v -0.33067 5.03036 -0.98379 +v -2.76097 6.35628 -2.11706 +v -2.80942 6.35924 -2.10655 +v -2.79390 6.33354 -2.09932 +v -2.02971 5.27955 -1.74297 +v -2.07412 5.23055 -1.76368 +v -1.87105 5.05519 -1.66898 +v -0.93780 4.60531 -1.23380 +v -0.83908 4.53600 -1.18777 +v -0.81512 4.53600 -1.17660 +v -0.76236 4.57481 -1.15199 +v -0.32797 4.97422 -0.94944 +v -0.29511 5.01955 -0.93411 +v -0.32728 5.05506 -0.94912 +v -2.77908 6.39271 -2.09241 +v -2.81365 6.35924 -2.09749 +v -2.79812 6.33354 -2.09025 +v -2.03393 5.27955 -1.73390 +v -2.07835 5.23055 -1.75462 +v -1.87527 5.05519 -1.65992 +v -0.94202 4.60531 -1.22474 +v -0.84331 4.53600 -1.17871 +v -0.81935 4.53600 -1.16754 +v -0.76658 4.57481 -1.14293 +v -0.33220 4.97422 -0.94037 +v -0.29933 5.01955 -0.92505 +v -0.33151 5.05506 -0.94005 +v -2.78330 6.39271 -2.08334 +v -3.02933 5.46924 -2.13186 +v -3.01376 5.44347 -2.12460 +v -1.84495 3.73395 -1.57958 +v -1.78532 3.66816 -1.55177 +v -0.88608 4.51152 -1.13245 +v -0.90149 4.54553 -1.13964 +v -0.97225 4.58456 -1.17263 +v -1.90625 5.03481 -1.60816 +v -2.11178 5.21229 -1.70401 +v -2.16520 5.16317 -1.72892 +v -2.99869 5.50304 -2.11758 +v -3.03355 5.46924 -2.12280 +v -3.01799 5.44347 -2.11554 +v -1.84918 3.73395 -1.57051 +v -1.78955 3.66816 -1.54271 +v -0.89031 4.51152 -1.12339 +v -0.90572 4.54553 -1.13057 +v -0.97648 4.58456 -1.16357 +v -1.91048 5.03481 -1.59910 +v -2.11601 5.21229 -1.69494 +v -2.16943 5.16317 -1.71985 +v -3.00292 5.50304 -2.10851 +v -3.01125 5.46541 -2.07930 +v -3.00848 5.46084 -2.07801 +v -1.84129 3.75368 -1.53374 +v -1.80073 3.70893 -1.51482 +v -0.93684 4.51915 -1.11199 +v -0.93866 4.52317 -1.11283 +v -1.00068 4.55738 -1.14176 +v -1.93720 5.00884 -1.57846 +v -2.12815 5.17373 -1.66750 +v -2.17678 5.12901 -1.69018 +v -3.00868 5.46824 -2.07810 +v -2.95208 5.46541 -2.20618 +v -2.94932 5.46084 -2.20489 +v -1.78212 3.75368 -1.66062 +v -1.74156 3.70893 -1.64171 +v -0.87767 4.51915 -1.23887 +v -0.87950 4.52317 -1.23972 +v -0.94152 4.55738 -1.26864 +v -1.87803 5.00884 -1.70534 +v -2.06899 5.17373 -1.79439 +v -2.11762 5.12901 -1.81706 +v -2.94952 5.46824 -2.20498 +v -2.99974 5.46924 -2.19530 +v -2.98418 5.44347 -2.18805 +v -1.81537 3.73395 -1.64302 +v -1.75574 3.66816 -1.61521 +v -0.85650 4.51152 -1.19589 +v -0.87191 4.54553 -1.20308 +v -0.94267 4.58456 -1.23607 +v -1.87667 5.03481 -1.67160 +v -2.08220 5.21229 -1.76745 +v -2.13562 5.16317 -1.79236 +v -2.96911 5.50304 -2.18102 +v -3.00397 5.46924 -2.18624 +v -2.98841 5.44347 -2.17898 +v -1.81959 3.73395 -1.63396 +v -1.75996 3.66816 -1.60615 +v -0.86073 4.51152 -1.18683 +v -0.87613 4.54553 -1.19402 +v -0.94689 4.58456 -1.22701 +v -1.88089 5.03481 -1.66254 +v -2.08643 5.21229 -1.75838 +v -2.13985 5.16317 -1.78329 +v -2.97333 5.50304 -2.17195 +v -0.78542 4.55519 -1.08551 +v -0.84141 4.51400 -1.11162 +v -0.86800 4.51400 -1.12402 +v -0.97534 4.39556 -1.17407 +v -1.72738 3.70574 -1.52476 +v -1.76920 3.65037 -1.54426 +v -1.73759 3.61549 -1.52952 +v 0.02790 3.19592 -0.70625 +v 0.61413 3.11631 -0.43289 +v 0.64573 3.15118 -0.41815 +v 0.62944 3.18714 -0.42575 +v -0.26752 4.87659 -0.84401 +v -0.33630 4.97145 -0.87608 +v -0.78964 4.55519 -1.07645 +v -0.84564 4.51400 -1.10256 +v -0.87222 4.51400 -1.11495 +v -0.97956 4.39556 -1.16501 +v -1.73161 3.70574 -1.51569 +v -1.77343 3.65037 -1.53519 +v -1.74182 3.61549 -1.52045 +v 0.02368 3.19592 -0.69719 +v 0.60990 3.11631 -0.42383 +v 0.64150 3.15118 -0.40909 +v 0.62521 3.18714 -0.41669 +v -0.27174 4.87659 -0.83495 +v -0.34052 4.97145 -0.86702 +v -0.78598 4.53115 -1.04164 +v -0.85009 4.48400 -1.07153 +v -0.87364 4.48400 -1.08251 +v -0.97390 4.37337 -1.12927 +v -1.72499 3.68442 -1.47951 +v -1.74932 3.65221 -1.49085 +v -1.74094 3.64296 -1.48694 +v 0.01553 3.22553 -0.66789 +v 0.58738 3.14788 -0.40123 +v 0.59574 3.15711 -0.39733 +v 0.58862 3.17283 -0.40065 +v -0.30688 4.85955 -0.81823 +v -0.35703 4.92872 -0.84162 +v -0.72682 4.53115 -1.16852 +v -0.79092 4.48400 -1.19841 +v -0.81447 4.48400 -1.20940 +v -0.91473 4.37337 -1.25615 +v -1.66583 3.68442 -1.60639 +v -1.69016 3.65221 -1.61773 +v -1.68177 3.64296 -1.61383 +v 0.07470 3.22553 -0.79477 +v 0.64654 3.14788 -0.52811 +v 0.65491 3.15711 -0.52421 +v 0.64778 3.17283 -0.52754 +v -0.24772 4.85955 -0.94511 +v -0.29787 4.92872 -0.96850 +v -0.75583 4.55519 -1.14895 +v -0.81183 4.51400 -1.17506 +v -0.83841 4.51400 -1.18746 +v -0.94575 4.39556 -1.23751 +v -1.69780 3.70574 -1.58820 +v -1.73962 3.65037 -1.60770 +v -1.70801 3.61549 -1.59296 +v 0.05748 3.19592 -0.76969 +v 0.64371 3.11631 -0.49633 +v 0.67531 3.15118 -0.48160 +v 0.65902 3.18714 -0.48919 +v -0.23793 4.87659 -0.90745 +v -0.30671 4.97145 -0.93952 +v -0.76006 4.55519 -1.13989 +v -0.81606 4.51400 -1.16600 +v -0.84264 4.51400 -1.17840 +v -0.94998 4.39556 -1.22845 +v -1.70203 3.70574 -1.57913 +v -1.74384 3.65037 -1.59863 +v -1.71224 3.61549 -1.58390 +v 0.05326 3.19592 -0.76063 +v 0.63948 3.11631 -0.48727 +v 0.67109 3.15118 -0.47253 +v 0.65480 3.18714 -0.48013 +v -0.24216 4.87659 -0.89839 +v -0.31094 4.97145 -0.93046 +v -2.84600 6.36000 2.04638 +v -2.82787 6.33000 2.03792 +v -2.06658 5.28000 1.68292 +v -2.16627 5.17000 1.72941 +v -2.99101 5.51000 2.11399 +v -3.03633 5.47000 2.13513 +v -1.74031 3.61000 1.53078 +v 0.07231 3.18000 0.68555 +v 0.61610 3.11000 0.43197 +v 0.65235 3.15000 0.41507 +v -0.34459 4.99000 0.87995 +v -0.31740 5.02000 0.86727 +v -0.35365 5.06000 0.88418 +v -2.80975 6.40000 2.02947 +v -2.84557 6.36134 2.07928 +v -2.82288 6.32380 2.06870 +v -2.06665 5.28078 1.71606 +v -2.15599 5.18220 1.75772 +v -2.98006 5.52192 2.14199 +v -3.03688 5.47177 2.16849 +v -1.73304 3.60054 1.56050 +v 0.08343 3.16963 0.71346 +v 0.63225 3.09898 0.45754 +v 0.67700 3.14836 0.43668 +v -0.31994 4.98836 0.90156 +v -0.29127 5.02000 0.88818 +v -0.33535 5.06865 0.90874 +v -2.79897 6.41275 2.05755 +v -2.82064 6.36000 2.10075 +v -2.80252 6.33000 2.09230 +v -2.04122 5.28000 1.73730 +v -2.14091 5.17000 1.78379 +v -2.96565 5.51000 2.16837 +v -3.01097 5.47000 2.18950 +v -1.71495 3.61000 1.58516 +v 0.09767 3.18000 0.73992 +v 0.64145 3.11000 0.48635 +v 0.67770 3.15000 0.46945 +v -0.31923 4.99000 0.93433 +v -0.29204 5.02000 0.92165 +v -0.32830 5.06000 0.93855 +v -2.78439 6.40000 2.08385 +v -2.81365 6.35924 2.09749 +v -2.79812 6.33354 2.09025 +v -2.03393 5.27955 1.73390 +v -2.07835 5.23055 1.75462 +v -1.87527 5.05519 1.65992 +v -0.94202 4.60531 1.22474 +v -0.84331 4.53600 1.17871 +v -0.81935 4.53600 1.16754 +v -0.76658 4.57481 1.14293 +v -0.33220 4.97422 0.94037 +v -0.29933 5.01955 0.92505 +v -0.33151 5.05506 0.94005 +v -2.78330 6.39271 2.08334 +v -2.80942 6.35924 2.10655 +v -2.79390 6.33354 2.09932 +v -2.02971 5.27955 1.74297 +v -2.07412 5.23055 1.76368 +v -1.87105 5.05519 1.66898 +v -0.93780 4.60531 1.23380 +v -0.83908 4.53600 1.18777 +v -0.81512 4.53600 1.17660 +v -0.76236 4.57481 1.15199 +v -0.32797 4.97422 0.94944 +v -0.29511 5.01955 0.93411 +v -0.32728 5.05506 0.94912 +v -2.77908 6.39271 2.09241 +v -2.76175 6.35541 2.11743 +v -2.75925 6.35127 2.11626 +v -1.98059 5.27733 1.75317 +v -2.02050 5.23329 1.77178 +v -1.84433 5.08116 1.68963 +v -0.91230 4.63187 1.25501 +v -0.81849 4.56600 1.21127 +v -0.81068 4.56600 1.20763 +v -0.76597 4.59889 1.18678 +v -0.33481 4.99533 0.98573 +v -0.31886 5.01733 0.97829 +v -0.33067 5.03036 0.98379 +v -2.76097 6.35628 2.11706 +v -2.82092 6.35541 1.99055 +v -2.81842 6.35127 1.98938 +v -2.03976 5.27733 1.62628 +v -2.07967 5.23329 1.64489 +v -1.90349 5.08116 1.56274 +v -0.97147 4.63187 1.12813 +v -0.87765 4.56600 1.08439 +v -0.86984 4.56600 1.08074 +v -0.82514 4.59889 1.05990 +v -0.39398 4.99533 0.85884 +v -0.37803 5.01733 0.85141 +v -0.38983 5.03036 0.85691 +v -2.82013 6.35628 1.99018 +v -2.84323 6.35924 2.03405 +v -2.82771 6.33354 2.02681 +v -2.06352 5.27955 1.67046 +v -2.10793 5.23055 1.69117 +v -1.90486 5.05519 1.59648 +v -0.97161 4.60531 1.16130 +v -0.87289 4.53600 1.11527 +v -0.84893 4.53600 1.10409 +v -0.79617 4.57481 1.07949 +v -0.36178 4.97422 0.87693 +v -0.32891 5.01955 0.86161 +v -0.36109 5.05506 0.87661 +v -2.81289 6.39271 2.01990 +v -2.83900 6.35924 2.04311 +v -2.82348 6.33354 2.03587 +v -2.05929 5.27955 1.67953 +v -2.10370 5.23055 1.70024 +v -1.90063 5.05519 1.60554 +v -0.96738 4.60531 1.17036 +v -0.86867 4.53600 1.12433 +v -0.84471 4.53600 1.11316 +v -0.79194 4.57481 1.08855 +v -0.35756 4.97422 0.88600 +v -0.32469 5.01955 0.87067 +v -0.35687 5.05506 0.88567 +v -2.80866 6.39271 2.02896 +v -3.00397 5.46924 2.18624 +v -2.98841 5.44347 2.17898 +v -1.81959 3.73395 1.63396 +v -1.75996 3.66816 1.60615 +v -0.86073 4.51152 1.18683 +v -0.87613 4.54553 1.19402 +v -0.94689 4.58456 1.22701 +v -1.88089 5.03481 1.66254 +v -2.08643 5.21229 1.75838 +v -2.13985 5.16317 1.78329 +v -2.97333 5.50304 2.17195 +v -2.99974 5.46924 2.19530 +v -2.98418 5.44347 2.18805 +v -1.81537 3.73395 1.64302 +v -1.75574 3.66816 1.61521 +v -0.85650 4.51152 1.19589 +v -0.87191 4.54553 1.20308 +v -0.94267 4.58456 1.23607 +v -1.87667 5.03481 1.67160 +v -2.08220 5.21229 1.76745 +v -2.13562 5.16317 1.79236 +v -2.96911 5.50304 2.18102 +v -2.95208 5.46541 2.20618 +v -2.94932 5.46084 2.20489 +v -1.78212 3.75368 1.66062 +v -1.74156 3.70893 1.64171 +v -0.87767 4.51915 1.23887 +v -0.87950 4.52317 1.23972 +v -0.94152 4.55738 1.26864 +v -1.87803 5.00884 1.70534 +v -2.06899 5.17373 1.79439 +v -2.11762 5.12901 1.81706 +v -2.94952 5.46824 2.20498 +v -3.01125 5.46541 2.07930 +v -3.00848 5.46084 2.07801 +v -1.84129 3.75368 1.53374 +v -1.80073 3.70893 1.51482 +v -0.93684 4.51915 1.11199 +v -0.93866 4.52317 1.11283 +v -1.00068 4.55738 1.14176 +v -1.93720 5.00884 1.57846 +v -2.12815 5.17373 1.66750 +v -2.17678 5.12901 1.69018 +v -3.00868 5.46824 2.07810 +v -3.03355 5.46924 2.12280 +v -3.01799 5.44347 2.11554 +v -1.84918 3.73395 1.57051 +v -1.78955 3.66816 1.54271 +v -0.89031 4.51152 1.12339 +v -0.90572 4.54553 1.13057 +v -0.97648 4.58456 1.16357 +v -1.91048 5.03481 1.59910 +v -2.11601 5.21229 1.69494 +v -2.16943 5.16317 1.71985 +v -3.00292 5.50304 2.10851 +v -3.02933 5.46924 2.13186 +v -3.01376 5.44347 2.12460 +v -1.84495 3.73395 1.57958 +v -1.78532 3.66816 1.55177 +v -0.88608 4.51152 1.13245 +v -0.90149 4.54553 1.13964 +v -0.97225 4.58456 1.17263 +v -1.90625 5.03481 1.60816 +v -2.11178 5.21229 1.70401 +v -2.16520 5.16317 1.72892 +v -2.99869 5.50304 2.11758 +v -0.76006 4.55519 1.13989 +v -0.81606 4.51400 1.16600 +v -0.84264 4.51400 1.17840 +v -0.94998 4.39556 1.22845 +v -1.70203 3.70574 1.57913 +v -1.74384 3.65037 1.59863 +v -1.71224 3.61549 1.58390 +v 0.05326 3.19592 0.76063 +v 0.63948 3.11631 0.48727 +v 0.67109 3.15118 0.47253 +v 0.65480 3.18714 0.48013 +v -0.24216 4.87659 0.89839 +v -0.31094 4.97145 0.93046 +v -0.75583 4.55519 1.14895 +v -0.81183 4.51400 1.17506 +v -0.83841 4.51400 1.18746 +v -0.94575 4.39556 1.23751 +v -1.69780 3.70574 1.58820 +v -1.73962 3.65037 1.60770 +v -1.70801 3.61549 1.59296 +v 0.05748 3.19592 0.76969 +v 0.64371 3.11631 0.49633 +v 0.67531 3.15118 0.48160 +v 0.65902 3.18714 0.48919 +v -0.23793 4.87659 0.90745 +v -0.30671 4.97145 0.93952 +v -0.72682 4.53115 1.16852 +v -0.79092 4.48400 1.19841 +v -0.81447 4.48400 1.20940 +v -0.91473 4.37337 1.25615 +v -1.66583 3.68442 1.60639 +v -1.69016 3.65221 1.61773 +v -1.68177 3.64296 1.61383 +v 0.07470 3.22553 0.79477 +v 0.64654 3.14788 0.52811 +v 0.65491 3.15711 0.52421 +v 0.64778 3.17283 0.52754 +v -0.24772 4.85955 0.94511 +v -0.29787 4.92872 0.96850 +v -0.78598 4.53115 1.04164 +v -0.85009 4.48400 1.07153 +v -0.87364 4.48400 1.08251 +v -0.97390 4.37337 1.12927 +v -1.72499 3.68442 1.47951 +v -1.74932 3.65221 1.49085 +v -1.74094 3.64296 1.48694 +v 0.01553 3.22553 0.66789 +v 0.58738 3.14788 0.40123 +v 0.59574 3.15711 0.39733 +v 0.58862 3.17283 0.40065 +v -0.30688 4.85955 0.81823 +v -0.35703 4.92872 0.84162 +v -0.78964 4.55519 1.07645 +v -0.84564 4.51400 1.10256 +v -0.87222 4.51400 1.11495 +v -0.97956 4.39556 1.16501 +v -1.73161 3.70574 1.51569 +v -1.77343 3.65037 1.53519 +v -1.74182 3.61549 1.52045 +v 0.02368 3.19592 0.69719 +v 0.60990 3.11631 0.42383 +v 0.64150 3.15118 0.40909 +v 0.62521 3.18714 0.41669 +v -0.27174 4.87659 0.83495 +v -0.34052 4.97145 0.86702 +v -0.78542 4.55519 1.08551 +v -0.84141 4.51400 1.11162 +v -0.86800 4.51400 1.12402 +v -0.97534 4.39556 1.17407 +v -1.72738 3.70574 1.52476 +v -1.76920 3.65037 1.54426 +v -1.73759 3.61549 1.52952 +v 0.02790 3.19592 0.70625 +v 0.61413 3.11631 0.43289 +v 0.64573 3.15118 0.41815 +v 0.62944 3.18714 0.42575 +v -0.26752 4.87659 0.84401 +v -0.33630 4.97145 0.87608 +vn -0.6976 -0.6976 -0.1636 +vn -0.4557 -0.8750 -0.1636 +vn -0.5406 -0.8252 -0.1636 +vn -0.7084 0.6866 -0.1635 +vn 0.1971 -0.9666 -0.1636 +vn -0.6287 -0.7603 -0.1636 +vn 0.6976 -0.6976 -0.1636 +vn 0.8824 -0.4412 -0.1636 +vn 0.6976 0.6976 -0.1636 +vn -0.3664 0.9160 -0.1636 +vn 0.2186 0.9620 -0.1635 +vn 0.2047 -0.9651 -0.1635 +vn -0.7510 -0.6397 -0.1636 +vn 0.6976 -0.6976 -0.1635 +vn 0.8869 -0.4321 -0.1636 +vn 0.7971 0.5812 -0.1636 +vn -0.9271 0.3371 -0.1635 +vn -0.6976 0.6976 -0.1636 +vn 0.0757 0.9836 -0.1636 +vn 0.1935 0.9674 -0.1636 +vn 0.5511 0.8183 -0.1636 +vn 0.0418 0.9856 -0.1636 +vn -0.6976 -0.6976 0.1636 +vn -0.4557 -0.8750 0.1636 +vn -0.5406 -0.8252 0.1636 +vn -0.7084 0.6866 0.1635 +vn 0.1971 -0.9666 0.1636 +vn -0.6287 -0.7603 0.1636 +vn 0.6976 -0.6976 0.1636 +vn 0.8824 -0.4412 0.1636 +vn 0.6976 0.6976 0.1636 +vn -0.3664 0.9160 0.1636 +vn 0.2186 0.9620 0.1635 +vn 0.2047 -0.9651 0.1635 +vn -0.7510 -0.6397 0.1636 +vn 0.6976 -0.6976 0.1635 +vn 0.8869 -0.4321 0.1636 +vn 0.7971 0.5812 0.1636 +vn -0.9271 0.3371 0.1635 +vn -0.6976 0.6976 0.1636 +vn 0.0757 0.9836 0.1636 +vn 0.1935 0.9674 0.1636 +vn 0.5511 0.8183 0.1636 +vn 0.0418 0.9856 0.1636 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +vn -0.7071 -0.7071 0.0000 +vn -0.4619 -0.8869 0.0000 +vn 0.1241 -0.9923 0.0000 +vn -0.2748 -0.9615 0.0000 +vn 0.0808 -0.9967 0.0000 +vn -0.0278 -0.9996 0.0000 +vn 0.0500 -0.9988 0.0000 +vn 0.7068 -0.7074 0.0000 +vn 0.6097 0.7926 0.0000 +vn 0.5322 0.8466 0.0000 +vn 0.0423 0.9991 0.0000 +vn -0.7071 0.7071 0.0000 +vn -0.5002 -0.5002 0.7069 +vn -0.3268 -0.6274 0.7068 +vn 0.0878 -0.7019 0.7069 +vn -0.1943 -0.6801 0.7069 +vn 0.0572 -0.7049 0.7070 +vn -0.0196 -0.7067 0.7073 +vn 0.0353 -0.7063 0.7071 +vn 0.4999 -0.5002 0.7070 +vn 0.4311 0.5604 0.7072 +vn 0.3763 0.5986 0.7071 +vn 0.0299 0.7065 0.7071 +vn -0.5002 0.5002 0.7069 +vn -0.5002 -0.5002 -0.7069 +vn -0.3268 -0.6274 -0.7068 +vn 0.0878 -0.7019 -0.7069 +vn -0.1943 -0.6801 -0.7069 +vn 0.0572 -0.7049 -0.7070 +vn -0.0196 -0.7067 -0.7073 +vn 0.0353 -0.7063 -0.7071 +vn 0.4999 -0.5002 -0.7070 +vn 0.4311 0.5604 -0.7072 +vn 0.3763 0.5986 -0.7071 +vn 0.0299 0.7065 -0.7071 +vn -0.5002 0.5002 -0.7069 +vn 0.2747 0.9615 0.0000 +vn -0.1414 0.9900 0.0000 +vn -0.5480 -0.8365 0.0000 +vn -0.4008 -0.9162 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.7071 -0.7071 0.0000 +vn 0.2075 -0.9782 0.0000 +vn -0.7073 -0.7069 0.0000 +vn 0.8137 -0.5812 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.7474 0.6644 0.0000 +vn -0.6247 0.7809 0.0000 +vn 0.0000 1.0000 0.0000 +vn 0.1155 0.9933 0.0000 +vn -0.3713 0.9285 0.0000 +vn 0.0948 0.9955 0.0000 +vn 0.9487 0.3160 0.0000 +vn -0.0808 0.9967 0.0000 +vn 0.1943 0.6799 0.7071 +vn -0.1000 0.7000 0.7071 +vn -0.5000 -0.5000 0.7071 +vn -0.3875 -0.5915 0.7071 +vn -0.2834 -0.6478 0.7071 +vn 0.0000 -0.7071 0.7071 +vn 0.5000 -0.5000 0.7071 +vn 0.1467 -0.6917 0.7071 +vn -0.5001 -0.4999 0.7071 +vn 0.5754 -0.4110 0.7071 +vn 0.7071 0.0000 0.7071 +vn 0.5285 0.4698 0.7071 +vn -0.4417 0.5521 0.7072 +vn 0.0000 0.7071 0.7071 +vn 0.0817 0.7025 0.7070 +vn -0.2626 0.6566 0.7070 +vn 0.0670 0.7038 0.7072 +vn 0.6708 0.2236 0.7072 +vn -0.0571 0.7048 0.7071 +vn 0.1943 0.6799 -0.7071 +vn -0.1000 0.7000 -0.7071 +vn -0.5000 -0.5000 -0.7071 +vn -0.3875 -0.5915 -0.7071 +vn -0.2834 -0.6478 -0.7071 +vn 0.0000 -0.7071 -0.7071 +vn 0.5000 -0.5000 -0.7071 +vn 0.1467 -0.6917 -0.7071 +vn -0.5001 -0.4999 -0.7071 +vn 0.5754 -0.4110 -0.7071 +vn 0.7071 0.0000 -0.7071 +vn 0.5285 0.4698 -0.7071 +vn -0.4417 0.5521 -0.7072 +vn 0.0000 0.7071 -0.7071 +vn 0.0817 0.7025 -0.7070 +vn -0.2626 0.6566 -0.7070 +vn 0.0670 0.7038 -0.7072 +vn 0.6708 0.2236 -0.7072 +vn -0.0571 0.7048 -0.7071 +vn -1.0000 0.0000 0.0000 +vn -0.7433 -0.6690 0.0000 +vn -0.5145 0.8575 0.0000 +vn -0.9284 0.3715 0.0000 +vn -0.8742 -0.4856 0.0000 +vn 0.7070 -0.7072 0.0000 +vn 0.8990 -0.4380 0.0000 +vn 0.8222 0.5692 0.0000 +vn -0.7072 0.7070 0.0000 +vn -0.7071 0.0000 0.7071 +vn -0.5000 0.5000 0.7071 +vn -0.5256 -0.4730 0.7071 +vn -0.3638 0.6064 0.7071 +vn -0.6565 0.2627 0.7071 +vn -0.6182 -0.3434 0.7071 +vn 0.4999 -0.5001 0.7071 +vn 0.6357 -0.3097 0.7071 +vn 0.5814 0.4025 0.7071 +vn -0.5001 0.5000 0.7071 +vn -0.7071 0.0000 -0.7071 +vn -0.5000 0.5000 -0.7071 +vn -0.5256 -0.4730 -0.7071 +vn -0.3638 0.6064 -0.7071 +vn -0.6565 0.2627 -0.7071 +vn -0.6182 -0.3434 -0.7071 +vn 0.4999 -0.5001 -0.7071 +vn 0.6357 -0.3097 -0.7071 +vn 0.5814 0.4025 -0.7071 +vn -0.5001 0.5000 -0.7071 +vn -0.6313 -0.7756 0.0000 +vn -0.6300 -0.7766 0.0000 +vn 0.8944 -0.4472 0.0000 +vn 0.7071 0.7072 0.0000 +vn -0.3715 0.9284 0.0000 +vn 0.2216 0.9751 0.0000 +vn 0.7071 0.7071 0.0000 +vn 0.4258 0.9048 0.0000 +vn -0.4464 -0.5484 0.7071 +vn -0.4456 -0.5492 0.7070 +vn 0.4999 -0.5000 0.7071 +vn 0.6325 -0.3162 0.7071 +vn 0.5000 0.5000 0.7071 +vn -0.2627 0.6565 0.7071 +vn 0.1568 0.6897 0.7070 +vn 0.5001 0.5001 0.7070 +vn 0.3010 0.6397 0.7072 +vn -0.4999 0.4999 0.7072 +vn -0.4464 -0.5484 -0.7071 +vn -0.4456 -0.5492 -0.7070 +vn 0.4999 -0.5000 -0.7071 +vn 0.6325 -0.3162 -0.7071 +vn 0.5000 0.5000 -0.7071 +vn -0.2627 0.6565 -0.7071 +vn 0.1568 0.6897 -0.7070 +vn 0.5001 0.5001 -0.7070 +vn 0.3010 0.6397 -0.7072 +vn -0.4999 0.4999 -0.7072 +vn -0.0948 -0.9955 0.0000 +vn 0.1961 -0.9806 0.0000 +vn -0.0671 -0.7041 0.7069 +vn 0.1388 -0.6938 0.7066 +vn 0.5002 -0.5002 0.7069 +vn 0.5002 0.5002 0.7069 +vn -0.0671 -0.7041 -0.7069 +vn 0.1388 -0.6938 -0.7066 +vn 0.5002 -0.5002 -0.7069 +vn 0.5002 0.5002 -0.7069 +vn -0.7072 -0.7070 0.0000 +vn 0.2036 -0.9791 0.0000 +vn 0.7072 0.7070 0.0000 +vn 0.6225 0.7826 0.0000 +vn -0.7131 0.7010 0.0000 +vn -0.5000 -0.4999 0.7071 +vn 0.1439 -0.6923 0.7071 +vn 0.5000 0.4999 0.7071 +vn 0.4402 0.5534 0.7071 +vn -0.5043 0.4958 0.7070 +vn -0.5000 -0.4999 -0.7071 +vn 0.1439 -0.6923 -0.7071 +vn 0.5000 0.4999 -0.7071 +vn 0.4402 0.5534 -0.7071 +vn -0.5043 0.4958 -0.7070 +vn -0.9811 0.0989 -0.1664 +vn -0.6160 -0.7700 -0.1664 +vn 0.7506 -0.6394 -0.1664 +vn -0.6973 -0.6972 -0.1664 +vn -0.8983 -0.4068 -0.1664 +vn -0.7916 -0.5879 -0.1664 +vn 0.6973 -0.6972 -0.1664 +vn 0.9855 -0.0340 -0.1664 +vn 0.6972 -0.6973 -0.1664 +vn -0.7833 -0.5990 -0.1664 +vn 0.8541 -0.4928 -0.1664 +vn 0.7513 -0.6386 -0.1664 +vn 0.7541 0.6353 -0.1664 +vn 0.7081 -0.6863 -0.1664 +vn 0.5646 0.8084 -0.1664 +vn 0.6972 0.6973 -0.1664 +vn -0.4379 0.8835 -0.1664 +vn -0.9811 0.0989 0.1664 +vn -0.6160 -0.7700 0.1664 +vn 0.7506 -0.6394 0.1664 +vn -0.6973 -0.6972 0.1664 +vn -0.8983 -0.4068 0.1664 +vn -0.7916 -0.5879 0.1664 +vn 0.6973 -0.6972 0.1664 +vn 0.9855 -0.0340 0.1664 +vn 0.6972 -0.6973 0.1664 +vn -0.7833 -0.5990 0.1664 +vn 0.8541 -0.4928 0.1664 +vn 0.7513 -0.6386 0.1664 +vn 0.7541 0.6353 0.1664 +vn 0.7081 -0.6863 0.1664 +vn 0.5646 0.8084 0.1664 +vn 0.6972 0.6973 0.1664 +vn -0.4379 0.8835 0.1664 +vn -0.4472 0.8944 0.0000 +vn -0.9960 0.0891 0.0000 +vn -0.9487 0.3162 0.0000 +vn -0.7070 -0.7072 0.0000 +vn 0.6402 -0.7682 0.0000 +vn 0.8575 -0.5145 0.0000 +vn 0.9864 -0.1644 0.0000 +vn 0.8320 -0.5547 0.0000 +vn 0.6508 -0.7593 0.0000 +vn 0.8137 -0.5813 0.0000 +vn 0.8366 -0.5478 0.0000 +vn 0.7072 -0.7070 0.0000 +vn -0.4292 0.9032 0.0000 +vn -0.3160 0.6321 0.7075 +vn -0.4998 0.4998 0.7074 +vn -0.7041 0.0630 0.7073 +vn -0.6705 0.2235 0.7074 +vn -0.4999 -0.4999 0.7072 +vn 0.4527 -0.5432 0.7071 +vn 0.6062 -0.3637 0.7073 +vn -0.4994 -0.5001 0.7074 +vn 0.6976 -0.1162 0.7070 +vn 0.5884 -0.3922 0.7071 +vn 0.4602 -0.5369 0.7071 +vn 0.5755 -0.4111 0.7069 +vn 0.5916 -0.3874 0.7070 +vn 0.5001 -0.5000 0.7071 +vn -0.3035 0.6386 0.7072 +vn -0.3160 0.6321 -0.7075 +vn -0.4998 0.4998 -0.7074 +vn -0.7041 0.0630 -0.7073 +vn -0.6705 0.2235 -0.7074 +vn -0.4999 -0.4999 -0.7072 +vn 0.4527 -0.5432 -0.7071 +vn 0.6062 -0.3637 -0.7073 +vn -0.4994 -0.5001 -0.7074 +vn 0.6976 -0.1162 -0.7070 +vn 0.5884 -0.3922 -0.7071 +vn 0.4602 -0.5369 -0.7071 +vn 0.5755 -0.4111 -0.7069 +vn 0.5916 -0.3874 -0.7070 +vn 0.5001 -0.5000 -0.7071 +vn -0.3035 0.6386 -0.7072 +vn -0.9695 -0.2449 0.0000 +vn -0.9806 0.1960 0.0000 +vn -0.9091 -0.4167 0.0000 +vn -0.8403 -0.5421 0.0000 +vn -0.9731 -0.2305 0.0000 +vn -0.8823 -0.4706 0.0000 +vn -0.7526 -0.6585 0.0000 +vn 0.8829 -0.4696 0.0000 +vn 0.7296 -0.6839 0.0000 +vn 0.8944 -0.4473 0.0000 +vn 0.7690 0.6392 0.0000 +vn -0.6855 -0.1732 0.7072 +vn -0.6934 0.1387 0.7071 +vn -0.6428 -0.2946 0.7071 +vn -0.5942 -0.3834 0.7071 +vn -0.6882 -0.1630 0.7070 +vn -0.6238 -0.3327 0.7073 +vn -0.5321 -0.4656 0.7071 +vn 0.6243 -0.3321 0.7071 +vn 0.5160 -0.4837 0.7070 +vn 0.6327 -0.3163 0.7069 +vn 0.5437 0.4519 0.7072 +vn 0.4998 0.4998 0.7074 +vn -0.3161 0.6326 0.7070 +vn -0.6855 -0.1732 -0.7072 +vn -0.6934 0.1387 -0.7071 +vn -0.6428 -0.2946 -0.7071 +vn -0.5942 -0.3834 -0.7071 +vn -0.6882 -0.1630 -0.7070 +vn -0.6238 -0.3327 -0.7073 +vn -0.5321 -0.4656 -0.7071 +vn 0.6243 -0.3321 -0.7071 +vn 0.5160 -0.4837 -0.7070 +vn 0.6327 -0.3163 -0.7069 +vn 0.5437 0.4519 -0.7072 +vn 0.4998 0.4998 -0.7074 +vn -0.3161 0.6326 -0.7070 +vn -0.8334 0.5526 0.0000 +vn -0.9487 -0.3162 0.0000 +vn -0.7893 -0.6140 0.0000 +vn 0.3379 -0.9412 0.0000 +vn -0.3955 -0.9185 0.0000 +vn 0.8898 0.4563 0.0000 +vn 0.7181 -0.6960 0.0000 +vn 0.5547 0.8321 0.0000 +vn 0.6139 0.7894 0.0000 +vn -0.5895 0.3909 0.7069 +vn -0.6712 -0.2238 0.7067 +vn -0.5584 -0.4343 0.7068 +vn 0.2390 -0.6657 0.7069 +vn -0.2829 -0.6551 0.7006 +vn 0.6364 0.3335 0.6956 +vn 0.5079 -0.4922 0.7070 +vn 0.4999 0.4999 0.7072 +vn 0.3922 0.5884 0.7071 +vn 0.4343 0.5584 0.7068 +vn -0.5895 0.3909 -0.7069 +vn -0.6712 -0.2238 -0.7067 +vn -0.5584 -0.4343 -0.7068 +vn 0.2390 -0.6657 -0.7069 +vn -0.2829 -0.6551 -0.7006 +vn 0.6364 0.3335 -0.6956 +vn 0.5079 -0.4922 -0.7070 +vn 0.4999 0.4999 -0.7072 +vn 0.3922 0.5884 -0.7071 +vn 0.4343 0.5584 -0.7068 +vn -0.5546 0.8321 0.0000 +vn -0.8209 -0.5711 0.0000 +vn -0.6247 -0.7808 0.0000 +vn 0.9702 -0.2423 0.0000 +vn 0.9695 0.2449 0.0000 +vn 0.5146 0.8575 0.0000 +vn -0.3162 0.9487 0.0000 +vn -0.3922 0.5884 0.7071 +vn -0.5805 -0.4038 0.7071 +vn -0.4417 -0.5521 0.7071 +vn 0.6860 -0.1715 0.7071 +vn 0.6857 0.1732 0.7070 +vn 0.3638 0.6063 0.7071 +vn -0.2236 0.6708 0.7071 +vn -0.3922 0.5884 -0.7071 +vn -0.5805 -0.4038 -0.7071 +vn -0.4417 -0.5521 -0.7071 +vn 0.6860 -0.1715 -0.7071 +vn 0.6857 0.1732 -0.7070 +vn 0.3638 0.6063 -0.7071 +vn -0.2236 0.6708 -0.7071 +vn -0.8320 0.5547 0.0000 +vn -0.9884 0.1521 0.0000 +vn -0.9110 -0.4125 0.0000 +vn -0.8062 -0.5916 0.0000 +vn -0.7719 -0.6357 0.0000 +vn 0.9998 -0.0189 0.0000 +vn 0.9806 -0.1961 0.0000 +vn 0.7071 -0.7072 0.0000 +vn 0.9744 0.2249 0.0000 +vn 0.8403 0.5421 0.0000 +vn 0.9171 0.3987 0.0000 +vn 0.3940 0.9191 0.0000 +vn 0.7592 0.6508 0.0000 +vn 0.8183 0.5748 0.0000 +vn 0.9615 0.2748 0.0000 +vn 0.7894 0.6139 0.0000 +vn 0.9285 0.3714 0.0000 +vn -0.5884 0.3923 0.7071 +vn -0.6989 0.1075 0.7071 +vn -0.6442 -0.2917 0.7071 +vn -0.5701 -0.4183 0.7071 +vn -0.5459 -0.4495 0.7071 +vn 0.7070 -0.0133 0.7071 +vn 0.6934 -0.1387 0.7070 +vn 0.6890 0.1590 0.7071 +vn 0.5942 0.3834 0.7071 +vn 0.6485 0.2819 0.7071 +vn 0.2786 0.6499 0.7071 +vn 0.5369 0.4602 0.7071 +vn 0.5786 0.4064 0.7071 +vn 0.6799 0.1943 0.7071 +vn 0.5581 0.4341 0.7071 +vn 0.6566 0.2626 0.7071 +vn -0.4416 0.5522 0.7072 +vn -0.5884 0.3923 -0.7071 +vn -0.6989 0.1075 -0.7071 +vn -0.6442 -0.2917 -0.7071 +vn -0.5701 -0.4183 -0.7071 +vn -0.5459 -0.4495 -0.7071 +vn 0.7070 -0.0133 -0.7071 +vn 0.6934 -0.1387 -0.7070 +vn 0.6890 0.1590 -0.7071 +vn 0.5942 0.3834 -0.7071 +vn 0.6485 0.2819 -0.7071 +vn 0.2786 0.6499 -0.7071 +vn 0.5369 0.4602 -0.7071 +vn 0.5786 0.4064 -0.7071 +vn 0.6799 0.1943 -0.7071 +vn 0.5581 0.4341 -0.7071 +vn 0.6566 0.2626 -0.7071 +vn -0.4416 0.5522 -0.7072 +vn -0.6980 0.6980 -0.1597 +vn 0.6980 0.6980 -0.1597 +vn 0.0880 0.9832 -0.1597 +vn -0.6980 0.6981 -0.1597 +vn -0.8796 -0.4481 -0.1597 +vn -0.6980 -0.6980 -0.1597 +vn -0.5878 -0.7931 -0.1597 +vn 0.6980 -0.6980 -0.1597 +vn -0.6981 -0.6980 -0.1597 +vn -0.0597 -0.9854 -0.1597 +vn 0.6981 -0.6980 -0.1597 +vn 0.7584 0.6320 -0.1597 +vn 0.9305 -0.3296 -0.1597 +vn 0.9711 0.1774 -0.1597 +vn -0.8740 0.4589 -0.1597 +vn -0.7217 0.6736 -0.1597 +vn 0.8465 0.5079 -0.1597 +vn -0.9871 -0.0130 -0.1597 +vn 0.7883 0.5942 -0.1597 +vn -0.8972 0.4118 -0.1597 +vn -0.6980 0.6980 0.1597 +vn 0.6980 0.6980 0.1597 +vn 0.0880 0.9832 0.1597 +vn -0.6980 0.6981 0.1597 +vn -0.8796 -0.4481 0.1597 +vn -0.6980 -0.6980 0.1597 +vn -0.5878 -0.7931 0.1597 +vn 0.6980 -0.6980 0.1597 +vn -0.6981 -0.6980 0.1597 +vn -0.0597 -0.9854 0.1597 +vn 0.6981 -0.6980 0.1597 +vn 0.7584 0.6320 0.1597 +vn 0.9305 -0.3296 0.1597 +vn 0.9711 0.1774 0.1597 +vn -0.8740 0.4589 0.1597 +vn -0.7217 0.6736 0.1597 +vn 0.8465 0.5079 0.1597 +vn -0.9871 -0.0130 0.1597 +vn 0.7883 0.5942 0.1597 +vn -0.8972 0.4118 0.1597 +vn -0.9247 0.3808 0.0000 +vn -0.7526 0.6585 0.0000 +vn -0.3714 -0.9285 0.0000 +vn -0.7071 -0.7072 0.0000 +vn -0.7512 -0.6601 0.0000 +vn -0.9487 -0.3161 0.0000 +vn -0.7515 -0.6597 0.0000 +vn -0.6508 -0.7592 0.0000 +vn 0.9975 -0.0713 0.0000 +vn 0.9487 0.3162 0.0000 +vn 0.8132 0.5819 0.0000 +vn 0.7634 0.6459 0.0000 +vn -0.8193 0.5734 0.0000 +vn -0.6540 0.2693 0.7070 +vn -0.5322 0.4657 0.7071 +vn -0.2625 -0.6564 0.7073 +vn -0.5312 -0.4668 0.7071 +vn -0.6708 -0.2236 0.7072 +vn -0.5314 -0.4665 0.7071 +vn -0.4602 -0.5369 0.7071 +vn 0.7054 -0.0504 0.7070 +vn 0.6707 0.2236 0.7072 +vn 0.5751 0.4115 0.7071 +vn 0.5398 0.4568 0.7071 +vn -0.5794 0.4056 0.7069 +vn -0.6540 0.2693 -0.7070 +vn -0.5322 0.4657 -0.7071 +vn -0.2625 -0.6564 -0.7073 +vn -0.5312 -0.4668 -0.7071 +vn -0.6708 -0.2236 -0.7072 +vn -0.5314 -0.4665 -0.7071 +vn -0.4602 -0.5369 -0.7071 +vn 0.7054 -0.0504 -0.7070 +vn 0.6707 0.2236 -0.7072 +vn 0.5751 0.4115 -0.7071 +vn 0.5398 0.4568 -0.7071 +vn -0.5794 0.4056 -0.7069 +vn -0.8910 -0.4539 0.0000 +vn 0.5657 -0.8246 0.0000 +vn 0.5052 -0.8630 0.0000 +vn 0.5781 -0.8160 0.0000 +vn -0.7738 0.6334 0.0000 +vn 0.0891 0.9960 0.0000 +vn -0.7070 0.7072 0.0000 +vn -0.6300 -0.3209 0.7072 +vn 0.4000 -0.5830 0.7072 +vn 0.3572 -0.6103 0.7071 +vn 0.4108 -0.6706 0.6177 +vn -0.6646 0.4108 0.6242 +vn 0.5000 0.4999 0.7072 +vn 0.0630 0.7043 0.7071 +vn -0.4999 0.5001 0.7071 +vn -0.6300 -0.3209 -0.7072 +vn 0.4000 -0.5830 -0.7072 +vn 0.3572 -0.6103 -0.7071 +vn 0.4108 -0.6706 -0.6177 +vn -0.6646 0.4108 -0.6242 +vn 0.5000 0.4999 -0.7072 +vn 0.0630 0.7043 -0.7071 +vn -0.4999 0.5001 -0.7071 +vn -0.5010 0.8654 0.0000 +vn -0.5647 0.8253 0.0000 +vn -0.7809 0.6247 0.0000 +vn -0.6402 -0.7682 0.0000 +vn -0.5951 -0.8036 0.0000 +vn 0.9999 -0.0138 0.0000 +vn 0.9982 -0.0605 0.0000 +vn 0.8192 0.5735 0.0000 +vn 0.7072 0.7071 0.0000 +vn 0.3161 0.9487 0.0000 +vn -0.3543 0.6119 0.7071 +vn -0.3993 0.5836 0.7071 +vn -0.5522 0.4418 0.7071 +vn -0.4526 -0.5432 0.7072 +vn -0.4208 -0.5682 0.7071 +vn 0.7071 -0.0098 0.7070 +vn 0.7059 -0.0428 0.7070 +vn 0.5794 0.4055 0.7070 +vn 0.5285 0.4698 0.7070 +vn 0.2236 0.6708 0.7071 +vn -0.5522 0.4417 0.7071 +vn -0.3543 0.6119 -0.7071 +vn -0.3993 0.5836 -0.7071 +vn -0.5522 0.4418 -0.7071 +vn -0.4526 -0.5432 -0.7072 +vn -0.4208 -0.5682 -0.7071 +vn 0.7071 -0.0098 -0.7070 +vn 0.7059 -0.0428 -0.7070 +vn 0.5794 0.4055 -0.7070 +vn 0.5285 0.4698 -0.7070 +vn 0.2236 0.6708 -0.7071 +vn -0.5522 0.4417 -0.7071 +vn -0.8854 0.4648 0.0000 +vn -0.7328 0.6804 0.0000 +vn -0.8522 -0.5233 0.0000 +vn 0.6644 -0.7474 0.0000 +vn 0.5547 -0.8320 0.0000 +vn 0.9845 0.1755 0.0000 +vn 0.8137 0.5812 0.0000 +vn -0.6261 0.3287 0.7071 +vn -0.5182 0.4812 0.7071 +vn -0.6026 -0.3700 0.7071 +vn 0.4698 -0.5285 0.7071 +vn 0.3922 -0.5883 0.7072 +vn 0.6961 0.1241 0.7071 +vn 0.5754 0.4110 0.7071 +vn -0.6261 0.3287 -0.7071 +vn -0.5182 0.4812 -0.7071 +vn -0.6026 -0.3700 -0.7071 +vn 0.4698 -0.5285 -0.7071 +vn 0.3922 -0.5883 -0.7072 +vn 0.6961 0.1241 -0.7071 +vn 0.5754 0.4110 -0.7071 +vn -0.9999 -0.0132 0.0000 +vn -0.9398 -0.3417 0.0000 +vn -0.9975 0.0713 0.0000 +vn -0.1643 -0.9864 0.0000 +vn 0.6499 -0.7600 0.0000 +vn 0.8522 0.5233 0.0000 +vn 0.8575 0.5145 0.0000 +vn 0.7070 0.7072 0.0000 +vn -0.7070 -0.0093 0.7072 +vn -0.6646 -0.2417 0.7071 +vn -0.7053 0.0504 0.7071 +vn -0.1162 -0.6975 0.7071 +vn 0.4596 -0.5374 0.7071 +vn 0.6026 0.3700 0.7071 +vn 0.6063 0.3638 0.7072 +vn -0.7070 -0.0093 -0.7072 +vn -0.6646 -0.2417 -0.7071 +vn -0.7053 0.0504 -0.7071 +vn -0.1162 -0.6975 -0.7071 +vn 0.4596 -0.5374 -0.7071 +vn 0.6026 0.3700 -0.7071 +vn 0.6063 0.3638 -0.7072 +vn -0.9999 0.0139 0.0000 +vn -0.0605 -0.9982 0.0000 +vn 0.7372 0.6757 0.0000 +vn 0.8321 0.5547 0.0000 +vn 0.6585 0.7526 0.0000 +vn 0.7515 0.6597 0.0000 +vn -0.9978 0.0665 0.0000 +vn -0.7070 0.0098 0.7071 +vn -0.0428 -0.7058 0.7071 +vn 0.4999 -0.4999 0.7072 +vn 0.5212 0.4778 0.7072 +vn 0.5884 0.3923 0.7071 +vn 0.4657 0.5322 0.7071 +vn 0.5314 0.4665 0.7071 +vn -0.7055 0.0470 0.7071 +vn -0.7070 0.0098 -0.7071 +vn -0.0428 -0.7058 -0.7071 +vn 0.4999 -0.4999 -0.7072 +vn 0.5212 0.4778 -0.7072 +vn 0.5884 0.3923 -0.7071 +vn 0.4657 0.5322 -0.7071 +vn 0.5314 0.4665 -0.7071 +vn -0.7055 0.0470 -0.7071 +vn 0.1645 0.9864 0.0000 +vn -0.8322 -0.5545 0.0000 +vn -0.7448 -0.6673 0.0000 +vn -0.6509 -0.7592 0.0000 +vn 0.7808 -0.6247 0.0000 +vn 0.9487 -0.3162 0.0000 +vn 0.9445 -0.3285 0.0000 +vn 0.4471 0.8945 0.0000 +vn -0.6467 0.7628 0.0000 +vn 0.1163 0.6975 0.7071 +vn -0.5883 -0.3921 0.7072 +vn -0.5267 -0.4718 0.7071 +vn -0.4603 -0.5369 0.7070 +vn 0.5521 -0.4417 0.7072 +vn 0.6707 -0.2236 0.7072 +vn 0.6679 -0.2323 0.7071 +vn 0.3162 0.6326 0.7070 +vn -0.4573 0.5394 0.7071 +vn 0.1163 0.6975 -0.7071 +vn -0.5883 -0.3921 -0.7072 +vn -0.5267 -0.4718 -0.7071 +vn -0.4603 -0.5369 -0.7070 +vn 0.5521 -0.4417 -0.7072 +vn 0.6707 -0.2236 -0.7072 +vn 0.6679 -0.2323 -0.7071 +vn 0.3162 0.6326 -0.7070 +vn -0.4573 0.5394 -0.7071 +vn 0.5908 0.7877 -0.1745 +vn -0.6963 0.6963 -0.1745 +vn -0.9804 0.0915 -0.1745 +vn -0.6963 -0.6962 -0.1746 +vn -0.8193 0.5461 -0.1746 +vn -0.6875 -0.7049 -0.1746 +vn -0.0174 -0.9845 -0.1746 +vn 0.6963 -0.6962 -0.1746 +vn 0.7877 0.5908 -0.1745 +vn 0.7500 0.6380 -0.1745 +vn -0.5461 0.8193 -0.1746 +vn 0.6962 0.6962 -0.1746 +vn -0.1718 0.9695 -0.1746 +vn -0.6963 0.6962 -0.1746 +vn 0.5527 0.8149 -0.1746 +vn 0.6962 0.6963 -0.1746 +vn 0.9608 0.2153 -0.1746 +vn 0.7335 -0.6569 -0.1746 +vn 0.9621 -0.2096 -0.1746 +vn 0.6151 0.7689 -0.1746 +vn -0.6024 0.7789 -0.1745 +vn 0.5908 0.7877 0.1745 +vn -0.6963 0.6963 0.1745 +vn -0.9804 0.0915 0.1745 +vn -0.6963 -0.6962 0.1746 +vn -0.8193 0.5461 0.1746 +vn -0.6875 -0.7049 0.1746 +vn -0.0174 -0.9845 0.1746 +vn 0.6963 -0.6962 0.1746 +vn 0.7877 0.5908 0.1745 +vn 0.7500 0.6380 0.1745 +vn -0.5461 0.8193 0.1746 +vn 0.6962 0.6962 0.1746 +vn -0.1718 0.9695 0.1746 +vn -0.6963 0.6962 0.1746 +vn 0.5527 0.8149 0.1746 +vn 0.6962 0.6963 0.1746 +vn 0.9608 0.2153 0.1746 +vn 0.7335 -0.6569 0.1746 +vn 0.9621 -0.2096 0.1746 +vn 0.6151 0.7689 0.1746 +vn -0.6024 0.7789 0.1745 +vn -0.5882 0.8088 0.0000 +vn -0.1961 -0.9806 0.0000 +vn -0.6000 -0.8000 0.0000 +vn -0.0294 -0.9996 0.0000 +vn 0.8000 0.6000 0.0000 +vn 0.7617 0.6479 0.0000 +vn -0.6174 0.7866 0.0000 +vn -0.4159 0.5719 0.7071 +vn -0.1387 -0.6934 0.7071 +vn -0.4243 -0.5657 0.7071 +vn -0.0208 -0.7067 0.7073 +vn 0.5657 0.4243 0.7071 +vn 0.5385 0.4581 0.7072 +vn -0.4366 0.5562 0.7071 +vn -0.4159 0.5719 -0.7071 +vn -0.1387 -0.6934 -0.7071 +vn -0.4243 -0.5657 -0.7071 +vn -0.0208 -0.7067 -0.7073 +vn 0.5657 0.4243 -0.7071 +vn 0.5385 0.4581 -0.7072 +vn -0.4366 0.5562 -0.7071 +vn -0.8320 0.5548 0.0000 +vn -0.7944 0.6075 0.0000 +vn -0.7029 -0.7113 0.0000 +vn -0.6941 -0.7199 0.0000 +vn -0.3162 -0.9487 0.0000 +vn 0.9697 0.2445 0.0000 +vn 0.9899 -0.1414 0.0000 +vn 0.9762 0.2169 0.0000 +vn 0.6246 0.7809 0.0000 +vn 0.1962 0.9806 0.0000 +vn -0.9923 0.1240 0.0000 +vn -0.5883 0.3922 0.7072 +vn -0.5616 0.4295 0.7072 +vn -0.4970 -0.5029 0.7072 +vn -0.4908 -0.5090 0.7072 +vn -0.2238 -0.6708 0.7070 +vn 0.6934 -0.1387 0.7071 +vn 0.6858 0.1729 0.7070 +vn 0.7001 -0.1000 0.7070 +vn 0.6903 0.1534 0.7071 +vn 0.4417 0.5521 0.7072 +vn 0.1388 0.6935 0.7070 +vn -0.7016 0.0877 0.7071 +vn -0.5883 0.3922 -0.7072 +vn -0.5616 0.4295 -0.7072 +vn -0.4970 -0.5029 -0.7072 +vn -0.4908 -0.5090 -0.7072 +vn -0.2238 -0.6708 -0.7070 +vn 0.6934 -0.1387 -0.7071 +vn 0.6858 0.1729 -0.7070 +vn 0.7001 -0.1000 -0.7070 +vn 0.6903 0.1534 -0.7071 +vn 0.4417 0.5521 -0.7072 +vn 0.1388 0.6935 -0.7070 +vn -0.7016 0.0877 -0.7071 +vn -0.9864 0.1644 0.0000 +vn -0.9968 0.0795 0.0000 +vn 0.7944 -0.6074 0.0000 +vn 0.9192 -0.3939 0.0000 +vn 0.9900 0.1414 0.0000 +vn 0.8598 0.5105 0.0000 +vn 0.8849 0.4657 0.0000 +vn -0.4474 0.8943 0.0000 +vn -0.6974 0.1162 0.7072 +vn -0.7048 0.0562 0.7071 +vn -0.6707 0.2235 0.7073 +vn -0.4999 -0.5000 0.7071 +vn 0.5617 -0.4295 0.7071 +vn 0.6500 -0.2786 0.7071 +vn 0.7000 0.1000 0.7071 +vn 0.6080 0.3610 0.7072 +vn 0.6258 0.3293 0.7071 +vn -0.3162 0.6325 0.7071 +vn 0.4999 0.5000 0.7072 +vn -0.4999 0.5000 0.7071 +vn -0.6974 0.1162 -0.7072 +vn -0.7048 0.0562 -0.7071 +vn -0.6707 0.2235 -0.7073 +vn -0.4999 -0.5000 -0.7071 +vn 0.5617 -0.4295 -0.7071 +vn 0.6500 -0.2786 -0.7071 +vn 0.7000 0.1000 -0.7071 +vn 0.6080 0.3610 -0.7072 +vn 0.6258 0.3293 -0.7071 +vn -0.3162 0.6325 -0.7071 +vn 0.4999 0.5000 -0.7072 +vn -0.4999 0.5000 -0.7071 +vn -0.7593 0.6508 0.0000 +vn -0.6024 -0.7982 0.0000 +vn -0.5734 -0.8193 0.0000 +vn -0.1962 -0.9806 0.0000 +vn 0.6145 -0.7889 0.0000 +vn -0.1745 0.9847 0.0000 +vn -0.5369 0.4602 0.7071 +vn -0.4260 -0.5644 0.7071 +vn -0.4055 -0.5793 0.7071 +vn 0.4345 -0.5578 0.7071 +vn 0.5001 0.4999 0.7071 +vn -0.1234 0.6962 0.7071 +vn -0.5369 0.4602 -0.7071 +vn -0.4260 -0.5644 -0.7071 +vn -0.4055 -0.5793 -0.7071 +vn 0.4345 -0.5578 -0.7071 +vn 0.5001 0.4999 -0.7071 +vn -0.1234 0.6962 -0.7071 +vn -0.8898 -0.4563 0.0000 +vn -0.8575 -0.5145 0.0000 +vn -0.2425 -0.9702 0.0000 +vn 0.7498 -0.6616 0.0000 +vn 0.9798 -0.2000 0.0000 +vn 0.8321 -0.5547 0.0000 +vn -0.6100 0.7924 0.0000 +vn -0.6292 -0.3227 0.7071 +vn -0.6064 -0.3638 0.7071 +vn -0.1715 -0.6860 0.7071 +vn 0.5302 -0.4678 0.7071 +vn 0.6928 -0.1414 0.7071 +vn 0.5884 -0.3921 0.7071 +vn 0.4417 0.5522 0.7071 +vn -0.4314 0.5604 0.7070 +vn -0.6292 -0.3227 -0.7071 +vn -0.6064 -0.3638 -0.7071 +vn -0.1715 -0.6860 -0.7071 +vn 0.5302 -0.4678 -0.7071 +vn 0.6928 -0.1414 -0.7071 +vn 0.5884 -0.3921 -0.7071 +vn 0.4417 0.5522 -0.7071 +vn -0.4314 0.5604 -0.7070 +vn -0.9697 -0.2445 0.0000 +vn -0.9487 0.3163 0.0000 +vn -0.0301 -0.9995 0.0000 +vn 0.8946 0.4470 0.0000 +vn 0.3511 0.9363 0.0000 +vn 0.9864 0.1644 0.0000 +vn 0.5798 0.8148 0.0000 +vn 0.6459 0.7634 0.0000 +vn 0.4472 0.8944 0.0000 +vn 0.5631 0.8264 0.0000 +vn 0.6644 0.7474 0.0000 +vn -0.9701 0.2425 0.0000 +vn -0.6860 -0.1729 0.7068 +vn -0.6708 0.2236 0.7072 +vn -0.0213 -0.7070 0.7068 +vn 0.6326 0.3160 0.7071 +vn 0.2483 0.6621 0.7071 +vn 0.6975 0.1162 0.7071 +vn 0.4099 0.5761 0.7072 +vn 0.4567 0.5397 0.7072 +vn 0.3162 0.6325 0.7071 +vn 0.3981 0.5843 0.7072 +vn 0.4698 0.5285 0.7071 +vn -0.6861 0.1715 0.7070 +vn -0.6860 -0.1729 -0.7068 +vn -0.6708 0.2236 -0.7072 +vn -0.0213 -0.7070 -0.7068 +vn 0.6326 0.3160 -0.7071 +vn 0.2483 0.6621 -0.7071 +vn 0.6975 0.1162 -0.7071 +vn 0.4099 0.5761 -0.7072 +vn 0.4567 0.5397 -0.7072 +vn 0.3162 0.6325 -0.7071 +vn 0.3981 0.5843 -0.7072 +vn 0.4698 0.5285 -0.7071 +vn -0.6861 0.1715 -0.7070 +vn -0.0541 0.9832 -0.1745 +vn -0.8549 -0.4885 -0.1746 +vn 0.0000 0.9846 -0.1746 +vn -0.4885 -0.8549 -0.1746 +vn 0.6963 -0.6963 -0.1745 +vn -0.0538 -0.9832 -0.1745 +vn 0.7689 0.6151 -0.1746 +vn 0.7293 0.6615 -0.1746 +vn -0.0541 0.9832 0.1745 +vn -0.8549 -0.4885 0.1746 +vn 0.0000 0.9846 0.1746 +vn -0.4885 -0.8549 0.1746 +vn 0.6963 -0.6963 0.1745 +vn -0.0538 -0.9832 0.1745 +vn 0.7689 0.6151 0.1746 +vn 0.7293 0.6615 0.1746 +vn -0.8682 -0.4962 0.0000 +vn -0.4472 -0.8944 0.0000 +vn -0.6508 -0.7593 0.0000 +vn -0.8400 -0.5425 0.0000 +vn 0.7838 -0.6210 0.0000 +vn 0.9806 0.1960 0.0000 +vn -0.0653 0.9979 0.0000 +vn -0.6140 -0.3508 0.7071 +vn -0.5521 0.4417 0.7072 +vn -0.3162 -0.6324 0.7072 +vn -0.4602 -0.5369 0.7070 +vn -0.5940 -0.3837 0.7070 +vn 0.5543 -0.4391 0.7071 +vn 0.6934 0.1386 0.7071 +vn -0.0462 0.7055 0.7072 +vn -0.6140 -0.3508 -0.7071 +vn -0.5521 0.4417 -0.7072 +vn -0.3162 -0.6324 -0.7072 +vn -0.4602 -0.5369 -0.7070 +vn -0.5940 -0.3837 -0.7070 +vn 0.5543 -0.4391 -0.7071 +vn 0.6934 0.1386 -0.7071 +vn -0.0462 0.7055 -0.7072 +vn -0.6140 -0.7893 0.0000 +vn -0.4842 -0.8750 0.0000 +vn 0.4706 0.8824 0.0000 +vn 0.8400 0.5425 0.0000 +vn 0.5433 0.8396 0.0000 +vn -0.4341 -0.5581 0.7072 +vn -0.3424 -0.6187 0.7071 +vn 0.3327 0.6239 0.7071 +vn 0.5940 0.3836 0.7071 +vn 0.3842 0.5937 0.7071 +vn -0.4341 -0.5581 -0.7072 +vn -0.3424 -0.6187 -0.7071 +vn 0.3327 0.6239 -0.7071 +vn 0.5940 0.3836 -0.7071 +vn 0.3842 0.5937 -0.7071 +vn -0.8000 0.6000 0.0000 +vn -0.7788 0.6273 0.0000 +vn -0.5547 -0.8321 0.0000 +vn -0.0578 -0.9983 0.0000 +vn 0.7809 0.6247 0.0000 +vn 0.7407 0.6718 0.0000 +vn -0.5657 0.4243 0.7071 +vn -0.5507 0.4436 0.7071 +vn -0.3922 -0.5883 0.7071 +vn -0.0408 -0.7060 0.7071 +vn 0.5521 0.4417 0.7072 +vn 0.5238 0.4750 0.7071 +vn -0.5657 0.4243 -0.7071 +vn -0.5507 0.4436 -0.7071 +vn -0.3922 -0.5883 -0.7071 +vn -0.0408 -0.7060 -0.7071 +vn 0.5521 0.4417 -0.7072 +vn 0.5238 0.4750 -0.7071 +vn -0.9501 0.2545 -0.1803 +vn -0.5997 -0.7796 -0.1803 +vn 0.7086 -0.6822 -0.1803 +vn 0.8716 0.4558 -0.1803 +vn 0.4663 0.8660 -0.1803 +vn -0.6722 0.7181 -0.1803 +vn -0.9501 0.2545 0.1803 +vn -0.5997 -0.7796 0.1803 +vn 0.7086 -0.6822 0.1803 +vn 0.8716 0.4558 0.1803 +vn 0.4663 0.8660 0.1803 +vn -0.6722 0.7181 0.1803 +vn -0.9654 0.2607 0.0000 +vn -0.9744 0.2249 0.0000 +vn -0.5591 -0.8291 0.0000 +vn 0.2425 -0.9702 0.0000 +vn 0.9710 -0.2390 0.0000 +vn 0.9648 -0.2631 0.0000 +vn 0.9762 -0.2170 0.0000 +vn -0.6834 0.7300 0.0000 +vn -0.6827 0.1843 0.7071 +vn -0.6890 0.1590 0.7071 +vn -0.3953 -0.5862 0.7071 +vn -0.4527 -0.5432 0.7071 +vn 0.1714 -0.6860 0.7071 +vn 0.6709 -0.2236 0.7071 +vn 0.6866 -0.1690 0.7071 +vn 0.6822 -0.1861 0.7071 +vn 0.6903 -0.1534 0.7071 +vn 0.4998 0.5001 0.7072 +vn -0.4833 0.5162 0.7071 +vn -0.6827 0.1843 -0.7071 +vn -0.6890 0.1590 -0.7071 +vn -0.3953 -0.5862 -0.7071 +vn -0.4527 -0.5432 -0.7071 +vn 0.1714 -0.6860 -0.7071 +vn 0.6709 -0.2236 -0.7071 +vn 0.6866 -0.1690 -0.7071 +vn 0.6822 -0.1861 -0.7071 +vn 0.6903 -0.1534 -0.7071 +vn 0.4998 0.5001 -0.7072 +vn -0.4833 0.5162 -0.7071 +vn -0.9674 0.2533 0.0000 +vn -0.9285 0.3714 0.0000 +vn -0.4471 0.8945 0.0000 +vn 0.6823 -0.7311 0.0000 +vn 0.7250 -0.6887 0.0000 +vn 0.8862 0.4634 0.0000 +vn -0.6841 0.1791 0.7070 +vn -0.6566 0.2626 0.7070 +vn -0.3162 0.6326 0.7070 +vn 0.4824 -0.5169 0.7071 +vn 0.5127 -0.4870 0.7071 +vn 0.6266 0.3277 0.7071 +vn 0.4999 0.5000 0.7071 +vn -0.6841 0.1791 -0.7070 +vn -0.6566 0.2626 -0.7070 +vn -0.3162 0.6326 -0.7070 +vn 0.4824 -0.5169 -0.7071 +vn 0.5127 -0.4870 -0.7071 +vn 0.6266 0.3277 -0.7071 +vn 0.4999 0.5000 -0.7071 +vn -0.6638 -0.6711 -0.3303 +vn -0.0344 -0.9432 -0.3304 +vn 0.5897 -0.7370 -0.3303 +vn 0.3636 0.8710 -0.3304 +vn -0.6674 0.6674 -0.3304 +vn -0.6638 -0.6711 0.3303 +vn -0.0344 -0.9432 0.3304 +vn 0.5897 -0.7370 0.3303 +vn 0.3636 0.8710 0.3304 +vn -0.6674 0.6674 0.3304 +vn -0.7032 -0.7110 0.0000 +vn -0.0365 -0.9993 0.0000 +vn 0.6246 -0.7809 0.0000 +vn 0.6403 0.7682 0.0000 +vn 0.3764 0.9265 0.0000 +vn -0.4972 -0.5027 0.7071 +vn -0.0258 -0.7066 0.7071 +vn 0.4417 -0.5522 0.7070 +vn 0.4527 0.5432 0.7071 +vn 0.2661 0.6551 0.7071 +vn -0.4972 -0.5027 -0.7071 +vn -0.0258 -0.7066 -0.7071 +vn 0.4417 -0.5522 -0.7070 +vn 0.4527 0.5432 -0.7071 +vn 0.2661 0.6551 -0.7071 +vn -0.7753 -0.5384 -0.3303 +vn -0.0866 -0.9399 -0.3303 +vn 0.5896 -0.7371 -0.3303 +vn 0.5047 0.7976 -0.3303 +vn -0.8442 0.4221 -0.3304 +vn -0.7753 -0.5384 0.3303 +vn -0.0866 -0.9399 0.3303 +vn 0.5896 -0.7371 0.3303 +vn 0.5047 0.7976 0.3303 +vn -0.8442 0.4221 0.3304 +vn -0.8945 -0.4470 0.0000 +vn -0.8301 -0.5576 0.0000 +vn -0.0917 -0.9958 0.0000 +vn 0.6247 0.7809 0.0000 +vn 0.5261 0.8504 0.0000 +vn -0.6326 -0.3161 0.7071 +vn -0.5871 -0.3943 0.7070 +vn -0.5001 -0.5001 0.7070 +vn -0.0649 -0.7041 0.7071 +vn 0.4416 -0.5522 0.7072 +vn 0.3720 0.6013 0.7071 +vn -0.6326 -0.3161 -0.7071 +vn -0.5871 -0.3943 -0.7070 +vn -0.5001 -0.5001 -0.7070 +vn -0.0649 -0.7041 -0.7071 +vn 0.4416 -0.5522 -0.7072 +vn 0.3720 0.6013 -0.7071 +vn -0.5721 -0.5236 -0.6313 +vn -0.5284 -0.5896 -0.6109 +vn -0.4653 0.6674 -0.5815 +vn 0.4390 0.8842 -0.1598 +vn -0.3946 0.7370 -0.5487 +vn -0.5385 -0.5753 -0.6157 +vn -0.0401 -0.9227 -0.3833 +vn 0.0405 -0.9375 -0.3456 +vn 0.7445 -0.6673 -0.0175 +vn 0.8738 0.4843 0.0430 +vn 0.7445 -0.6674 -0.0172 +vn 0.7445 0.6674 -0.0173 +vn 0.5188 0.8460 -0.1227 +vn -0.4652 0.6674 -0.5816 +vn -0.8513 -0.5237 -0.0325 +vn -0.8076 -0.5896 -0.0122 +vn -0.7445 0.6674 0.0173 +vn 0.1598 0.8842 0.4390 +vn -0.6740 0.7371 0.0502 +vn -0.8178 -0.5753 -0.0170 +vn -0.3194 -0.9228 0.2156 +vn -0.2388 -0.9375 0.2532 +vn 0.4653 -0.6674 0.5815 +vn 0.5947 0.4843 0.6416 +vn 0.4653 -0.6675 0.5814 +vn 0.4652 0.6674 0.5815 +vn 0.2395 0.8461 0.4763 +vn -0.7445 0.6674 0.0174 +vn 0.4226 -0.0001 -0.9063 +vn -0.4227 -0.0001 0.9063 +vn 0.4228 0.0004 -0.9062 +vn -0.4225 0.0003 0.9064 +vn 0.4226 0.0000 -0.9063 +vn -0.4226 -0.0000 0.9063 +vn 0.4225 -0.0001 -0.9064 +vn -0.4214 0.0010 0.9069 +vn -0.4231 -0.0011 0.9061 +vn 0.4227 0.0003 -0.9063 +vn -0.4228 -0.0003 0.9062 +vn -0.4225 0.0002 0.9064 +vn -0.4226 0.0001 0.9063 +vn 0.4225 -0.0001 -0.9063 +vn -0.4234 -0.0010 0.9059 +vn -0.4223 0.0004 0.9064 +vn -0.4226 -0.0001 0.9063 +vn 0.4219 -0.0006 -0.9067 +vn -0.4163 0.0055 0.9092 +vn -0.7540 -0.5545 -0.3520 +vn -0.7076 -0.6247 -0.3302 +vn -0.6408 0.7071 -0.2989 +vn -0.5585 -0.7875 -0.2606 +vn -0.3628 -0.9164 -0.1694 +vn -0.4866 -0.8437 -0.2269 +vn 0.5027 -0.8321 0.2343 +vn 0.5803 -0.7682 0.2704 +vn 0.7079 -0.6247 0.3295 +vn 0.6410 0.7070 0.2986 +vn 0.4017 0.8964 0.1873 +vn -0.6406 0.7072 -0.2991 +vn -0.8322 -0.3920 0.3923 +vn -0.7992 -0.4417 0.4076 +vn -0.7520 0.5000 0.4296 +vn -0.6938 -0.5568 0.4567 +vn -0.5554 -0.6480 0.5212 +vn -0.6429 -0.5966 0.4804 +vn -0.2991 -0.7071 0.6407 +vn 0.0567 -0.5883 0.8067 +vn 0.1114 -0.5432 0.8322 +vn 0.2016 -0.4416 0.8743 +vn 0.1544 0.4999 0.8522 +vn -0.0148 0.6339 0.7733 +vn -0.7519 0.5000 0.4297 +vn 0.4246 0.0044 -0.9054 +vn 0.4231 0.0010 -0.9061 +vn -0.4227 -0.0003 0.9063 +vn 0.4227 0.0001 -0.9063 +vn 0.4224 -0.0002 -0.9064 +vn -0.4225 0.0001 0.9063 +vn 0.4225 -0.0002 -0.9064 +vn 0.4163 -0.0056 -0.9092 +vn -0.4231 -0.0004 0.9061 +vn -0.2344 -0.3920 -0.8896 +vn -0.2015 -0.4417 -0.8743 +vn -0.1543 0.5000 -0.8522 +vn -0.0962 -0.5569 -0.8250 +vn 0.0422 -0.6480 -0.7605 +vn -0.0452 -0.5966 -0.8013 +vn 0.2988 -0.7071 -0.6409 +vn 0.6544 -0.5883 -0.4751 +vn 0.7091 -0.5432 -0.4496 +vn 0.7993 -0.4416 -0.4075 +vn 0.7521 0.4999 -0.4295 +vn 0.5829 0.6338 -0.5085 +vn -0.1543 0.4999 -0.8522 +vn -0.7542 -0.5546 -0.3515 +vn -0.7080 -0.6247 -0.3294 +vn -0.6409 0.7072 -0.2987 +vn -0.5586 -0.7875 -0.2605 +vn -0.3629 -0.9164 -0.1690 +vn 0.5027 -0.8321 0.2344 +vn 0.7078 -0.6247 0.3299 +vn 0.6410 0.7070 0.2987 +vn 0.4018 0.8964 0.1872 +vn -0.6408 0.7072 -0.2987 +vn -0.4227 -0.0004 0.9063 +vn 0.4225 -0.0003 -0.9064 +vn 0.4228 0.0006 -0.9062 +vn -0.4221 0.0014 0.9066 +vn -0.4235 -0.0025 0.9059 +vn 0.4228 0.0002 -0.9062 +vn -0.4261 -0.0029 0.9047 +vn -0.7542 -0.5546 -0.3517 +vn -0.7234 -0.6022 -0.3376 +vn -0.6408 -0.7071 -0.2990 +vn 0.5868 -0.7619 0.2740 +vn 0.8105 0.4472 0.3782 +vn 0.4052 0.8945 0.1891 +vn 0.3628 0.9164 0.1694 +vn 0.5585 0.7875 0.2606 +vn -0.5802 0.7682 -0.2706 +vn 0.3142 0.9380 0.1465 +vn -0.6409 0.7071 -0.2987 +vn -0.8321 -0.3922 0.3922 +vn -0.8104 -0.4258 0.4024 +vn -0.7520 -0.5000 0.4296 +vn 0.1162 -0.5388 0.8344 +vn 0.2745 0.3159 0.9082 +vn -0.0123 0.6325 0.7745 +vn -0.0423 0.6479 0.7605 +vn 0.0961 0.5568 0.8250 +vn -0.7091 0.5431 0.4496 +vn -0.0767 0.6632 0.7445 +vn -0.7520 0.4999 0.4296 +vn 0.4231 0.0015 -0.9061 +vn 0.4227 0.0002 -0.9063 +vn -0.4227 -0.0002 0.9063 +vn 0.4226 0.0001 -0.9063 +vn -0.4228 -0.0006 0.9062 +vn 0.4230 0.0010 -0.9061 +vn -0.4228 -0.0002 0.9062 +vn 0.4165 -0.0051 -0.9091 +vn -0.4210 0.0013 0.9070 +vn -0.2345 -0.3920 -0.8896 +vn -0.2127 -0.4258 -0.8794 +vn -0.1544 -0.5001 -0.8521 +vn 0.7139 -0.5389 -0.4472 +vn 0.8720 0.3163 -0.3735 +vn 0.5854 0.6325 -0.5072 +vn 0.5554 0.6480 -0.5212 +vn 0.6938 0.5568 -0.4567 +vn -0.1114 0.5431 -0.8322 +vn 0.5210 0.6632 -0.5374 +vn -0.7542 -0.5544 -0.3519 +vn -0.7236 -0.6022 -0.3372 +vn -0.6410 -0.7071 -0.2985 +vn 0.5870 -0.7619 0.2737 +vn 0.8107 0.4471 0.3780 +vn 0.4053 0.8945 0.1888 +vn 0.3629 0.9164 0.1690 +vn 0.5586 0.7875 0.2604 +vn 0.3142 0.9380 0.1464 +vn -0.4227 0.0001 0.9063 +vn 0.4227 -0.0001 -0.9063 +vn -0.4230 0.0005 0.9061 +vn 0.4225 0.0002 -0.9064 +vn 0.4228 -0.0003 -0.9062 +vn -0.4221 -0.0007 0.9065 +vn -0.5027 0.8320 -0.2344 +vn -0.6411 0.7071 -0.2984 +vn -0.5794 0.7690 -0.2701 +vn -0.6962 0.6402 -0.3247 +vn -0.6409 -0.7071 -0.2989 +vn -0.1908 -0.9776 -0.0889 +vn -0.1107 -0.9925 -0.0516 +vn 0.6407 -0.7071 0.2991 +vn 0.8105 0.4471 0.3784 +vn 0.7820 0.5055 0.3647 +vn 0.7078 0.6247 0.3297 +vn -0.5830 0.7657 -0.2716 +vn -0.6543 0.5884 0.4751 +vn -0.2988 0.7071 0.6409 +vn -0.7520 0.5000 0.4295 +vn -0.7085 0.5437 0.4499 +vn -0.7911 0.4526 0.4114 +vn -0.7520 -0.4999 0.4296 +vn -0.4338 -0.6912 0.5780 +vn -0.3771 -0.7018 0.6044 +vn 0.1543 -0.5000 0.8522 +vn 0.2744 0.3162 0.9081 +vn 0.2542 0.3574 0.8987 +vn 0.2017 0.4418 0.8742 +vn -0.7110 0.5414 0.4487 +vn 0.4222 -0.0005 -0.9065 +vn 0.4221 0.0007 -0.9065 +vn 0.4230 -0.0005 -0.9061 +vn -0.4225 -0.0002 0.9064 +vn 0.4229 -0.0004 -0.9062 +vn 0.4229 -0.0003 -0.9062 +vn -0.4228 0.0002 0.9062 +vn 0.4224 0.0003 -0.9064 +vn -0.4223 -0.0004 0.9065 +vn -0.0566 0.5883 -0.8066 +vn 0.2990 0.7071 -0.6408 +vn -0.1544 0.5000 -0.8521 +vn -0.1108 0.5437 -0.8319 +vn -0.1934 0.4526 -0.8705 +vn -0.1543 -0.4999 -0.8522 +vn 0.1639 -0.6913 -0.7037 +vn 0.2206 -0.7018 -0.6774 +vn 0.7519 -0.5000 -0.4296 +vn 0.8518 0.3574 -0.3831 +vn 0.7992 0.4417 -0.4076 +vn -0.1134 0.5415 -0.8330 +vn -0.5026 0.8321 -0.2347 +vn -0.6407 0.7071 -0.2991 +vn -0.5793 0.7690 -0.2703 +vn -0.6964 0.6402 -0.3244 +vn -0.6410 -0.7070 -0.2988 +vn -0.1908 -0.9776 -0.0890 +vn 0.6409 -0.7071 0.2987 +vn 0.8109 0.4471 0.3775 +vn 0.7075 0.6247 0.3303 +vn -0.5828 0.7657 -0.2721 +vn -0.8513 -0.5237 0.0325 +vn -0.8076 -0.5896 0.0122 +vn -0.7445 0.6674 -0.0173 +vn 0.1598 0.8842 -0.4390 +vn -0.6740 0.7371 -0.0502 +vn -0.8178 -0.5753 0.0170 +vn -0.3194 -0.9228 -0.2156 +vn -0.2388 -0.9375 -0.2532 +vn 0.4653 -0.6674 -0.5815 +vn 0.5947 0.4843 -0.6416 +vn 0.4653 -0.6675 -0.5814 +vn 0.4652 0.6674 -0.5815 +vn 0.2395 0.8461 -0.4763 +vn -0.7445 0.6674 -0.0174 +vn -0.5721 -0.5236 0.6313 +vn -0.5284 -0.5896 0.6109 +vn -0.4653 0.6674 0.5815 +vn 0.4390 0.8842 0.1598 +vn -0.3946 0.7370 0.5487 +vn -0.5385 -0.5753 0.6157 +vn -0.0401 -0.9227 0.3833 +vn 0.0405 -0.9375 0.3456 +vn 0.7445 -0.6673 0.0175 +vn 0.8738 0.4843 -0.0430 +vn 0.7445 -0.6674 0.0172 +vn 0.7445 0.6674 0.0173 +vn 0.5188 0.8460 0.1227 +vn -0.4652 0.6674 0.5816 +vn -0.4227 -0.0001 -0.9063 +vn 0.4226 -0.0001 0.9063 +vn -0.4225 0.0003 -0.9064 +vn 0.4228 0.0004 0.9062 +vn -0.4226 -0.0000 -0.9063 +vn 0.4226 0.0000 0.9063 +vn -0.4214 0.0010 -0.9069 +vn 0.4225 -0.0001 0.9064 +vn 0.4246 0.0044 0.9054 +vn -0.4227 -0.0003 -0.9063 +vn 0.4231 0.0010 0.9061 +vn -0.4226 0.0001 -0.9063 +vn 0.4227 0.0001 0.9063 +vn -0.4225 0.0001 -0.9063 +vn 0.4224 -0.0002 0.9064 +vn 0.4225 -0.0002 0.9064 +vn -0.4231 -0.0004 -0.9061 +vn 0.4163 -0.0056 0.9092 +vn -0.7542 -0.5546 0.3515 +vn -0.7080 -0.6247 0.3294 +vn -0.6409 0.7072 0.2987 +vn -0.5586 -0.7875 0.2605 +vn -0.3629 -0.9164 0.1690 +vn -0.4866 -0.8437 0.2269 +vn 0.5027 -0.8321 -0.2344 +vn 0.5803 -0.7682 -0.2704 +vn 0.7078 -0.6247 -0.3299 +vn 0.6410 0.7070 -0.2987 +vn 0.4018 0.8964 -0.1872 +vn -0.6408 0.7072 0.2987 +vn -0.2344 -0.3920 0.8896 +vn -0.2015 -0.4417 0.8743 +vn -0.1543 0.5000 0.8522 +vn -0.0962 -0.5569 0.8250 +vn 0.0422 -0.6480 0.7605 +vn -0.0452 -0.5966 0.8013 +vn 0.2988 -0.7071 0.6409 +vn 0.6544 -0.5883 0.4751 +vn 0.7091 -0.5432 0.4496 +vn 0.7993 -0.4416 0.4075 +vn 0.7521 0.4999 0.4295 +vn 0.5829 0.6338 0.5085 +vn -0.1543 0.4999 0.8522 +vn -0.4231 -0.0011 -0.9061 +vn -0.4228 -0.0003 -0.9062 +vn 0.4227 0.0003 0.9063 +vn -0.4225 0.0002 -0.9064 +vn -0.4234 -0.0010 -0.9059 +vn 0.4225 -0.0001 0.9063 +vn -0.4223 0.0004 -0.9064 +vn -0.4226 -0.0001 -0.9063 +vn -0.4163 0.0055 -0.9092 +vn 0.4219 -0.0006 0.9067 +vn -0.8322 -0.3920 -0.3923 +vn -0.7992 -0.4417 -0.4076 +vn -0.7520 0.5000 -0.4296 +vn -0.6938 -0.5568 -0.4567 +vn -0.5554 -0.6480 -0.5212 +vn -0.6429 -0.5966 -0.4804 +vn -0.2991 -0.7071 -0.6407 +vn 0.0567 -0.5883 -0.8067 +vn 0.1114 -0.5432 -0.8322 +vn 0.2016 -0.4416 -0.8743 +vn 0.1544 0.4999 -0.8522 +vn -0.0148 0.6339 -0.7733 +vn -0.7519 0.5000 -0.4297 +vn -0.7540 -0.5545 0.3520 +vn -0.7076 -0.6247 0.3302 +vn -0.6408 0.7071 0.2989 +vn -0.5585 -0.7875 0.2606 +vn -0.3628 -0.9164 0.1694 +vn 0.5027 -0.8321 -0.2343 +vn 0.7079 -0.6247 -0.3295 +vn 0.6410 0.7070 -0.2986 +vn 0.4017 0.8964 -0.1873 +vn -0.6406 0.7072 0.2991 +vn 0.4231 0.0015 0.9061 +vn -0.4227 -0.0002 -0.9063 +vn 0.4227 0.0002 0.9063 +vn 0.4226 0.0001 0.9063 +vn -0.4228 -0.0006 -0.9062 +vn 0.4230 0.0010 0.9061 +vn -0.4228 -0.0002 -0.9062 +vn -0.4210 0.0013 -0.9070 +vn 0.4165 -0.0051 0.9091 +vn -0.7542 -0.5544 0.3519 +vn -0.7236 -0.6022 0.3372 +vn -0.6410 -0.7071 0.2985 +vn 0.5870 -0.7619 -0.2737 +vn 0.8107 0.4471 -0.3780 +vn 0.4053 0.8945 -0.1888 +vn 0.3629 0.9164 -0.1690 +vn 0.5586 0.7875 -0.2604 +vn -0.5802 0.7682 0.2706 +vn 0.3142 0.9380 -0.1464 +vn -0.6409 0.7071 0.2987 +vn -0.2345 -0.3920 0.8896 +vn -0.2127 -0.4258 0.8794 +vn -0.1544 -0.5001 0.8521 +vn 0.7139 -0.5389 0.4472 +vn 0.8720 0.3163 0.3735 +vn 0.5854 0.6325 0.5072 +vn 0.5554 0.6480 0.5212 +vn 0.6938 0.5568 0.4567 +vn -0.1114 0.5431 0.8322 +vn 0.5210 0.6632 0.5374 +vn -0.4227 -0.0004 -0.9063 +vn 0.4225 -0.0003 0.9064 +vn -0.4221 0.0014 -0.9066 +vn 0.4228 0.0006 0.9062 +vn -0.4235 -0.0025 -0.9059 +vn 0.4228 0.0002 0.9062 +vn -0.4261 -0.0029 -0.9047 +vn -0.8321 -0.3922 -0.3922 +vn -0.8104 -0.4258 -0.4024 +vn -0.7520 -0.5000 -0.4296 +vn 0.1162 -0.5388 -0.8344 +vn 0.2745 0.3159 -0.9082 +vn -0.0123 0.6325 -0.7745 +vn -0.0423 0.6479 -0.7605 +vn 0.0961 0.5568 -0.8250 +vn -0.7091 0.5431 -0.4496 +vn -0.0767 0.6632 -0.7445 +vn -0.7520 0.4999 -0.4296 +vn -0.7542 -0.5546 0.3517 +vn -0.7234 -0.6022 0.3376 +vn -0.6408 -0.7071 0.2990 +vn 0.5868 -0.7619 -0.2740 +vn 0.8105 0.4472 -0.3782 +vn 0.4052 0.8945 -0.1891 +vn 0.3628 0.9164 -0.1694 +vn 0.5585 0.7875 -0.2606 +vn 0.3142 0.9380 -0.1465 +vn 0.4222 -0.0005 0.9065 +vn 0.4221 0.0007 0.9065 +vn -0.4225 -0.0002 -0.9064 +vn 0.4230 -0.0005 0.9061 +vn 0.4229 -0.0004 0.9062 +vn -0.4228 0.0002 -0.9062 +vn 0.4229 -0.0003 0.9062 +vn -0.4223 -0.0004 -0.9065 +vn 0.4224 0.0003 0.9064 +vn -0.5026 0.8321 0.2347 +vn -0.6407 0.7071 0.2991 +vn -0.5793 0.7690 0.2703 +vn -0.6964 0.6402 0.3244 +vn -0.6410 -0.7070 0.2988 +vn -0.1908 -0.9776 0.0890 +vn -0.1107 -0.9925 0.0516 +vn 0.6409 -0.7071 -0.2987 +vn 0.8109 0.4471 -0.3775 +vn 0.7820 0.5055 -0.3647 +vn 0.7075 0.6247 -0.3303 +vn -0.5828 0.7657 0.2721 +vn -0.0566 0.5883 0.8066 +vn 0.2990 0.7071 0.6408 +vn -0.1544 0.5000 0.8521 +vn -0.1108 0.5437 0.8319 +vn -0.1934 0.4526 0.8705 +vn -0.1543 -0.4999 0.8522 +vn 0.1639 -0.6913 0.7037 +vn 0.2206 -0.7018 0.6774 +vn 0.7519 -0.5000 0.4296 +vn 0.8518 0.3574 0.3831 +vn 0.7992 0.4417 0.4076 +vn -0.1134 0.5415 0.8330 +vn -0.4227 0.0001 -0.9063 +vn -0.4230 0.0005 -0.9061 +vn 0.4227 -0.0001 0.9063 +vn 0.4225 0.0002 0.9064 +vn -0.4221 -0.0007 -0.9065 +vn 0.4228 -0.0003 0.9062 +vn -0.6543 0.5884 -0.4751 +vn -0.2988 0.7071 -0.6409 +vn -0.7520 0.5000 -0.4295 +vn -0.7085 0.5437 -0.4499 +vn -0.7911 0.4526 -0.4114 +vn -0.7520 -0.4999 -0.4296 +vn -0.4338 -0.6912 -0.5780 +vn -0.3771 -0.7018 -0.6044 +vn 0.1543 -0.5000 -0.8522 +vn 0.2744 0.3162 -0.9081 +vn 0.2542 0.3574 -0.8987 +vn 0.2017 0.4418 -0.8742 +vn -0.7110 0.5414 -0.4487 +vn -0.5027 0.8320 0.2344 +vn -0.6411 0.7071 0.2984 +vn -0.5794 0.7690 0.2701 +vn -0.6962 0.6402 0.3247 +vn -0.6409 -0.7071 0.2989 +vn -0.1908 -0.9776 0.0889 +vn 0.6407 -0.7071 -0.2991 +vn 0.8105 0.4471 -0.3784 +vn 0.7078 0.6247 -0.3297 +vn -0.5830 0.7657 0.2716 +o head +usemtl frame +f 1//1 2//1 30//1 29//1 +f 2//2 3//2 31//2 30//2 +f 3//1 4//1 32//1 31//1 +f 4//3 5//3 33//3 32//3 +f 5//1 6//1 34//1 33//1 +f 6//4 7//4 35//4 34//4 +f 7//1 8//1 36//1 35//1 +f 8//5 9//5 37//5 36//5 +f 9//6 10//6 38//6 37//6 +f 10//7 11//7 39//7 38//7 +f 11//8 12//8 40//8 39//8 +f 12//9 13//9 41//9 40//9 +f 13//10 14//10 42//10 41//10 +f 14//11 15//11 43//11 42//11 +f 15//9 16//9 44//9 43//9 +f 16//7 17//7 45//7 44//7 +f 17//12 18//12 46//12 45//12 +f 18//13 19//13 47//13 46//13 +f 19//14 20//14 48//14 47//14 +f 20//15 21//15 49//15 48//15 +f 21//16 22//16 50//16 49//16 +f 22//17 23//17 51//17 50//17 +f 23//18 24//18 52//18 51//18 +f 24//19 25//19 53//19 52//19 +f 25//20 26//20 54//20 53//20 +f 26//21 27//21 55//21 54//21 +f 27//22 28//22 56//22 55//22 +f 28//18 1//18 29//18 56//18 +f 29//23 30//23 58//23 57//23 +f 30//24 31//24 59//24 58//24 +f 31//23 32//23 60//23 59//23 +f 32//25 33//25 61//25 60//25 +f 33//23 34//23 62//23 61//23 +f 34//26 35//26 63//26 62//26 +f 35//23 36//23 64//23 63//23 +f 36//27 37//27 65//27 64//27 +f 37//28 38//28 66//28 65//28 +f 38//29 39//29 67//29 66//29 +f 39//30 40//30 68//30 67//30 +f 40//31 41//31 69//31 68//31 +f 41//32 42//32 70//32 69//32 +f 42//33 43//33 71//33 70//33 +f 43//31 44//31 72//31 71//31 +f 44//29 45//29 73//29 72//29 +f 45//34 46//34 74//34 73//34 +f 46//35 47//35 75//35 74//35 +f 47//36 48//36 76//36 75//36 +f 48//37 49//37 77//37 76//37 +f 49//38 50//38 78//38 77//38 +f 50//39 51//39 79//39 78//39 +f 51//40 52//40 80//40 79//40 +f 52//41 53//41 81//41 80//41 +f 53//42 54//42 82//42 81//42 +f 54//43 55//43 83//43 82//43 +f 55//44 56//44 84//44 83//44 +f 56//40 29//40 57//40 84//40 +f 1//45 28//45 27//45 +f 83//46 84//46 57//46 +f 1//45 27//45 26//45 +f 82//46 83//46 57//46 +f 1//45 26//45 25//45 +f 81//46 82//46 57//46 +f 23//45 22//45 21//45 +f 77//46 78//46 79//46 +f 24//45 23//45 21//45 +f 77//46 79//46 80//46 +f 25//45 24//45 21//45 +f 77//46 80//46 81//46 +f 1//45 25//45 21//45 +f 77//46 81//46 57//46 +f 21//45 20//45 19//45 +f 75//46 76//46 77//46 +f 21//45 19//45 18//45 +f 74//46 75//46 77//46 +f 1//45 21//45 18//45 +f 74//46 77//46 57//46 +f 1//45 18//45 17//45 +f 73//46 74//46 57//46 +f 1//45 17//45 16//45 +f 72//46 73//46 57//46 +f 1//45 16//45 15//45 +f 71//46 72//46 57//46 +f 14//45 13//45 12//45 +f 68//46 69//46 70//46 +f 15//45 14//45 12//45 +f 68//46 70//46 71//46 +f 15//45 12//45 11//45 +f 67//46 68//46 71//46 +f 15//45 11//45 10//45 +f 66//46 67//46 71//46 +f 15//45 10//45 9//45 +f 65//46 66//46 71//46 +f 15//45 9//45 8//45 +f 64//46 65//46 71//46 +f 15//45 8//45 7//45 +f 63//46 64//46 71//46 +f 15//45 7//45 6//45 +f 62//46 63//46 71//46 +f 15//45 6//45 5//45 +f 61//46 62//46 71//46 +f 15//45 5//45 4//45 +f 60//46 61//46 71//46 +f 15//45 4//45 3//45 +f 59//46 60//46 71//46 +f 1//45 15//45 3//45 +f 59//46 71//46 57//46 +f 3//45 2//45 1//45 +f 57//46 58//46 59//46 +usemtl glass +f 85//45 97//45 96//45 +f 122//46 123//46 111//46 +f 85//45 96//45 95//45 +f 121//46 122//46 111//46 +f 85//45 95//45 94//45 +f 120//46 121//46 111//46 +f 85//45 94//45 93//45 +f 119//46 120//46 111//46 +f 85//45 93//45 92//45 +f 118//46 119//46 111//46 +f 85//45 92//45 91//45 +f 117//46 118//46 111//46 +f 85//45 91//45 90//45 +f 116//46 117//46 111//46 +f 85//45 90//45 89//45 +f 115//46 116//46 111//46 +f 89//45 88//45 87//45 +f 113//46 114//46 115//46 +f 85//45 89//45 87//45 +f 113//46 115//46 111//46 +f 87//45 86//45 85//45 +f 111//46 112//46 113//46 +f 85//47 86//47 99//47 98//47 +f 86//48 87//48 100//48 99//48 +f 87//47 88//47 101//47 100//47 +f 88//49 89//49 102//49 101//49 +f 89//50 90//50 103//50 102//50 +f 90//51 91//51 104//51 103//51 +f 91//52 92//52 105//52 104//52 +f 92//53 93//53 106//53 105//53 +f 93//54 94//54 107//54 106//54 +f 94//55 95//55 108//55 107//55 +f 95//56 96//56 109//56 108//56 +f 96//57 97//57 110//57 109//57 +f 97//58 85//58 98//58 110//58 +f 98//59 99//59 112//59 111//59 +f 99//60 100//60 113//60 112//60 +f 100//59 101//59 114//59 113//59 +f 101//61 102//61 115//61 114//61 +f 102//62 103//62 116//62 115//62 +f 103//63 104//63 117//63 116//63 +f 104//64 105//64 118//64 117//64 +f 105//65 106//65 119//65 118//65 +f 106//66 107//66 120//66 119//66 +f 107//67 108//67 121//67 120//67 +f 108//68 109//68 122//68 121//68 +f 109//69 110//69 123//69 122//69 +f 110//70 98//70 111//70 123//70 +f 124//45 136//45 135//45 +f 161//46 162//46 150//46 +f 124//45 135//45 134//45 +f 160//46 161//46 150//46 +f 124//45 134//45 133//45 +f 159//46 160//46 150//46 +f 124//45 133//45 132//45 +f 158//46 159//46 150//46 +f 124//45 132//45 131//45 +f 157//46 158//46 150//46 +f 124//45 131//45 130//45 +f 156//46 157//46 150//46 +f 124//45 130//45 129//45 +f 155//46 156//46 150//46 +f 124//45 129//45 128//45 +f 154//46 155//46 150//46 +f 128//45 127//45 126//45 +f 152//46 153//46 154//46 +f 124//45 128//45 126//45 +f 152//46 154//46 150//46 +f 126//45 125//45 124//45 +f 150//46 151//46 152//46 +f 124//71 125//71 138//71 137//71 +f 125//72 126//72 139//72 138//72 +f 126//71 127//71 140//71 139//71 +f 127//73 128//73 141//73 140//73 +f 128//74 129//74 142//74 141//74 +f 129//75 130//75 143//75 142//75 +f 130//76 131//76 144//76 143//76 +f 131//77 132//77 145//77 144//77 +f 132//78 133//78 146//78 145//78 +f 133//79 134//79 147//79 146//79 +f 134//80 135//80 148//80 147//80 +f 135//81 136//81 149//81 148//81 +f 136//82 124//82 137//82 149//82 +f 137//47 138//47 151//47 150//47 +f 138//48 139//48 152//48 151//48 +f 139//47 140//47 153//47 152//47 +f 140//49 141//49 154//49 153//49 +f 141//50 142//50 155//50 154//50 +f 142//51 143//51 156//51 155//51 +f 143//52 144//52 157//52 156//52 +f 144//53 145//53 158//53 157//53 +f 145//54 146//54 159//54 158//54 +f 146//55 147//55 160//55 159//55 +f 147//56 148//56 161//56 160//56 +f 148//57 149//57 162//57 161//57 +f 149//58 137//58 150//58 162//58 +usemtl champagne +f 163//45 184//45 183//45 +f 227//46 228//46 207//46 +f 163//45 183//45 182//45 +f 226//46 227//46 207//46 +f 163//45 182//45 181//45 +f 225//46 226//46 207//46 +f 181//45 180//45 179//45 +f 223//46 224//46 225//46 +f 178//45 177//45 176//45 +f 220//46 221//46 222//46 +f 179//45 178//45 176//45 +f 220//46 222//46 223//46 +f 181//45 179//45 176//45 +f 220//46 223//46 225//46 +f 163//45 181//45 176//45 +f 220//46 225//46 207//46 +f 163//45 176//45 175//45 +f 219//46 220//46 207//46 +f 163//45 175//45 174//45 +f 218//46 219//46 207//46 +f 174//45 173//45 172//45 +f 216//46 217//46 218//46 +f 163//45 174//45 172//45 +f 216//46 218//46 207//46 +f 163//45 172//45 171//45 +f 215//46 216//46 207//46 +f 163//45 171//45 170//45 +f 214//46 215//46 207//46 +f 163//45 170//45 169//45 +f 213//46 214//46 207//46 +f 169//45 168//45 167//45 +f 211//46 212//46 213//46 +f 163//45 169//45 167//45 +f 211//46 213//46 207//46 +f 163//45 167//45 166//45 +f 210//46 211//46 207//46 +f 163//45 166//45 165//45 +f 209//46 210//46 207//46 +f 165//45 164//45 163//45 +f 207//46 208//46 209//46 +f 163//83 164//83 186//83 185//83 +f 164//84 165//84 187//84 186//84 +f 165//47 166//47 188//47 187//47 +f 166//85 167//85 189//85 188//85 +f 167//47 168//47 190//47 189//47 +f 168//86 169//86 191//86 190//86 +f 169//87 170//87 192//87 191//87 +f 170//88 171//88 193//88 192//88 +f 171//89 172//89 194//89 193//89 +f 172//90 173//90 195//90 194//90 +f 173//91 174//91 196//91 195//91 +f 174//87 175//87 197//87 196//87 +f 175//92 176//92 198//92 197//92 +f 176//93 177//93 199//93 198//93 +f 177//94 178//94 200//94 199//94 +f 178//95 179//95 201//95 200//95 +f 179//96 180//96 202//96 201//96 +f 180//97 181//97 203//97 202//97 +f 181//98 182//98 204//98 203//98 +f 182//99 183//99 205//99 204//99 +f 183//95 184//95 206//95 205//95 +f 184//100 163//100 185//100 206//100 +f 185//101 186//101 208//101 207//101 +f 186//102 187//102 209//102 208//102 +f 187//103 188//103 210//103 209//103 +f 188//104 189//104 211//104 210//104 +f 189//103 190//103 212//103 211//103 +f 190//105 191//105 213//105 212//105 +f 191//106 192//106 214//106 213//106 +f 192//107 193//107 215//107 214//107 +f 193//108 194//108 216//108 215//108 +f 194//109 195//109 217//109 216//109 +f 195//110 196//110 218//110 217//110 +f 196//106 197//106 219//106 218//106 +f 197//111 198//111 220//111 219//111 +f 198//112 199//112 221//112 220//112 +f 199//113 200//113 222//113 221//113 +f 200//114 201//114 223//114 222//114 +f 201//115 202//115 224//115 223//115 +f 202//116 203//116 225//116 224//116 +f 203//117 204//117 226//117 225//117 +f 204//118 205//118 227//118 226//118 +f 205//114 206//114 228//114 227//114 +f 206//119 185//119 207//119 228//119 +f 229//45 250//45 249//45 +f 293//46 294//46 273//46 +f 229//45 249//45 248//45 +f 292//46 293//46 273//46 +f 229//45 248//45 247//45 +f 291//46 292//46 273//46 +f 247//45 246//45 245//45 +f 289//46 290//46 291//46 +f 244//45 243//45 242//45 +f 286//46 287//46 288//46 +f 245//45 244//45 242//45 +f 286//46 288//46 289//46 +f 247//45 245//45 242//45 +f 286//46 289//46 291//46 +f 229//45 247//45 242//45 +f 286//46 291//46 273//46 +f 229//45 242//45 241//45 +f 285//46 286//46 273//46 +f 229//45 241//45 240//45 +f 284//46 285//46 273//46 +f 240//45 239//45 238//45 +f 282//46 283//46 284//46 +f 229//45 240//45 238//45 +f 282//46 284//46 273//46 +f 229//45 238//45 237//45 +f 281//46 282//46 273//46 +f 229//45 237//45 236//45 +f 280//46 281//46 273//46 +f 229//45 236//45 235//45 +f 279//46 280//46 273//46 +f 235//45 234//45 233//45 +f 277//46 278//46 279//46 +f 229//45 235//45 233//45 +f 277//46 279//46 273//46 +f 229//45 233//45 232//45 +f 276//46 277//46 273//46 +f 229//45 232//45 231//45 +f 275//46 276//46 273//46 +f 231//45 230//45 229//45 +f 273//46 274//46 275//46 +f 229//120 230//120 252//120 251//120 +f 230//121 231//121 253//121 252//121 +f 231//122 232//122 254//122 253//122 +f 232//123 233//123 255//123 254//123 +f 233//122 234//122 256//122 255//122 +f 234//124 235//124 257//124 256//124 +f 235//125 236//125 258//125 257//125 +f 236//126 237//126 259//126 258//126 +f 237//127 238//127 260//127 259//127 +f 238//128 239//128 261//128 260//128 +f 239//129 240//129 262//129 261//129 +f 240//125 241//125 263//125 262//125 +f 241//130 242//130 264//130 263//130 +f 242//131 243//131 265//131 264//131 +f 243//132 244//132 266//132 265//132 +f 244//133 245//133 267//133 266//133 +f 245//134 246//134 268//134 267//134 +f 246//135 247//135 269//135 268//135 +f 247//136 248//136 270//136 269//136 +f 248//137 249//137 271//137 270//137 +f 249//133 250//133 272//133 271//133 +f 250//138 229//138 251//138 272//138 +f 251//83 252//83 274//83 273//83 +f 252//84 253//84 275//84 274//84 +f 253//47 254//47 276//47 275//47 +f 254//85 255//85 277//85 276//85 +f 255//47 256//47 278//47 277//47 +f 256//86 257//86 279//86 278//86 +f 257//87 258//87 280//87 279//87 +f 258//88 259//88 281//88 280//88 +f 259//89 260//89 282//89 281//89 +f 260//90 261//90 283//90 282//90 +f 261//91 262//91 284//91 283//91 +f 262//87 263//87 285//87 284//87 +f 263//92 264//92 286//92 285//92 +f 264//93 265//93 287//93 286//93 +f 265//94 266//94 288//94 287//94 +f 266//95 267//95 289//95 288//95 +f 267//96 268//96 290//96 289//96 +f 268//97 269//97 291//97 290//97 +f 269//98 270//98 292//98 291//98 +f 270//99 271//99 293//99 292//99 +f 271//95 272//95 294//95 293//95 +f 272//100 251//100 273//100 294//100 +f 295//45 308//45 307//45 +f 335//46 336//46 323//46 +f 295//45 307//45 306//45 +f 334//46 335//46 323//46 +f 295//45 306//45 305//45 +f 333//46 334//46 323//46 +f 295//45 305//45 304//45 +f 332//46 333//46 323//46 +f 295//45 304//45 303//45 +f 331//46 332//46 323//46 +f 302//45 301//45 300//45 +f 328//46 329//46 330//46 +f 302//45 300//45 299//45 +f 327//46 328//46 330//46 +f 303//45 302//45 299//45 +f 327//46 330//46 331//46 +f 303//45 299//45 298//45 +f 326//46 327//46 331//46 +f 295//45 303//45 298//45 +f 326//45 331//45 323//45 +f 298//45 297//45 296//45 +f 324//46 325//46 326//46 +f 298//45 296//45 295//45 +f 323//46 324//46 326//46 +f 295//139 296//139 310//139 309//139 +f 296//58 297//58 311//58 310//58 +f 297//140 298//140 312//140 311//140 +f 298//139 299//139 313//139 312//139 +f 299//141 300//141 314//141 313//141 +f 300//142 301//142 315//142 314//142 +f 301//47 302//47 316//47 315//47 +f 302//143 303//143 317//143 316//143 +f 303//47 304//47 318//47 317//47 +f 304//144 305//144 319//144 318//144 +f 305//145 306//145 320//145 319//145 +f 306//146 307//146 321//146 320//146 +f 307//93 308//93 322//93 321//93 +f 308//147 295//147 309//147 322//147 +f 309//148 310//148 324//148 323//148 +f 310//149 311//149 325//149 324//149 +f 311//150 312//150 326//150 325//150 +f 312//148 313//148 327//148 326//148 +f 313//151 314//151 328//151 327//151 +f 314//152 315//152 329//152 328//152 +f 315//103 316//103 330//103 329//103 +f 316//153 317//153 331//153 330//153 +f 317//103 318//103 332//103 331//103 +f 318//154 319//154 333//154 332//154 +f 319//155 320//155 334//155 333//155 +f 320//156 321//156 335//156 334//156 +f 321//112 322//112 336//112 335//112 +f 322//157 309//157 323//157 336//157 +f 337//45 350//45 349//45 +f 377//46 378//46 365//46 +f 337//45 349//45 348//45 +f 376//46 377//46 365//46 +f 337//45 348//45 347//45 +f 375//46 376//46 365//46 +f 337//45 347//45 346//45 +f 374//46 375//46 365//46 +f 337//45 346//45 345//45 +f 373//46 374//46 365//46 +f 344//45 343//45 342//45 +f 370//46 371//46 372//46 +f 344//45 342//45 341//45 +f 369//46 370//46 372//46 +f 345//45 344//45 341//45 +f 369//46 372//46 373//46 +f 345//45 341//45 340//45 +f 368//46 369//46 373//46 +f 337//46 345//46 340//46 +f 368//46 373//46 365//46 +f 340//45 339//45 338//45 +f 366//46 367//46 368//46 +f 340//45 338//45 337//45 +f 365//46 366//46 368//46 +f 337//158 338//158 352//158 351//158 +f 338//159 339//159 353//159 352//159 +f 339//160 340//160 354//160 353//160 +f 340//158 341//158 355//158 354//158 +f 341//161 342//161 356//161 355//161 +f 342//162 343//162 357//162 356//162 +f 343//122 344//122 358//122 357//122 +f 344//163 345//163 359//163 358//163 +f 345//122 346//122 360//122 359//122 +f 346//164 347//164 361//164 360//164 +f 347//165 348//165 362//165 361//165 +f 348//166 349//166 363//166 362//166 +f 349//131 350//131 364//131 363//131 +f 350//167 337//167 351//167 364//167 +f 351//139 352//139 366//139 365//139 +f 352//58 353//58 367//58 366//58 +f 353//140 354//140 368//140 367//140 +f 354//139 355//139 369//139 368//139 +f 355//141 356//141 370//141 369//141 +f 356//142 357//142 371//142 370//142 +f 357//47 358//47 372//47 371//47 +f 358//143 359//143 373//143 372//143 +f 359//47 360//47 374//47 373//47 +f 360//144 361//144 375//144 374//144 +f 361//145 362//145 376//145 375//145 +f 362//146 363//146 377//146 376//146 +f 363//93 364//93 378//93 377//93 +f 364//147 351//147 365//147 378//147 +f 463//45 475//45 474//45 +f 500//46 501//46 489//46 +f 474//45 473//45 472//45 +f 498//46 499//46 500//46 +f 474//45 472//45 471//45 +f 497//46 498//46 500//46 +f 471//45 470//45 469//45 +f 495//46 496//46 497//46 +f 474//45 471//45 469//45 +f 495//46 497//46 500//46 +f 463//45 474//45 469//45 +f 495//45 500//45 489//45 +f 463//45 469//45 468//45 +f 494//46 495//46 489//46 +f 468//45 467//45 466//45 +f 492//46 493//46 494//46 +f 468//45 466//45 465//45 +f 491//46 492//46 494//46 +f 468//45 465//45 464//45 +f 490//46 491//46 494//46 +f 468//45 464//45 463//45 +f 489//45 490//45 494//45 +f 463//168 464//168 477//168 476//168 +f 464//47 465//47 478//47 477//47 +f 465//139 466//139 479//139 478//139 +f 466//169 467//169 480//169 479//169 +f 467//144 468//144 481//144 480//144 +f 468//170 469//170 482//170 481//170 +f 469//171 470//171 483//171 482//171 +f 470//172 471//172 484//172 483//172 +f 471//173 472//173 485//173 484//173 +f 472//174 473//174 486//174 485//174 +f 473//95 474//95 487//95 486//95 +f 474//175 475//175 488//175 487//175 +f 475//58 463//58 476//58 488//58 +f 476//176 477//176 490//176 489//176 +f 477//103 478//103 491//103 490//103 +f 478//148 479//148 492//148 491//148 +f 479//177 480//177 493//177 492//177 +f 480//178 481//178 494//178 493//178 +f 481//179 482//179 495//179 494//179 +f 482//180 483//180 496//180 495//180 +f 483//181 484//181 497//181 496//181 +f 484//182 485//182 498//182 497//182 +f 485//183 486//183 499//183 498//183 +f 486//114 487//114 500//114 499//114 +f 487//184 488//184 501//184 500//184 +f 488//185 476//185 489//185 501//185 +f 502//45 514//45 513//45 +f 539//46 540//46 528//46 +f 513//45 512//45 511//45 +f 537//46 538//46 539//46 +f 513//45 511//45 510//45 +f 536//46 537//46 539//46 +f 510//45 509//45 508//45 +f 534//46 535//46 536//46 +f 513//45 510//45 508//45 +f 534//46 536//46 539//46 +f 502//46 513//46 508//46 +f 534//46 539//46 528//46 +f 502//45 508//45 507//45 +f 533//46 534//46 528//46 +f 507//45 506//45 505//45 +f 531//46 532//46 533//46 +f 507//45 505//45 504//45 +f 530//46 531//46 533//46 +f 507//45 504//45 503//45 +f 529//46 530//46 533//46 +f 507//46 503//46 502//46 +f 528//46 529//46 533//46 +f 502//186 503//186 516//186 515//186 +f 503//122 504//122 517//122 516//122 +f 504//158 505//158 518//158 517//158 +f 505//187 506//187 519//187 518//187 +f 506//188 507//188 520//188 519//188 +f 507//189 508//189 521//189 520//189 +f 508//190 509//190 522//190 521//190 +f 509//191 510//191 523//191 522//191 +f 510//192 511//192 524//192 523//192 +f 511//193 512//193 525//193 524//193 +f 512//133 513//133 526//133 525//133 +f 513//194 514//194 527//194 526//194 +f 514//195 502//195 515//195 527//195 +f 515//168 516//168 529//168 528//168 +f 516//47 517//47 530//47 529//47 +f 517//139 518//139 531//139 530//139 +f 518//169 519//169 532//169 531//169 +f 519//144 520//144 533//144 532//144 +f 520//170 521//170 534//170 533//170 +f 521//171 522//171 535//171 534//171 +f 522//172 523//172 536//172 535//172 +f 523//173 524//173 537//173 536//173 +f 524//174 525//174 538//174 537//174 +f 525//95 526//95 539//95 538//95 +f 526//175 527//175 540//175 539//175 +f 527//58 515//58 528//58 540//58 +usemtl dark +f 379//45 386//45 385//45 +f 401//46 402//46 395//46 +f 379//45 385//45 384//45 +f 400//46 401//46 395//46 +f 379//45 384//45 383//45 +f 399//46 400//46 395//46 +f 379//45 383//45 382//45 +f 398//46 399//46 395//46 +f 379//45 382//45 381//45 +f 397//46 398//46 395//46 +f 381//45 380//45 379//45 +f 395//46 396//46 397//46 +f 379//95 380//95 388//95 387//95 +f 380//139 381//139 389//139 388//139 +f 381//196 382//196 390//196 389//196 +f 382//197 383//197 391//197 390//197 +f 383//88 384//88 392//88 391//88 +f 384//174 385//174 393//174 392//174 +f 385//95 386//95 394//95 393//95 +f 386//58 379//58 387//58 394//58 +f 387//114 388//114 396//114 395//114 +f 388//148 389//148 397//148 396//148 +f 389//198 390//198 398//198 397//198 +f 390//199 391//199 399//199 398//199 +f 391//200 392//200 400//200 399//200 +f 392//201 393//201 401//201 400//201 +f 393//114 394//114 402//114 401//114 +f 394//70 387//70 395//70 402//70 +f 403//45 410//45 409//45 +f 425//46 426//46 419//46 +f 403//45 409//45 408//45 +f 424//46 425//46 419//46 +f 403//45 408//45 407//45 +f 423//46 424//46 419//46 +f 403//45 407//45 406//45 +f 422//46 423//46 419//46 +f 403//45 406//45 405//45 +f 421//46 422//46 419//46 +f 405//45 404//45 403//45 +f 419//46 420//46 421//46 +f 403//133 404//133 412//133 411//133 +f 404//158 405//158 413//158 412//158 +f 405//202 406//202 414//202 413//202 +f 406//203 407//203 415//203 414//203 +f 407//204 408//204 416//204 415//204 +f 408//205 409//205 417//205 416//205 +f 409//133 410//133 418//133 417//133 +f 410//82 403//82 411//82 418//82 +f 411//95 412//95 420//95 419//95 +f 412//139 413//139 421//139 420//139 +f 413//196 414//196 422//196 421//196 +f 414//197 415//197 423//197 422//197 +f 415//88 416//88 424//88 423//88 +f 416//174 417//174 425//174 424//174 +f 417//95 418//95 426//95 425//95 +f 418//58 411//58 419//58 426//58 +f 427//45 432//45 431//45 +f 443//46 444//46 439//46 +f 427//45 431//45 430//45 +f 442//46 443//46 439//46 +f 427//45 430//45 429//45 +f 441//46 442//46 439//46 +f 429//45 428//45 427//45 +f 439//46 440//46 441//46 +f 427//206 428//206 434//206 433//206 +f 428//207 429//207 435//207 434//207 +f 429//92 430//92 436//92 435//92 +f 430//208 431//208 437//208 436//208 +f 431//209 432//209 438//209 437//209 +f 432//210 427//210 433//210 438//210 +f 433//211 434//211 440//211 439//211 +f 434//212 435//212 441//212 440//212 +f 435//111 436//111 442//111 441//111 +f 436//213 437//213 443//213 442//213 +f 437//214 438//214 444//214 443//214 +f 438//215 433//215 439//215 444//215 +f 445//45 450//45 449//45 +f 461//46 462//46 457//46 +f 445//45 449//45 448//45 +f 460//46 461//46 457//46 +f 445//45 448//45 447//45 +f 459//46 460//46 457//46 +f 447//45 446//45 445//45 +f 457//46 458//46 459//46 +f 445//216 446//216 452//216 451//216 +f 446//217 447//217 453//217 452//217 +f 447//130 448//130 454//130 453//130 +f 448//218 449//218 455//218 454//218 +f 449//219 450//219 456//219 455//219 +f 450//220 445//220 451//220 456//220 +f 451//206 452//206 458//206 457//206 +f 452//207 453//207 459//207 458//207 +f 453//92 454//92 460//92 459//92 +f 454//208 455//208 461//208 460//208 +f 455//209 456//209 462//209 461//209 +f 456//210 451//210 457//210 462//210 +o neck +usemtl frame +f 541//221 542//221 559//221 558//221 +f 542//222 543//222 560//222 559//222 +f 543//223 544//223 561//223 560//223 +f 544//224 545//224 562//224 561//224 +f 545//225 546//225 563//225 562//225 +f 546//226 547//226 564//226 563//226 +f 547//227 548//227 565//227 564//227 +f 548//228 549//228 566//228 565//228 +f 549//229 550//229 567//229 566//229 +f 550//230 551//230 568//230 567//230 +f 551//231 552//231 569//231 568//231 +f 552//232 553//232 570//232 569//232 +f 553//233 554//233 571//233 570//233 +f 554//234 555//234 572//234 571//234 +f 555//235 556//235 573//235 572//235 +f 556//236 557//236 574//236 573//236 +f 557//237 541//237 558//237 574//237 +f 558//238 559//238 576//238 575//238 +f 559//239 560//239 577//239 576//239 +f 560//240 561//240 578//240 577//240 +f 561//241 562//241 579//241 578//241 +f 562//242 563//242 580//242 579//242 +f 563//243 564//243 581//243 580//243 +f 564//244 565//244 582//244 581//244 +f 565//245 566//245 583//245 582//245 +f 566//246 567//246 584//246 583//246 +f 567//247 568//247 585//247 584//247 +f 568//248 569//248 586//248 585//248 +f 569//249 570//249 587//249 586//249 +f 570//250 571//250 588//250 587//250 +f 571//251 572//251 589//251 588//251 +f 572//252 573//252 590//252 589//252 +f 573//253 574//253 591//253 590//253 +f 574//254 558//254 575//254 591//254 +f 541//45 557//45 556//45 +f 590//46 591//46 575//46 +f 541//45 556//45 555//45 +f 589//46 590//46 575//46 +f 541//45 555//45 554//45 +f 588//46 589//46 575//46 +f 541//45 554//45 553//45 +f 587//46 588//46 575//46 +f 541//45 553//45 552//45 +f 586//46 587//46 575//46 +f 541//45 552//45 551//45 +f 585//46 586//46 575//46 +f 541//45 551//45 550//45 +f 584//46 585//46 575//46 +f 541//45 550//45 549//45 +f 583//46 584//46 575//46 +f 541//45 549//45 548//45 +f 582//46 583//46 575//46 +f 541//45 548//45 547//45 +f 581//46 582//46 575//46 +f 541//45 547//45 546//45 +f 580//46 581//46 575//46 +f 541//45 546//45 545//45 +f 579//46 580//46 575//46 +f 541//45 545//45 544//45 +f 578//46 579//46 575//46 +f 541//45 544//45 543//45 +f 577//46 578//46 575//46 +f 543//45 542//45 541//45 +f 575//46 576//46 577//46 +usemtl champagne +f 592//45 608//45 607//45 +f 641//46 642//46 626//46 +f 592//45 607//45 606//45 +f 640//46 641//46 626//46 +f 592//45 606//45 605//45 +f 639//46 640//46 626//46 +f 592//45 605//45 604//45 +f 638//46 639//46 626//46 +f 592//45 604//45 603//45 +f 637//46 638//46 626//46 +f 592//45 603//45 602//45 +f 636//46 637//46 626//46 +f 592//45 602//45 601//45 +f 635//46 636//46 626//46 +f 592//45 601//45 600//45 +f 634//46 635//46 626//46 +f 592//45 600//45 599//45 +f 633//46 634//46 626//46 +f 592//45 599//45 598//45 +f 632//46 633//46 626//46 +f 592//45 598//45 597//45 +f 631//46 632//46 626//46 +f 597//45 596//45 595//45 +f 629//46 630//46 631//46 +f 592//45 597//45 595//45 +f 629//46 631//46 626//46 +f 592//45 595//45 594//45 +f 628//46 629//46 626//46 +f 594//45 593//45 592//45 +f 626//46 627//46 628//46 +f 592//255 593//255 610//255 609//255 +f 593//58 594//58 611//58 610//58 +f 594//256 595//256 612//256 611//256 +f 595//257 596//257 613//257 612//257 +f 596//258 597//258 614//258 613//258 +f 597//259 598//259 615//259 614//259 +f 598//260 599//260 616//260 615//260 +f 599//88 600//88 617//88 616//88 +f 600//47 601//47 618//47 617//47 +f 601//261 602//261 619//261 618//261 +f 602//262 603//262 620//262 619//262 +f 603//263 604//263 621//263 620//263 +f 604//264 605//264 622//264 621//264 +f 605//265 606//265 623//265 622//265 +f 606//266 607//266 624//266 623//266 +f 607//174 608//174 625//174 624//174 +f 608//267 592//267 609//267 625//267 +f 609//268 610//268 627//268 626//268 +f 610//269 611//269 628//269 627//269 +f 611//270 612//270 629//270 628//270 +f 612//271 613//271 630//271 629//271 +f 613//272 614//272 631//272 630//272 +f 614//273 615//273 632//273 631//273 +f 615//274 616//274 633//274 632//274 +f 616//107 617//107 634//107 633//107 +f 617//275 618//275 635//275 634//275 +f 618//276 619//276 636//276 635//276 +f 619//277 620//277 637//277 636//277 +f 620//278 621//278 638//278 637//278 +f 621//279 622//279 639//279 638//279 +f 622//280 623//280 640//280 639//280 +f 623//281 624//281 641//281 640//281 +f 624//201 625//201 642//201 641//201 +f 625//282 609//282 626//282 642//282 +f 643//45 659//45 658//45 +f 692//46 693//46 677//46 +f 643//45 658//45 657//45 +f 691//46 692//46 677//46 +f 643//45 657//45 656//45 +f 690//46 691//46 677//46 +f 643//45 656//45 655//45 +f 689//46 690//46 677//46 +f 643//45 655//45 654//45 +f 688//46 689//46 677//46 +f 643//45 654//45 653//45 +f 687//46 688//46 677//46 +f 643//45 653//45 652//45 +f 686//46 687//46 677//46 +f 643//45 652//45 651//45 +f 685//46 686//46 677//46 +f 643//45 651//45 650//45 +f 684//46 685//46 677//46 +f 643//45 650//45 649//45 +f 683//46 684//46 677//46 +f 643//45 649//45 648//45 +f 682//46 683//46 677//46 +f 648//45 647//45 646//45 +f 680//46 681//46 682//46 +f 643//45 648//45 646//45 +f 680//46 682//46 677//46 +f 643//45 646//45 645//45 +f 679//46 680//46 677//46 +f 645//45 644//45 643//45 +f 677//46 678//46 679//46 +f 643//283 644//283 661//283 660//283 +f 644//284 645//284 662//284 661//284 +f 645//285 646//285 663//285 662//285 +f 646//286 647//286 664//286 663//286 +f 647//287 648//287 665//287 664//287 +f 648//288 649//288 666//288 665//288 +f 649//289 650//289 667//289 666//289 +f 650//126 651//126 668//126 667//126 +f 651//290 652//290 669//290 668//290 +f 652//291 653//291 670//291 669//291 +f 653//292 654//292 671//292 670//292 +f 654//293 655//293 672//293 671//293 +f 655//294 656//294 673//294 672//294 +f 656//295 657//295 674//295 673//295 +f 657//296 658//296 675//296 674//296 +f 658//205 659//205 676//205 675//205 +f 659//297 643//297 660//297 676//297 +f 660//255 661//255 678//255 677//255 +f 661//58 662//58 679//58 678//58 +f 662//256 663//256 680//256 679//256 +f 663//257 664//257 681//257 680//257 +f 664//258 665//258 682//258 681//258 +f 665//259 666//259 683//259 682//259 +f 666//260 667//260 684//260 683//260 +f 667//88 668//88 685//88 684//88 +f 668//47 669//47 686//47 685//47 +f 669//261 670//261 687//261 686//261 +f 670//262 671//262 688//262 687//262 +f 671//263 672//263 689//263 688//263 +f 672//264 673//264 690//264 689//264 +f 673//265 674//265 691//265 690//265 +f 674//266 675//266 692//266 691//266 +f 675//174 676//174 693//174 692//174 +f 676//267 660//267 677//267 693//267 +f 952//45 968//45 967//45 +f 1001//46 1002//46 986//46 +f 952//45 967//45 966//45 +f 1000//46 1001//46 986//46 +f 952//45 966//45 965//45 +f 999//46 1000//46 986//46 +f 952//45 965//45 964//45 +f 998//46 999//46 986//46 +f 952//45 964//45 963//45 +f 997//46 998//46 986//46 +f 952//45 963//45 962//45 +f 996//46 997//46 986//46 +f 952//45 962//45 961//45 +f 995//46 996//46 986//46 +f 952//45 961//45 960//45 +f 994//46 995//46 986//46 +f 952//45 960//45 959//45 +f 993//46 994//46 986//46 +f 959//45 958//45 957//45 +f 991//46 992//46 993//46 +f 952//45 959//45 957//45 +f 991//45 993//45 986//45 +f 952//45 957//45 956//45 +f 990//46 991//46 986//46 +f 956//45 955//45 954//45 +f 988//46 989//46 990//46 +f 952//45 956//45 954//45 +f 988//46 990//46 986//46 +f 954//45 953//45 952//45 +f 986//46 987//46 988//46 +f 952//139 953//139 970//139 969//139 +f 953//298 954//298 971//298 970//298 +f 954//299 955//299 972//299 971//299 +f 955//300 956//300 973//300 972//300 +f 956//301 957//301 974//301 973//301 +f 957//302 958//302 975//302 974//302 +f 958//88 959//88 976//88 975//88 +f 959//47 960//47 977//47 976//47 +f 960//303 961//303 978//303 977//303 +f 961//304 962//304 979//304 978//304 +f 962//88 963//88 980//88 979//88 +f 963//305 964//305 981//305 980//305 +f 964//306 965//306 982//306 981//306 +f 965//307 966//307 983//307 982//307 +f 966//308 967//308 984//308 983//308 +f 967//174 968//174 985//174 984//174 +f 968//255 952//255 969//255 985//255 +f 969//148 970//148 987//148 986//148 +f 970//309 971//309 988//309 987//309 +f 971//310 972//310 989//310 988//310 +f 972//311 973//311 990//311 989//311 +f 973//312 974//312 991//312 990//312 +f 974//313 975//313 992//313 991//313 +f 975//200 976//200 993//200 992//200 +f 976//272 977//272 994//272 993//272 +f 977//314 978//314 995//314 994//314 +f 978//315 979//315 996//315 995//315 +f 979//200 980//200 997//200 996//200 +f 980//316 981//316 998//316 997//316 +f 981//317 982//317 999//317 998//317 +f 982//318 983//318 1000//318 999//318 +f 983//319 984//319 1001//319 1000//319 +f 984//320 985//320 1002//320 1001//320 +f 985//321 969//321 986//321 1002//321 +f 1003//45 1019//45 1018//45 +f 1052//46 1053//46 1037//46 +f 1003//45 1018//45 1017//45 +f 1051//46 1052//46 1037//46 +f 1003//45 1017//45 1016//45 +f 1050//46 1051//46 1037//46 +f 1003//45 1016//45 1015//45 +f 1049//46 1050//46 1037//46 +f 1003//45 1015//45 1014//45 +f 1048//46 1049//46 1037//46 +f 1003//45 1014//45 1013//45 +f 1047//46 1048//46 1037//46 +f 1003//45 1013//45 1012//45 +f 1046//46 1047//46 1037//46 +f 1003//45 1012//45 1011//45 +f 1045//46 1046//46 1037//46 +f 1003//45 1011//45 1010//45 +f 1044//46 1045//46 1037//46 +f 1010//45 1009//45 1008//45 +f 1042//46 1043//46 1044//46 +f 1003//46 1010//46 1008//46 +f 1042//46 1044//46 1037//46 +f 1003//45 1008//45 1007//45 +f 1041//46 1042//46 1037//46 +f 1007//45 1006//45 1005//45 +f 1039//46 1040//46 1041//46 +f 1003//45 1007//45 1005//45 +f 1039//46 1041//46 1037//46 +f 1005//45 1004//45 1003//45 +f 1037//46 1038//46 1039//46 +f 1003//158 1004//158 1021//158 1020//158 +f 1004//322 1005//322 1022//322 1021//322 +f 1005//323 1006//323 1023//323 1022//323 +f 1006//324 1007//324 1024//324 1023//324 +f 1007//325 1008//325 1025//325 1024//325 +f 1008//326 1009//326 1026//326 1025//326 +f 1009//204 1010//204 1027//204 1026//204 +f 1010//287 1011//287 1028//287 1027//287 +f 1011//327 1012//327 1029//327 1028//327 +f 1012//328 1013//328 1030//328 1029//328 +f 1013//204 1014//204 1031//204 1030//204 +f 1014//329 1015//329 1032//329 1031//329 +f 1015//330 1016//330 1033//330 1032//330 +f 1016//331 1017//331 1034//331 1033//331 +f 1017//332 1018//332 1035//332 1034//332 +f 1018//333 1019//333 1036//333 1035//333 +f 1019//334 1003//334 1020//334 1036//334 +f 1020//139 1021//139 1038//139 1037//139 +f 1021//298 1022//298 1039//298 1038//298 +f 1022//299 1023//299 1040//299 1039//299 +f 1023//300 1024//300 1041//300 1040//300 +f 1024//301 1025//301 1042//301 1041//301 +f 1025//302 1026//302 1043//302 1042//302 +f 1026//88 1027//88 1044//88 1043//88 +f 1027//47 1028//47 1045//47 1044//47 +f 1028//303 1029//303 1046//303 1045//303 +f 1029//304 1030//304 1047//304 1046//304 +f 1030//88 1031//88 1048//88 1047//88 +f 1031//305 1032//305 1049//305 1048//305 +f 1032//306 1033//306 1050//306 1049//306 +f 1033//307 1034//307 1051//307 1050//307 +f 1034//308 1035//308 1052//308 1051//308 +f 1035//174 1036//174 1053//174 1052//174 +f 1036//255 1020//255 1037//255 1053//255 +usemtl glass +f 694//45 705//45 704//45 +f 728//46 729//46 718//46 +f 694//45 704//45 703//45 +f 727//46 728//46 718//46 +f 694//45 703//45 702//45 +f 726//46 727//46 718//46 +f 694//45 702//45 701//45 +f 725//46 726//46 718//46 +f 701//45 700//45 699//45 +f 723//46 724//46 725//46 +f 694//45 701//45 699//45 +f 723//46 725//46 718//46 +f 694//45 699//45 698//45 +f 722//46 723//46 718//46 +f 694//45 698//45 697//45 +f 721//46 722//46 718//46 +f 694//45 697//45 696//45 +f 720//46 721//46 718//46 +f 696//45 695//45 694//45 +f 718//46 719//46 720//46 +f 694//335 695//335 707//335 706//335 +f 695//336 696//336 708//336 707//336 +f 696//337 697//337 709//337 708//337 +f 697//338 698//338 710//338 709//338 +f 698//87 699//87 711//87 710//87 +f 699//339 700//339 712//339 711//339 +f 700//340 701//340 713//340 712//340 +f 701//341 702//341 714//341 713//341 +f 702//208 703//208 715//208 714//208 +f 703//342 704//342 716//342 715//342 +f 704//343 705//343 717//343 716//343 +f 705//58 694//58 706//58 717//58 +f 706//344 707//344 719//344 718//344 +f 707//345 708//345 720//345 719//345 +f 708//346 709//346 721//346 720//346 +f 709//347 710//347 722//347 721//347 +f 710//106 711//106 723//106 722//106 +f 711//348 712//348 724//348 723//348 +f 712//349 713//349 725//349 724//349 +f 713//350 714//350 726//350 725//350 +f 714//351 715//351 727//351 726//351 +f 715//352 716//352 728//352 727//352 +f 716//353 717//353 729//353 728//353 +f 717//70 706//70 718//70 729//70 +f 730//45 741//45 740//45 +f 764//46 765//46 754//46 +f 730//45 740//45 739//45 +f 763//46 764//46 754//46 +f 730//45 739//45 738//45 +f 762//46 763//46 754//46 +f 730//45 738//45 737//45 +f 761//46 762//46 754//46 +f 737//45 736//45 735//45 +f 759//46 760//46 761//46 +f 730//45 737//45 735//45 +f 759//46 761//46 754//46 +f 730//45 735//45 734//45 +f 758//46 759//46 754//46 +f 730//45 734//45 733//45 +f 757//46 758//46 754//46 +f 730//45 733//45 732//45 +f 756//46 757//46 754//46 +f 732//45 731//45 730//45 +f 754//46 755//46 756//46 +f 730//354 731//354 743//354 742//354 +f 731//355 732//355 744//355 743//355 +f 732//356 733//356 745//356 744//356 +f 733//357 734//357 746//357 745//357 +f 734//125 735//125 747//125 746//125 +f 735//358 736//358 748//358 747//358 +f 736//359 737//359 749//359 748//359 +f 737//360 738//360 750//360 749//360 +f 738//361 739//361 751//361 750//361 +f 739//362 740//362 752//362 751//362 +f 740//363 741//363 753//363 752//363 +f 741//82 730//82 742//82 753//82 +f 742//335 743//335 755//335 754//335 +f 743//336 744//336 756//336 755//336 +f 744//337 745//337 757//337 756//337 +f 745//338 746//338 758//338 757//338 +f 746//87 747//87 759//87 758//87 +f 747//339 748//339 760//339 759//339 +f 748//340 749//340 761//340 760//340 +f 749//341 750//341 762//341 761//341 +f 750//208 751//208 763//208 762//208 +f 751//342 752//342 764//342 763//342 +f 752//343 753//343 765//343 764//343 +f 753//58 742//58 754//58 765//58 +f 766//45 776//45 775//45 +f 797//46 798//46 788//46 +f 766//45 775//45 774//45 +f 796//46 797//46 788//46 +f 766//45 774//45 773//45 +f 795//46 796//46 788//46 +f 766//45 773//45 772//45 +f 794//46 795//46 788//46 +f 766//45 772//45 771//45 +f 793//46 794//46 788//46 +f 766//45 771//45 770//45 +f 792//46 793//46 788//46 +f 766//45 770//45 769//45 +f 791//46 792//46 788//46 +f 766//45 769//45 768//45 +f 790//46 791//46 788//46 +f 768//45 767//45 766//45 +f 788//46 789//46 790//46 +f 766//364 767//364 778//364 777//364 +f 767//139 768//139 779//139 778//139 +f 768//365 769//365 780//365 779//365 +f 769//366 770//366 781//366 780//366 +f 770//87 771//87 782//87 781//87 +f 771//367 772//367 783//367 782//367 +f 772//368 773//368 784//368 783//368 +f 773//92 774//92 785//92 784//92 +f 774//369 775//369 786//369 785//369 +f 775//95 776//95 787//95 786//95 +f 776//370 766//370 777//370 787//370 +f 777//371 778//371 789//371 788//371 +f 778//148 779//148 790//148 789//148 +f 779//372 780//372 791//372 790//372 +f 780//373 781//373 792//373 791//373 +f 781//106 782//106 793//106 792//106 +f 782//374 783//374 794//374 793//374 +f 783//375 784//375 795//375 794//375 +f 784//111 785//111 796//111 795//111 +f 785//376 786//376 797//376 796//376 +f 786//114 787//114 798//114 797//114 +f 787//377 777//377 788//377 798//377 +f 799//45 809//45 808//45 +f 830//46 831//46 821//46 +f 799//45 808//45 807//45 +f 829//46 830//46 821//46 +f 799//45 807//45 806//45 +f 828//46 829//46 821//46 +f 799//45 806//45 805//45 +f 827//46 828//46 821//46 +f 799//45 805//45 804//45 +f 826//46 827//46 821//46 +f 799//45 804//45 803//45 +f 825//46 826//46 821//46 +f 799//45 803//45 802//45 +f 824//46 825//46 821//46 +f 799//45 802//45 801//45 +f 823//46 824//46 821//46 +f 801//45 800//45 799//45 +f 821//46 822//46 823//46 +f 799//378 800//378 811//378 810//378 +f 800//158 801//158 812//158 811//158 +f 801//379 802//379 813//379 812//379 +f 802//380 803//380 814//380 813//380 +f 803//125 804//125 815//125 814//125 +f 804//381 805//381 816//381 815//381 +f 805//382 806//382 817//382 816//382 +f 806//130 807//130 818//130 817//130 +f 807//383 808//383 819//383 818//383 +f 808//133 809//133 820//133 819//133 +f 809//384 799//384 810//384 820//384 +f 810//364 811//364 822//364 821//364 +f 811//139 812//139 823//139 822//139 +f 812//365 813//365 824//365 823//365 +f 813//366 814//366 825//366 824//366 +f 814//87 815//87 826//87 825//87 +f 815//367 816//367 827//367 826//367 +f 816//368 817//368 828//368 827//368 +f 817//92 818//92 829//92 828//92 +f 818//369 819//369 830//369 829//369 +f 819//95 820//95 831//95 830//95 +f 820//370 810//370 821//370 831//370 +usemtl dark +f 832//45 851//45 850//45 +f 890//46 891//46 872//46 +f 832//45 850//45 849//45 +f 889//46 890//46 872//46 +f 832//45 849//45 848//45 +f 888//46 889//46 872//46 +f 832//45 848//45 847//45 +f 887//46 888//46 872//46 +f 846//45 845//45 844//45 +f 884//46 885//46 886//46 +f 847//45 846//45 844//45 +f 884//46 886//46 887//46 +f 832//45 847//45 844//45 +f 884//46 887//46 872//46 +f 832//45 844//45 843//45 +f 883//46 884//46 872//46 +f 832//45 843//45 842//45 +f 882//46 883//46 872//46 +f 832//45 842//45 841//45 +f 881//46 882//46 872//46 +f 832//45 841//45 840//45 +f 880//46 881//46 872//46 +f 832//45 840//45 839//45 +f 879//46 880//46 872//46 +f 832//45 839//45 838//45 +f 878//46 879//46 872//46 +f 832//45 838//45 837//45 +f 877//46 878//46 872//46 +f 832//45 837//45 836//45 +f 876//46 877//46 872//46 +f 832//45 836//45 835//45 +f 875//46 876//46 872//46 +f 832//45 835//45 834//45 +f 874//46 875//46 872//46 +f 834//45 833//45 832//45 +f 872//46 873//46 874//46 +f 832//385 833//385 853//385 852//385 +f 833//386 834//386 854//386 853//386 +f 834//47 835//47 855//47 854//47 +f 835//387 836//387 856//387 855//387 +f 836//388 837//388 857//388 856//388 +f 837//389 838//389 858//389 857//389 +f 838//144 839//144 859//144 858//144 +f 839//390 840//390 860//390 859//390 +f 840//391 841//391 861//391 860//391 +f 841//392 842//392 862//392 861//392 +f 842//393 843//393 863//393 862//393 +f 843//394 844//394 864//394 863//394 +f 844//395 845//395 865//395 864//395 +f 845//396 846//396 866//396 865//396 +f 846//397 847//397 867//397 866//397 +f 847//398 848//398 868//398 867//398 +f 848//399 849//399 869//399 868//399 +f 849//400 850//400 870//400 869//400 +f 850//401 851//401 871//401 870//401 +f 851//94 832//94 852//94 871//94 +f 852//402 853//402 873//402 872//402 +f 853//403 854//403 874//403 873//403 +f 854//103 855//103 875//103 874//103 +f 855//404 856//404 876//404 875//404 +f 856//405 857//405 877//405 876//405 +f 857//406 858//406 878//406 877//406 +f 858//154 859//154 879//154 878//154 +f 859//407 860//407 880//407 879//407 +f 860//408 861//408 881//408 880//408 +f 861//107 862//107 882//107 881//107 +f 862//409 863//409 883//409 882//409 +f 863//410 864//410 884//410 883//410 +f 864//411 865//411 885//411 884//411 +f 865//412 866//412 886//412 885//412 +f 866//413 867//413 887//413 886//413 +f 867//414 868//414 888//414 887//414 +f 868//415 869//415 889//415 888//415 +f 869//416 870//416 890//416 889//416 +f 870//417 871//417 891//417 890//417 +f 871//418 852//418 872//418 891//418 +f 892//45 911//45 910//45 +f 950//46 951//46 932//46 +f 892//45 910//45 909//45 +f 949//46 950//46 932//46 +f 892//45 909//45 908//45 +f 948//46 949//46 932//46 +f 892//45 908//45 907//45 +f 947//46 948//46 932//46 +f 906//45 905//45 904//45 +f 944//46 945//46 946//46 +f 907//45 906//45 904//45 +f 944//46 946//46 947//46 +f 892//45 907//45 904//45 +f 944//46 947//46 932//46 +f 892//45 904//45 903//45 +f 943//46 944//46 932//46 +f 892//45 903//45 902//45 +f 942//46 943//46 932//46 +f 892//45 902//45 901//45 +f 941//46 942//46 932//46 +f 892//45 901//45 900//45 +f 940//46 941//46 932//46 +f 892//45 900//45 899//45 +f 939//46 940//46 932//46 +f 892//45 899//45 898//45 +f 938//46 939//46 932//46 +f 892//45 898//45 897//45 +f 937//46 938//46 932//46 +f 892//45 897//45 896//45 +f 936//46 937//46 932//46 +f 892//45 896//45 895//45 +f 935//46 936//46 932//46 +f 892//45 895//45 894//45 +f 934//46 935//46 932//46 +f 894//45 893//45 892//45 +f 932//46 933//46 934//46 +f 892//419 893//419 913//419 912//419 +f 893//420 894//420 914//420 913//420 +f 894//122 895//122 915//122 914//122 +f 895//421 896//421 916//421 915//421 +f 896//422 897//422 917//422 916//422 +f 897//423 898//423 918//423 917//423 +f 898//164 899//164 919//164 918//164 +f 899//424 900//424 920//424 919//424 +f 900//425 901//425 921//425 920//425 +f 901//126 902//126 922//126 921//126 +f 902//426 903//426 923//426 922//426 +f 903//427 904//427 924//427 923//427 +f 904//428 905//428 925//428 924//428 +f 905//429 906//429 926//429 925//429 +f 906//430 907//430 927//430 926//430 +f 907//431 908//431 928//431 927//431 +f 908//432 909//432 929//432 928//432 +f 909//433 910//433 930//433 929//433 +f 910//434 911//434 931//434 930//434 +f 911//435 892//435 912//435 931//435 +f 912//385 913//385 933//385 932//385 +f 913//386 914//386 934//386 933//386 +f 914//47 915//47 935//47 934//47 +f 915//387 916//387 936//387 935//387 +f 916//388 917//388 937//388 936//388 +f 917//389 918//389 938//389 937//389 +f 918//144 919//144 939//144 938//144 +f 919//390 920//390 940//390 939//390 +f 920//391 921//391 941//391 940//391 +f 921//392 922//392 942//392 941//392 +f 922//393 923//393 943//393 942//393 +f 923//394 924//394 944//394 943//394 +f 924//395 925//395 945//395 944//395 +f 925//396 926//396 946//396 945//396 +f 926//397 927//397 947//397 946//397 +f 927//398 928//398 948//398 947//398 +f 928//399 929//399 949//399 948//399 +f 929//400 930//400 950//400 949//400 +f 930//401 931//401 951//401 950//401 +f 931//94 912//94 932//94 951//94 +o body +usemtl frame +f 1054//436 1055//436 1081//436 1080//436 +f 1055//437 1056//437 1082//437 1081//437 +f 1056//438 1057//438 1083//438 1082//438 +f 1057//439 1058//439 1084//439 1083//439 +f 1058//440 1059//440 1085//440 1084//440 +f 1059//441 1060//441 1086//441 1085//441 +f 1060//442 1061//442 1087//442 1086//442 +f 1061//443 1062//443 1088//443 1087//443 +f 1062//444 1063//444 1089//444 1088//444 +f 1063//445 1064//445 1090//445 1089//445 +f 1064//446 1065//446 1091//446 1090//446 +f 1065//447 1066//447 1092//447 1091//447 +f 1066//448 1067//448 1093//448 1092//448 +f 1067//449 1068//449 1094//449 1093//449 +f 1068//437 1069//437 1095//437 1094//437 +f 1069//436 1070//436 1096//436 1095//436 +f 1070//450 1071//450 1097//450 1096//450 +f 1071//451 1072//451 1098//451 1097//451 +f 1072//437 1073//437 1099//437 1098//437 +f 1073//452 1074//452 1100//452 1099//452 +f 1074//437 1075//437 1101//437 1100//437 +f 1075//436 1076//436 1102//436 1101//436 +f 1076//453 1077//453 1103//453 1102//453 +f 1077//439 1078//439 1104//439 1103//439 +f 1078//454 1079//454 1105//454 1104//454 +f 1079//455 1054//455 1080//455 1105//455 +f 1080//456 1081//456 1107//456 1106//456 +f 1081//457 1082//457 1108//457 1107//457 +f 1082//458 1083//458 1109//458 1108//458 +f 1083//459 1084//459 1110//459 1109//459 +f 1084//460 1085//460 1111//460 1110//460 +f 1085//461 1086//461 1112//461 1111//461 +f 1086//462 1087//462 1113//462 1112//462 +f 1087//463 1088//463 1114//463 1113//463 +f 1088//464 1089//464 1115//464 1114//464 +f 1089//465 1090//465 1116//465 1115//465 +f 1090//466 1091//466 1117//466 1116//466 +f 1091//467 1092//467 1118//467 1117//467 +f 1092//468 1093//468 1119//468 1118//468 +f 1093//469 1094//469 1120//469 1119//469 +f 1094//457 1095//457 1121//457 1120//457 +f 1095//456 1096//456 1122//456 1121//456 +f 1096//470 1097//470 1123//470 1122//470 +f 1097//471 1098//471 1124//471 1123//471 +f 1098//457 1099//457 1125//457 1124//457 +f 1099//472 1100//472 1126//472 1125//472 +f 1100//457 1101//457 1127//457 1126//457 +f 1101//456 1102//456 1128//456 1127//456 +f 1102//473 1103//473 1129//473 1128//473 +f 1103//459 1104//459 1130//459 1129//459 +f 1104//474 1105//474 1131//474 1130//474 +f 1105//475 1080//475 1106//475 1131//475 +f 1054//45 1079//45 1078//45 +f 1130//46 1131//46 1106//46 +f 1077//45 1076//45 1075//45 +f 1127//46 1128//46 1129//46 +f 1077//45 1075//45 1074//45 +f 1126//46 1127//46 1129//46 +f 1077//45 1074//45 1073//45 +f 1125//46 1126//46 1129//46 +f 1077//45 1073//45 1072//45 +f 1124//46 1125//46 1129//46 +f 1078//45 1077//45 1072//45 +f 1124//46 1129//46 1130//46 +f 1071//45 1070//45 1069//45 +f 1121//46 1122//46 1123//46 +f 1071//45 1069//45 1068//45 +f 1120//46 1121//46 1123//46 +f 1071//45 1068//45 1067//45 +f 1119//46 1120//46 1123//46 +f 1072//45 1071//45 1067//45 +f 1119//46 1123//46 1124//46 +f 1078//45 1072//45 1067//45 +f 1119//46 1124//46 1130//46 +f 1078//45 1067//45 1066//45 +f 1118//46 1119//46 1130//46 +f 1054//45 1078//45 1066//45 +f 1118//46 1130//46 1106//46 +f 1054//45 1066//45 1065//45 +f 1117//46 1118//46 1106//46 +f 1054//45 1065//45 1064//45 +f 1116//46 1117//46 1106//46 +f 1054//45 1064//45 1063//45 +f 1115//46 1116//46 1106//46 +f 1054//45 1063//45 1062//45 +f 1114//46 1115//46 1106//46 +f 1054//45 1062//45 1061//45 +f 1113//46 1114//46 1106//46 +f 1061//45 1060//45 1059//45 +f 1111//46 1112//46 1113//46 +f 1061//45 1059//45 1058//45 +f 1110//46 1111//46 1113//46 +f 1061//45 1058//45 1057//45 +f 1109//46 1110//46 1113//46 +f 1061//45 1057//45 1056//45 +f 1108//46 1109//46 1113//46 +f 1061//45 1056//45 1055//45 +f 1107//46 1108//46 1113//46 +f 1061//45 1055//45 1054//45 +f 1106//46 1107//46 1113//46 +usemtl glass +f 1132//45 1150//45 1149//45 +f 1187//46 1188//46 1170//46 +f 1132//45 1149//45 1148//45 +f 1186//46 1187//46 1170//46 +f 1147//45 1146//45 1145//45 +f 1183//46 1184//46 1185//46 +f 1148//45 1147//45 1145//45 +f 1183//46 1185//46 1186//46 +f 1132//45 1148//45 1145//45 +f 1183//46 1186//46 1170//46 +f 1132//45 1145//45 1144//45 +f 1182//46 1183//46 1170//46 +f 1132//45 1144//45 1143//45 +f 1181//46 1182//46 1170//46 +f 1132//45 1143//45 1142//45 +f 1180//46 1181//46 1170//46 +f 1132//45 1142//45 1141//45 +f 1179//46 1180//46 1170//46 +f 1132//45 1141//45 1140//45 +f 1178//46 1179//46 1170//46 +f 1132//45 1140//45 1139//45 +f 1177//46 1178//46 1170//46 +f 1132//45 1139//45 1138//45 +f 1176//46 1177//46 1170//46 +f 1132//45 1138//45 1137//45 +f 1175//46 1176//46 1170//46 +f 1132//45 1137//45 1136//45 +f 1174//46 1175//46 1170//46 +f 1132//45 1136//45 1135//45 +f 1173//46 1174//46 1170//46 +f 1135//45 1134//45 1133//45 +f 1171//46 1172//46 1173//46 +f 1135//45 1133//45 1132//45 +f 1170//46 1171//46 1173//46 +f 1132//476 1133//476 1152//476 1151//476 +f 1133//477 1134//477 1153//477 1152//477 +f 1134//478 1135//478 1154//478 1153//478 +f 1135//479 1136//479 1155//479 1154//479 +f 1136//480 1137//480 1156//480 1155//480 +f 1137//481 1138//481 1157//481 1156//481 +f 1138//87 1139//87 1158//87 1157//87 +f 1139//47 1140//47 1159//47 1158//47 +f 1140//482 1141//482 1160//482 1159//482 +f 1141//483 1142//483 1161//483 1160//483 +f 1142//87 1143//87 1162//87 1161//87 +f 1143//484 1144//484 1163//484 1162//484 +f 1144//92 1145//92 1164//92 1163//92 +f 1145//485 1146//485 1165//485 1164//485 +f 1146//58 1147//58 1166//58 1165//58 +f 1147//174 1148//174 1167//174 1166//174 +f 1148//486 1149//486 1168//486 1167//486 +f 1149//487 1150//487 1169//487 1168//487 +f 1150//488 1132//488 1151//488 1169//488 +f 1151//489 1152//489 1171//489 1170//489 +f 1152//490 1153//490 1172//490 1171//490 +f 1153//491 1154//491 1173//491 1172//491 +f 1154//103 1155//103 1174//103 1173//103 +f 1155//492 1156//492 1175//492 1174//492 +f 1156//493 1157//493 1176//493 1175//493 +f 1157//106 1158//106 1177//106 1176//106 +f 1158//103 1159//103 1178//103 1177//103 +f 1159//494 1160//494 1179//494 1178//494 +f 1160//495 1161//495 1180//495 1179//495 +f 1161//106 1162//106 1181//106 1180//106 +f 1162//496 1163//496 1182//496 1181//496 +f 1163//111 1164//111 1183//111 1182//111 +f 1164//497 1165//497 1184//497 1183//497 +f 1165//149 1166//149 1185//149 1184//149 +f 1166//180 1167//180 1186//180 1185//180 +f 1167//498 1168//498 1187//498 1186//498 +f 1168//499 1169//499 1188//499 1187//499 +f 1169//500 1151//500 1170//500 1188//500 +f 1189//45 1207//45 1206//45 +f 1244//46 1245//46 1227//46 +f 1189//45 1206//45 1205//45 +f 1243//46 1244//46 1227//46 +f 1204//45 1203//45 1202//45 +f 1240//46 1241//46 1242//46 +f 1205//45 1204//45 1202//45 +f 1240//46 1242//46 1243//46 +f 1189//45 1205//45 1202//45 +f 1240//46 1243//46 1227//46 +f 1189//45 1202//45 1201//45 +f 1239//46 1240//46 1227//46 +f 1189//45 1201//45 1200//45 +f 1238//46 1239//46 1227//46 +f 1189//45 1200//45 1199//45 +f 1237//46 1238//46 1227//46 +f 1189//45 1199//45 1198//45 +f 1236//46 1237//46 1227//46 +f 1189//45 1198//45 1197//45 +f 1235//46 1236//46 1227//46 +f 1189//45 1197//45 1196//45 +f 1234//46 1235//46 1227//46 +f 1189//45 1196//45 1195//45 +f 1233//46 1234//46 1227//46 +f 1189//45 1195//45 1194//45 +f 1232//46 1233//46 1227//46 +f 1189//45 1194//45 1193//45 +f 1231//46 1232//46 1227//46 +f 1189//45 1193//45 1192//45 +f 1230//46 1231//46 1227//46 +f 1192//45 1191//45 1190//45 +f 1228//46 1229//46 1230//46 +f 1192//45 1190//45 1189//45 +f 1227//46 1228//46 1230//46 +f 1189//501 1190//501 1209//501 1208//501 +f 1190//502 1191//502 1210//502 1209//502 +f 1191//503 1192//503 1211//503 1210//503 +f 1192//122 1193//122 1212//122 1211//122 +f 1193//504 1194//504 1213//504 1212//504 +f 1194//505 1195//505 1214//505 1213//505 +f 1195//125 1196//125 1215//125 1214//125 +f 1196//122 1197//122 1216//122 1215//122 +f 1197//506 1198//506 1217//506 1216//506 +f 1198//507 1199//507 1218//507 1217//507 +f 1199//125 1200//125 1219//125 1218//125 +f 1200//508 1201//508 1220//508 1219//508 +f 1201//130 1202//130 1221//130 1220//130 +f 1202//509 1203//509 1222//509 1221//509 +f 1203//159 1204//159 1223//159 1222//159 +f 1204//190 1205//190 1224//190 1223//190 +f 1205//510 1206//510 1225//510 1224//510 +f 1206//511 1207//511 1226//511 1225//511 +f 1207//512 1189//512 1208//512 1226//512 +f 1208//476 1209//476 1228//476 1227//476 +f 1209//477 1210//477 1229//477 1228//477 +f 1210//478 1211//478 1230//478 1229//478 +f 1211//479 1212//479 1231//479 1230//479 +f 1212//480 1213//480 1232//480 1231//480 +f 1213//481 1214//481 1233//481 1232//481 +f 1214//87 1215//87 1234//87 1233//87 +f 1215//47 1216//47 1235//47 1234//47 +f 1216//482 1217//482 1236//482 1235//482 +f 1217//483 1218//483 1237//483 1236//483 +f 1218//87 1219//87 1238//87 1237//87 +f 1219//484 1220//484 1239//484 1238//484 +f 1220//92 1221//92 1240//92 1239//92 +f 1221//485 1222//485 1241//485 1240//485 +f 1222//58 1223//58 1242//58 1241//58 +f 1223//174 1224//174 1243//174 1242//174 +f 1224//486 1225//486 1244//486 1243//486 +f 1225//487 1226//487 1245//487 1244//487 +f 1226//488 1208//488 1227//488 1245//488 +f 1246//45 1255//45 1254//45 +f 1274//46 1275//46 1266//46 +f 1246//45 1254//45 1253//45 +f 1273//46 1274//46 1266//46 +f 1252//45 1251//45 1250//45 +f 1270//45 1271//45 1272//45 +f 1249//45 1248//45 1247//45 +f 1267//46 1268//46 1269//46 +f 1250//45 1249//45 1247//45 +f 1267//46 1269//46 1270//46 +f 1252//45 1250//45 1247//45 +f 1267//45 1270//45 1272//45 +f 1252//45 1247//45 1246//45 +f 1266//46 1267//46 1272//46 +f 1253//46 1252//46 1246//46 +f 1266//45 1272//45 1273//45 +f 1246//513 1247//513 1257//513 1256//513 +f 1247//479 1248//479 1258//479 1257//479 +f 1248//144 1249//144 1259//144 1258//144 +f 1249//514 1250//514 1260//514 1259//514 +f 1250//515 1251//515 1261//515 1260//515 +f 1251//516 1252//516 1262//516 1261//516 +f 1252//517 1253//517 1263//517 1262//517 +f 1253//174 1254//174 1264//174 1263//174 +f 1254//518 1255//518 1265//518 1264//518 +f 1255//519 1246//519 1256//519 1265//519 +f 1256//520 1257//520 1267//520 1266//520 +f 1257//103 1258//103 1268//103 1267//103 +f 1258//107 1259//107 1269//107 1268//107 +f 1259//521 1260//521 1270//521 1269//521 +f 1260//522 1261//522 1271//522 1270//522 +f 1261//523 1262//523 1272//523 1271//523 +f 1262//524 1263//524 1273//524 1272//524 +f 1263//525 1264//525 1274//525 1273//525 +f 1264//526 1265//526 1275//526 1274//526 +f 1265//527 1256//527 1266//527 1275//527 +f 1276//45 1285//45 1284//45 +f 1304//46 1305//46 1296//46 +f 1276//45 1284//45 1283//45 +f 1303//46 1304//46 1296//46 +f 1282//46 1281//46 1280//46 +f 1300//46 1301//46 1302//46 +f 1279//45 1278//45 1277//45 +f 1297//46 1298//46 1299//46 +f 1280//45 1279//45 1277//45 +f 1297//46 1299//46 1300//46 +f 1282//46 1280//46 1277//46 +f 1297//46 1300//46 1302//46 +f 1282//45 1277//45 1276//45 +f 1296//46 1297//46 1302//46 +f 1283//46 1282//46 1276//46 +f 1296//45 1302//45 1303//45 +f 1276//528 1277//528 1287//528 1286//528 +f 1277//122 1278//122 1288//122 1287//122 +f 1278//126 1279//126 1289//126 1288//126 +f 1279//529 1280//529 1290//529 1289//529 +f 1280//530 1281//530 1291//530 1290//530 +f 1281//531 1282//531 1292//531 1291//531 +f 1282//532 1283//532 1293//532 1292//532 +f 1283//533 1284//533 1294//533 1293//533 +f 1284//534 1285//534 1295//534 1294//534 +f 1285//535 1276//535 1286//535 1295//535 +f 1286//513 1287//513 1297//513 1296//513 +f 1287//479 1288//479 1298//479 1297//479 +f 1288//144 1289//144 1299//144 1298//144 +f 1289//514 1290//514 1300//514 1299//514 +f 1290//515 1291//515 1301//515 1300//515 +f 1291//516 1292//516 1302//516 1301//516 +f 1292//517 1293//517 1303//517 1302//517 +f 1293//174 1294//174 1304//174 1303//174 +f 1294//518 1295//518 1305//518 1304//518 +f 1295//519 1286//519 1296//519 1305//519 +f 1306//45 1318//45 1317//45 +f 1343//46 1344//46 1332//46 +f 1306//45 1317//45 1316//45 +f 1342//46 1343//46 1332//46 +f 1306//45 1316//45 1315//45 +f 1341//46 1342//46 1332//46 +f 1306//45 1315//45 1314//45 +f 1340//46 1341//46 1332//46 +f 1306//45 1314//45 1313//45 +f 1339//46 1340//46 1332//46 +f 1306//45 1313//45 1312//45 +f 1338//46 1339//46 1332//46 +f 1306//45 1312//45 1311//45 +f 1337//46 1338//46 1332//46 +f 1306//45 1311//45 1310//45 +f 1336//46 1337//46 1332//46 +f 1306//45 1310//45 1309//45 +f 1335//46 1336//46 1332//46 +f 1306//45 1309//45 1308//45 +f 1334//46 1335//46 1332//46 +f 1308//45 1307//45 1306//45 +f 1332//46 1333//46 1334//46 +f 1306//536 1307//536 1320//536 1319//536 +f 1307//537 1308//537 1321//537 1320//537 +f 1308//538 1309//538 1322//538 1321//538 +f 1309//539 1310//539 1323//539 1322//539 +f 1310//540 1311//540 1324//540 1323//540 +f 1311//88 1312//88 1325//88 1324//88 +f 1312//541 1313//541 1326//541 1325//541 +f 1313//542 1314//542 1327//542 1326//542 +f 1314//543 1315//543 1328//543 1327//543 +f 1315//93 1316//93 1329//93 1328//93 +f 1316//544 1317//544 1330//544 1329//544 +f 1317//545 1318//545 1331//545 1330//545 +f 1318//538 1306//538 1319//538 1331//538 +f 1319//546 1320//546 1333//546 1332//546 +f 1320//547 1321//547 1334//547 1333//547 +f 1321//548 1322//548 1335//548 1334//548 +f 1322//549 1323//549 1336//549 1335//549 +f 1323//550 1324//550 1337//550 1336//550 +f 1324//107 1325//107 1338//107 1337//107 +f 1325//551 1326//551 1339//551 1338//551 +f 1326//552 1327//552 1340//552 1339//552 +f 1327//553 1328//553 1341//553 1340//553 +f 1328//554 1329//554 1342//554 1341//554 +f 1329//180 1330//180 1343//180 1342//180 +f 1330//555 1331//555 1344//555 1343//555 +f 1331//556 1319//556 1332//556 1344//556 +f 1345//45 1357//45 1356//45 +f 1382//46 1383//46 1371//46 +f 1345//45 1356//45 1355//45 +f 1381//46 1382//46 1371//46 +f 1345//45 1355//45 1354//45 +f 1380//46 1381//46 1371//46 +f 1345//45 1354//45 1353//45 +f 1379//46 1380//46 1371//46 +f 1345//45 1353//45 1352//45 +f 1378//46 1379//46 1371//46 +f 1345//45 1352//45 1351//45 +f 1377//46 1378//46 1371//46 +f 1345//45 1351//45 1350//45 +f 1376//46 1377//46 1371//46 +f 1345//45 1350//45 1349//45 +f 1375//46 1376//46 1371//46 +f 1345//45 1349//45 1348//45 +f 1374//46 1375//46 1371//46 +f 1345//45 1348//45 1347//45 +f 1373//46 1374//46 1371//46 +f 1347//45 1346//45 1345//45 +f 1371//46 1372//46 1373//46 +f 1345//557 1346//557 1359//557 1358//557 +f 1346//558 1347//558 1360//558 1359//558 +f 1347//559 1348//559 1361//559 1360//559 +f 1348//560 1349//560 1362//560 1361//560 +f 1349//561 1350//561 1363//561 1362//561 +f 1350//126 1351//126 1364//126 1363//126 +f 1351//562 1352//562 1365//562 1364//562 +f 1352//563 1353//563 1366//563 1365//563 +f 1353//564 1354//564 1367//564 1366//564 +f 1354//565 1355//565 1368//565 1367//565 +f 1355//190 1356//190 1369//190 1368//190 +f 1356//566 1357//566 1370//566 1369//566 +f 1357//567 1345//567 1358//567 1370//567 +f 1358//536 1359//536 1372//536 1371//536 +f 1359//537 1360//537 1373//537 1372//537 +f 1360//538 1361//538 1374//538 1373//538 +f 1361//539 1362//539 1375//539 1374//539 +f 1362//540 1363//540 1376//540 1375//540 +f 1363//88 1364//88 1377//88 1376//88 +f 1364//541 1365//541 1378//541 1377//541 +f 1365//542 1366//542 1379//542 1378//542 +f 1366//543 1367//543 1380//543 1379//543 +f 1367//93 1368//93 1381//93 1380//93 +f 1368//544 1369//544 1382//544 1381//544 +f 1369//545 1370//545 1383//545 1382//545 +f 1370//538 1358//538 1371//538 1383//538 +usemtl champagne +f 1384//45 1392//45 1391//45 +f 1409//46 1410//46 1402//46 +f 1384//45 1391//45 1390//45 +f 1408//46 1409//46 1402//46 +f 1384//45 1390//45 1389//45 +f 1407//46 1408//46 1402//46 +f 1384//45 1389//45 1388//45 +f 1406//46 1407//46 1402//46 +f 1384//45 1388//45 1387//45 +f 1405//46 1406//46 1402//46 +f 1387//45 1386//45 1385//45 +f 1403//46 1404//46 1405//46 +f 1387//45 1385//45 1384//45 +f 1402//46 1403//46 1405//46 +f 1384//568 1385//568 1394//568 1393//568 +f 1385//569 1386//569 1395//569 1394//569 +f 1386//139 1387//139 1396//139 1395//139 +f 1387//570 1388//570 1397//570 1396//570 +f 1388//571 1389//571 1398//571 1397//571 +f 1389//572 1390//572 1399//572 1398//572 +f 1390//573 1391//573 1400//573 1399//573 +f 1391//574 1392//574 1401//574 1400//574 +f 1392//147 1384//147 1393//147 1401//147 +f 1393//575 1394//575 1403//575 1402//575 +f 1394//576 1395//576 1404//576 1403//576 +f 1395//148 1396//148 1405//148 1404//148 +f 1396//577 1397//577 1406//577 1405//577 +f 1397//578 1398//578 1407//578 1406//578 +f 1398//579 1399//579 1408//579 1407//579 +f 1399//580 1400//580 1409//580 1408//580 +f 1400//581 1401//581 1410//581 1409//581 +f 1401//149 1393//149 1402//149 1410//149 +f 1411//45 1419//45 1418//45 +f 1436//46 1437//46 1429//46 +f 1411//45 1418//45 1417//45 +f 1435//46 1436//46 1429//46 +f 1411//45 1417//45 1416//45 +f 1434//46 1435//46 1429//46 +f 1411//45 1416//45 1415//45 +f 1433//46 1434//46 1429//46 +f 1411//45 1415//45 1414//45 +f 1432//46 1433//46 1429//46 +f 1414//45 1413//45 1412//45 +f 1430//46 1431//46 1432//46 +f 1414//45 1412//45 1411//45 +f 1429//46 1430//46 1432//46 +f 1411//582 1412//582 1421//582 1420//582 +f 1412//583 1413//583 1422//583 1421//583 +f 1413//158 1414//158 1423//158 1422//158 +f 1414//584 1415//584 1424//584 1423//584 +f 1415//585 1416//585 1425//585 1424//585 +f 1416//586 1417//586 1426//586 1425//586 +f 1417//587 1418//587 1427//587 1426//587 +f 1418//588 1419//588 1428//588 1427//588 +f 1419//159 1411//159 1420//159 1428//159 +f 1420//568 1421//568 1430//568 1429//568 +f 1421//569 1422//569 1431//569 1430//569 +f 1422//139 1423//139 1432//139 1431//139 +f 1423//570 1424//570 1433//570 1432//570 +f 1424//571 1425//571 1434//571 1433//571 +f 1425//572 1426//572 1435//572 1434//572 +f 1426//573 1427//573 1436//573 1435//573 +f 1427//574 1428//574 1437//574 1436//574 +f 1428//147 1420//147 1429//147 1437//147 +f 1438//45 1451//45 1450//45 +f 1478//46 1479//46 1466//46 +f 1438//45 1450//45 1449//45 +f 1477//46 1478//46 1466//46 +f 1449//45 1448//45 1447//45 +f 1475//46 1476//46 1477//46 +f 1438//45 1449//45 1447//45 +f 1475//46 1477//46 1466//46 +f 1438//45 1447//45 1446//45 +f 1474//45 1475//45 1466//45 +f 1438//45 1446//45 1445//45 +f 1473//46 1474//46 1466//46 +f 1445//45 1444//45 1443//45 +f 1471//46 1472//46 1473//46 +f 1445//45 1443//45 1442//45 +f 1470//45 1471//45 1473//45 +f 1438//45 1445//45 1442//45 +f 1470//45 1473//45 1466//45 +f 1442//45 1441//45 1440//45 +f 1468//46 1469//46 1470//46 +f 1442//45 1440//45 1439//45 +f 1467//46 1468//46 1470//46 +f 1442//45 1439//45 1438//45 +f 1466//46 1467//46 1470//46 +f 1438//589 1439//589 1453//589 1452//589 +f 1439//147 1440//147 1454//147 1453//147 +f 1440//139 1441//139 1455//139 1454//139 +f 1441//590 1442//590 1456//590 1455//590 +f 1442//139 1443//139 1457//139 1456//139 +f 1443//591 1444//591 1458//591 1457//591 +f 1444//592 1445//592 1459//592 1458//592 +f 1445//593 1446//593 1460//593 1459//593 +f 1446//594 1447//594 1461//594 1460//594 +f 1447//92 1448//92 1462//92 1461//92 +f 1448//174 1449//174 1463//174 1462//174 +f 1449//595 1450//595 1464//595 1463//595 +f 1450//596 1451//596 1465//596 1464//596 +f 1451//519 1438//519 1452//519 1465//519 +f 1452//597 1453//597 1467//597 1466//597 +f 1453//149 1454//149 1468//149 1467//149 +f 1454//148 1455//148 1469//148 1468//148 +f 1455//598 1456//598 1470//598 1469//598 +f 1456//148 1457//148 1471//148 1470//148 +f 1457//599 1458//599 1472//599 1471//599 +f 1458//600 1459//600 1473//600 1472//600 +f 1459//601 1460//601 1474//601 1473//601 +f 1460//602 1461//602 1475//602 1474//602 +f 1461//111 1462//111 1476//111 1475//111 +f 1462//180 1463//180 1477//180 1476//180 +f 1463//603 1464//603 1478//603 1477//603 +f 1464//180 1465//180 1479//180 1478//180 +f 1465//527 1452//527 1466//527 1479//527 +f 1480//45 1493//45 1492//45 +f 1520//46 1521//46 1508//46 +f 1480//45 1492//45 1491//45 +f 1519//46 1520//46 1508//46 +f 1491//45 1490//45 1489//45 +f 1517//46 1518//46 1519//46 +f 1480//45 1491//45 1489//45 +f 1517//46 1519//46 1508//46 +f 1480//46 1489//46 1488//46 +f 1516//46 1517//46 1508//46 +f 1480//45 1488//45 1487//45 +f 1515//46 1516//46 1508//46 +f 1487//45 1486//45 1485//45 +f 1513//46 1514//46 1515//46 +f 1487//46 1485//46 1484//46 +f 1512//46 1513//46 1515//46 +f 1480//46 1487//46 1484//46 +f 1512//46 1515//46 1508//46 +f 1484//45 1483//45 1482//45 +f 1510//46 1511//46 1512//46 +f 1484//45 1482//45 1481//45 +f 1509//46 1510//46 1512//46 +f 1484//45 1481//45 1480//45 +f 1508//46 1509//46 1512//46 +f 1480//604 1481//604 1495//604 1494//604 +f 1481//159 1482//159 1496//159 1495//159 +f 1482//158 1483//158 1497//158 1496//158 +f 1483//605 1484//605 1498//605 1497//605 +f 1484//158 1485//158 1499//158 1498//158 +f 1485//606 1486//606 1500//606 1499//606 +f 1486//607 1487//607 1501//607 1500//607 +f 1487//608 1488//608 1502//608 1501//608 +f 1488//609 1489//609 1503//609 1502//609 +f 1489//130 1490//130 1504//130 1503//130 +f 1490//190 1491//190 1505//190 1504//190 +f 1491//610 1492//610 1506//610 1505//610 +f 1492//190 1493//190 1507//190 1506//190 +f 1493//535 1480//535 1494//535 1507//535 +f 1494//589 1495//589 1509//589 1508//589 +f 1495//147 1496//147 1510//147 1509//147 +f 1496//139 1497//139 1511//139 1510//139 +f 1497//590 1498//590 1512//590 1511//590 +f 1498//139 1499//139 1513//139 1512//139 +f 1499//591 1500//591 1514//591 1513//591 +f 1500//592 1501//592 1515//592 1514//592 +f 1501//593 1502//593 1516//593 1515//593 +f 1502//594 1503//594 1517//594 1516//594 +f 1503//92 1504//92 1518//92 1517//92 +f 1504//174 1505//174 1519//174 1518//174 +f 1505//595 1506//595 1520//595 1519//595 +f 1506//596 1507//596 1521//596 1520//596 +f 1507//519 1494//519 1508//519 1521//519 +f 1522//45 1534//45 1533//45 +f 1559//46 1560//46 1548//46 +f 1522//45 1533//45 1532//45 +f 1558//46 1559//46 1548//46 +f 1522//45 1532//45 1531//45 +f 1557//46 1558//46 1548//46 +f 1522//45 1531//45 1530//45 +f 1556//46 1557//46 1548//46 +f 1530//45 1529//45 1528//45 +f 1554//46 1555//46 1556//46 +f 1522//45 1530//45 1528//45 +f 1554//46 1556//46 1548//46 +f 1522//45 1528//45 1527//45 +f 1553//46 1554//46 1548//46 +f 1522//45 1527//45 1526//45 +f 1552//46 1553//46 1548//46 +f 1522//45 1526//45 1525//45 +f 1551//46 1552//46 1548//46 +f 1522//45 1525//45 1524//45 +f 1550//46 1551//46 1548//46 +f 1524//45 1523//45 1522//45 +f 1548//46 1549//46 1550//46 +f 1522//611 1523//611 1536//611 1535//611 +f 1523//47 1524//47 1537//47 1536//47 +f 1524//612 1525//612 1538//612 1537//612 +f 1525//88 1526//88 1539//88 1538//88 +f 1526//613 1527//613 1540//613 1539//613 +f 1527//614 1528//614 1541//614 1540//614 +f 1528//92 1529//92 1542//92 1541//92 +f 1529//95 1530//95 1543//95 1542//95 +f 1530//615 1531//615 1544//615 1543//615 +f 1531//616 1532//616 1545//616 1544//616 +f 1532//171 1533//171 1546//171 1545//171 +f 1533//95 1534//95 1547//95 1546//95 +f 1534//617 1522//617 1535//617 1547//617 +f 1535//618 1536//618 1549//618 1548//618 +f 1536//272 1537//272 1550//272 1549//272 +f 1537//619 1538//619 1551//619 1550//619 +f 1538//620 1539//620 1552//620 1551//620 +f 1539//621 1540//621 1553//621 1552//621 +f 1540//622 1541//622 1554//622 1553//622 +f 1541//111 1542//111 1555//111 1554//111 +f 1542//114 1543//114 1556//114 1555//114 +f 1543//623 1544//623 1557//623 1556//623 +f 1544//624 1545//624 1558//624 1557//624 +f 1545//180 1546//180 1559//180 1558//180 +f 1546//114 1547//114 1560//114 1559//114 +f 1547//625 1535//625 1548//625 1560//625 +f 1561//45 1573//45 1572//45 +f 1598//46 1599//46 1587//46 +f 1561//45 1572//45 1571//45 +f 1597//46 1598//46 1587//46 +f 1561//45 1571//45 1570//45 +f 1596//46 1597//46 1587//46 +f 1561//45 1570//45 1569//45 +f 1595//46 1596//46 1587//46 +f 1569//45 1568//45 1567//45 +f 1593//46 1594//46 1595//46 +f 1561//45 1569//45 1567//45 +f 1593//46 1595//46 1587//46 +f 1561//45 1567//45 1566//45 +f 1592//46 1593//46 1587//46 +f 1561//45 1566//45 1565//45 +f 1591//46 1592//46 1587//46 +f 1561//45 1565//45 1564//45 +f 1590//46 1591//46 1587//46 +f 1561//45 1564//45 1563//45 +f 1589//46 1590//46 1587//46 +f 1563//45 1562//45 1561//45 +f 1587//46 1588//46 1589//46 +f 1561//626 1562//626 1575//626 1574//626 +f 1562//287 1563//287 1576//287 1575//287 +f 1563//627 1564//627 1577//627 1576//627 +f 1564//628 1565//628 1578//628 1577//628 +f 1565//629 1566//629 1579//629 1578//629 +f 1566//630 1567//630 1580//630 1579//630 +f 1567//130 1568//130 1581//130 1580//130 +f 1568//133 1569//133 1582//133 1581//133 +f 1569//631 1570//631 1583//631 1582//631 +f 1570//632 1571//632 1584//632 1583//632 +f 1571//190 1572//190 1585//190 1584//190 +f 1572//133 1573//133 1586//133 1585//133 +f 1573//633 1561//633 1574//633 1586//633 +f 1574//611 1575//611 1588//611 1587//611 +f 1575//47 1576//47 1589//47 1588//47 +f 1576//612 1577//612 1590//612 1589//612 +f 1577//88 1578//88 1591//88 1590//88 +f 1578//613 1579//613 1592//613 1591//613 +f 1579//614 1580//614 1593//614 1592//614 +f 1580//92 1581//92 1594//92 1593//92 +f 1581//95 1582//95 1595//95 1594//95 +f 1582//615 1583//615 1596//615 1595//615 +f 1583//616 1584//616 1597//616 1596//616 +f 1584//171 1585//171 1598//171 1597//171 +f 1585//95 1586//95 1599//95 1598//95 +f 1586//617 1574//617 1587//617 1599//617 +usemtl dark +f 1600//45 1610//45 1609//45 +f 1631//46 1632//46 1622//46 +f 1600//45 1609//45 1608//45 +f 1630//46 1631//46 1622//46 +f 1600//45 1608//45 1607//45 +f 1629//46 1630//46 1622//46 +f 1600//45 1607//45 1606//45 +f 1628//46 1629//46 1622//46 +f 1600//45 1606//45 1605//45 +f 1627//46 1628//46 1622//46 +f 1600//45 1605//45 1604//45 +f 1626//46 1627//46 1622//46 +f 1600//45 1604//45 1603//45 +f 1625//46 1626//46 1622//46 +f 1600//45 1603//45 1602//45 +f 1624//46 1625//46 1622//46 +f 1602//45 1601//45 1600//45 +f 1622//46 1623//46 1624//46 +f 1600//634 1601//634 1612//634 1611//634 +f 1601//139 1602//139 1613//139 1612//139 +f 1602//635 1603//635 1614//635 1613//635 +f 1603//636 1604//636 1615//636 1614//636 +f 1604//637 1605//637 1616//637 1615//637 +f 1605//638 1606//638 1617//638 1616//638 +f 1606//639 1607//639 1618//639 1617//639 +f 1607//640 1608//640 1619//640 1618//640 +f 1608//88 1609//88 1620//88 1619//88 +f 1609//641 1610//641 1621//641 1620//641 +f 1610//642 1600//642 1611//642 1621//642 +f 1611//643 1612//643 1623//643 1622//643 +f 1612//148 1613//148 1624//148 1623//148 +f 1613//644 1614//644 1625//644 1624//644 +f 1614//645 1615//645 1626//645 1625//645 +f 1615//646 1616//646 1627//646 1626//646 +f 1616//647 1617//647 1628//647 1627//647 +f 1617//648 1618//648 1629//648 1628//648 +f 1618//649 1619//649 1630//649 1629//649 +f 1619//107 1620//107 1631//107 1630//107 +f 1620//650 1621//650 1632//650 1631//650 +f 1621//651 1611//651 1622//651 1632//651 +f 1633//45 1643//45 1642//45 +f 1664//46 1665//46 1655//46 +f 1633//45 1642//45 1641//45 +f 1663//46 1664//46 1655//46 +f 1633//45 1641//45 1640//45 +f 1662//46 1663//46 1655//46 +f 1633//45 1640//45 1639//45 +f 1661//46 1662//46 1655//46 +f 1633//45 1639//45 1638//45 +f 1660//46 1661//46 1655//46 +f 1633//45 1638//45 1637//45 +f 1659//46 1660//46 1655//46 +f 1633//45 1637//45 1636//45 +f 1658//46 1659//46 1655//46 +f 1633//45 1636//45 1635//45 +f 1657//46 1658//46 1655//46 +f 1635//45 1634//45 1633//45 +f 1655//46 1656//46 1657//46 +f 1633//652 1634//652 1645//652 1644//652 +f 1634//158 1635//158 1646//158 1645//158 +f 1635//653 1636//653 1647//653 1646//653 +f 1636//654 1637//654 1648//654 1647//654 +f 1637//655 1638//655 1649//655 1648//655 +f 1638//656 1639//656 1650//656 1649//656 +f 1639//657 1640//657 1651//657 1650//657 +f 1640//658 1641//658 1652//658 1651//658 +f 1641//126 1642//126 1653//126 1652//126 +f 1642//659 1643//659 1654//659 1653//659 +f 1643//660 1633//660 1644//660 1654//660 +f 1644//634 1645//634 1656//634 1655//634 +f 1645//139 1646//139 1657//139 1656//139 +f 1646//635 1647//635 1658//635 1657//635 +f 1647//636 1648//636 1659//636 1658//636 +f 1648//637 1649//637 1660//637 1659//637 +f 1649//638 1650//638 1661//638 1660//638 +f 1650//639 1651//639 1662//639 1661//639 +f 1651//640 1652//640 1663//640 1662//640 +f 1652//88 1653//88 1664//88 1663//88 +f 1653//641 1654//641 1665//641 1664//641 +f 1654//642 1644//642 1655//642 1665//642 +o tail +usemtl frame +f 1666//661 1667//661 1688//661 1687//661 +f 1667//662 1668//662 1689//662 1688//662 +f 1668//663 1669//663 1690//663 1689//663 +f 1669//664 1670//664 1691//664 1690//664 +f 1670//665 1671//665 1692//665 1691//665 +f 1671//666 1672//666 1693//666 1692//666 +f 1672//667 1673//667 1694//667 1693//667 +f 1673//668 1674//668 1695//668 1694//668 +f 1674//669 1675//669 1696//669 1695//669 +f 1675//670 1676//670 1697//670 1696//670 +f 1676//671 1677//671 1698//671 1697//671 +f 1677//672 1678//672 1699//672 1698//672 +f 1678//673 1679//673 1700//673 1699//673 +f 1679//674 1680//674 1701//674 1700//674 +f 1680//675 1681//675 1702//675 1701//675 +f 1681//676 1682//676 1703//676 1702//676 +f 1682//677 1683//677 1704//677 1703//677 +f 1683//678 1684//678 1705//678 1704//678 +f 1684//679 1685//679 1706//679 1705//679 +f 1685//680 1686//680 1707//680 1706//680 +f 1686//681 1666//681 1687//681 1707//681 +f 1687//682 1688//682 1709//682 1708//682 +f 1688//683 1689//683 1710//683 1709//683 +f 1689//684 1690//684 1711//684 1710//684 +f 1690//685 1691//685 1712//685 1711//685 +f 1691//686 1692//686 1713//686 1712//686 +f 1692//687 1693//687 1714//687 1713//687 +f 1693//688 1694//688 1715//688 1714//688 +f 1694//689 1695//689 1716//689 1715//689 +f 1695//690 1696//690 1717//690 1716//690 +f 1696//691 1697//691 1718//691 1717//691 +f 1697//692 1698//692 1719//692 1718//692 +f 1698//693 1699//693 1720//693 1719//693 +f 1699//694 1700//694 1721//694 1720//694 +f 1700//695 1701//695 1722//695 1721//695 +f 1701//696 1702//696 1723//696 1722//696 +f 1702//697 1703//697 1724//697 1723//697 +f 1703//698 1704//698 1725//698 1724//698 +f 1704//699 1705//699 1726//699 1725//699 +f 1705//700 1706//700 1727//700 1726//700 +f 1706//701 1707//701 1728//701 1727//701 +f 1707//702 1687//702 1708//702 1728//702 +f 1666//45 1686//45 1685//45 +f 1727//46 1728//46 1708//46 +f 1666//45 1685//45 1684//45 +f 1726//46 1727//46 1708//46 +f 1666//45 1684//45 1683//45 +f 1725//46 1726//46 1708//46 +f 1666//45 1683//45 1682//45 +f 1724//46 1725//46 1708//46 +f 1680//45 1679//45 1678//45 +f 1720//46 1721//46 1722//46 +f 1680//45 1678//45 1677//45 +f 1719//46 1720//46 1722//46 +f 1677//45 1676//45 1675//45 +f 1717//46 1718//46 1719//46 +f 1680//45 1677//45 1675//45 +f 1717//46 1719//46 1722//46 +f 1680//45 1675//45 1674//45 +f 1716//46 1717//46 1722//46 +f 1680//45 1674//45 1673//45 +f 1715//46 1716//46 1722//46 +f 1680//45 1673//45 1672//45 +f 1714//46 1715//46 1722//46 +f 1681//45 1680//45 1672//45 +f 1714//46 1722//46 1723//46 +f 1682//45 1681//45 1672//45 +f 1714//46 1723//46 1724//46 +f 1666//45 1682//45 1672//45 +f 1714//46 1724//46 1708//46 +f 1672//45 1671//45 1670//45 +f 1712//46 1713//46 1714//46 +f 1666//45 1672//45 1670//45 +f 1712//46 1714//46 1708//46 +f 1666//45 1670//45 1669//45 +f 1711//46 1712//46 1708//46 +f 1666//45 1669//45 1668//45 +f 1710//46 1711//46 1708//46 +f 1668//45 1667//45 1666//45 +f 1708//46 1709//46 1710//46 +usemtl champagne +f 1729//45 1738//45 1737//45 +f 1757//46 1758//46 1749//46 +f 1729//45 1737//45 1736//45 +f 1756//46 1757//46 1749//46 +f 1729//45 1736//45 1735//45 +f 1755//46 1756//46 1749//46 +f 1729//45 1735//45 1734//45 +f 1754//46 1755//46 1749//46 +f 1729//45 1734//45 1733//45 +f 1753//46 1754//46 1749//46 +f 1729//45 1733//45 1732//45 +f 1752//46 1753//46 1749//46 +f 1732//45 1731//45 1730//45 +f 1750//46 1751//46 1752//46 +f 1732//45 1730//45 1729//45 +f 1749//46 1750//46 1752//46 +f 1729//703 1730//703 1740//703 1739//703 +f 1730//95 1731//95 1741//95 1740//95 +f 1731//139 1732//139 1742//139 1741//139 +f 1732//704 1733//704 1743//704 1742//704 +f 1733//705 1734//705 1744//705 1743//705 +f 1734//706 1735//706 1745//706 1744//706 +f 1735//88 1736//88 1746//88 1745//88 +f 1736//707 1737//707 1747//707 1746//707 +f 1737//708 1738//708 1748//708 1747//708 +f 1738//709 1729//709 1739//709 1748//709 +f 1739//710 1740//710 1750//710 1749//710 +f 1740//114 1741//114 1751//114 1750//114 +f 1741//148 1742//148 1752//148 1751//148 +f 1742//711 1743//711 1753//711 1752//711 +f 1743//712 1744//712 1754//712 1753//712 +f 1744//713 1745//713 1755//713 1754//713 +f 1745//620 1746//620 1756//620 1755//620 +f 1746//714 1747//714 1757//714 1756//714 +f 1747//715 1748//715 1758//715 1757//715 +f 1748//716 1739//716 1749//716 1758//716 +f 1759//45 1768//45 1767//45 +f 1787//46 1788//46 1779//46 +f 1759//45 1767//45 1766//45 +f 1786//46 1787//46 1779//46 +f 1759//45 1766//45 1765//45 +f 1785//46 1786//46 1779//46 +f 1759//45 1765//45 1764//45 +f 1784//46 1785//46 1779//46 +f 1759//45 1764//45 1763//45 +f 1783//46 1784//46 1779//46 +f 1759//45 1763//45 1762//45 +f 1782//46 1783//46 1779//46 +f 1762//45 1761//45 1760//45 +f 1780//46 1781//46 1782//46 +f 1762//45 1760//45 1759//45 +f 1779//46 1780//46 1782//46 +f 1759//717 1760//717 1770//717 1769//717 +f 1760//133 1761//133 1771//133 1770//133 +f 1761//158 1762//158 1772//158 1771//158 +f 1762//718 1763//718 1773//718 1772//718 +f 1763//719 1764//719 1774//719 1773//719 +f 1764//720 1765//720 1775//720 1774//720 +f 1765//628 1766//628 1776//628 1775//628 +f 1766//721 1767//721 1777//721 1776//721 +f 1767//722 1768//722 1778//722 1777//722 +f 1768//723 1759//723 1769//723 1778//723 +f 1769//703 1770//703 1780//703 1779//703 +f 1770//95 1771//95 1781//95 1780//95 +f 1771//139 1772//139 1782//139 1781//139 +f 1772//704 1773//704 1783//704 1782//704 +f 1773//705 1774//705 1784//705 1783//705 +f 1774//706 1775//706 1785//706 1784//706 +f 1775//88 1776//88 1786//88 1785//88 +f 1776//707 1777//707 1787//707 1786//707 +f 1777//708 1778//708 1788//708 1787//708 +f 1778//709 1769//709 1779//709 1788//709 +f 1927//45 1941//45 1940//45 +f 1970//46 1971//46 1957//46 +f 1927//45 1940//45 1939//45 +f 1969//46 1970//46 1957//46 +f 1927//45 1939//45 1938//45 +f 1968//46 1969//46 1957//46 +f 1938//45 1937//45 1936//45 +f 1966//46 1967//46 1968//46 +f 1938//45 1936//45 1935//45 +f 1965//46 1966//46 1968//46 +f 1927//45 1938//45 1935//45 +f 1965//46 1968//46 1957//46 +f 1927//45 1935//45 1934//45 +f 1964//46 1965//46 1957//46 +f 1927//45 1934//45 1933//45 +f 1963//46 1964//46 1957//46 +f 1927//45 1933//45 1932//45 +f 1962//46 1963//46 1957//46 +f 1927//45 1932//45 1931//45 +f 1961//46 1962//46 1957//46 +f 1931//45 1930//45 1929//45 +f 1959//46 1960//46 1961//46 +f 1931//45 1929//45 1928//45 +f 1958//46 1959//46 1961//46 +f 1931//45 1928//45 1927//45 +f 1957//46 1958//46 1961//46 +f 1927//724 1928//724 1943//724 1942//724 +f 1928//725 1929//725 1944//725 1943//725 +f 1929//58 1930//58 1945//58 1944//58 +f 1930//726 1931//726 1946//726 1945//726 +f 1931//727 1932//727 1947//727 1946//727 +f 1932//728 1933//728 1948//728 1947//728 +f 1933//391 1934//391 1949//391 1948//391 +f 1934//729 1935//729 1950//729 1949//729 +f 1935//730 1936//730 1951//730 1950//730 +f 1936//88 1937//88 1952//88 1951//88 +f 1937//174 1938//174 1953//174 1952//174 +f 1938//731 1939//731 1954//731 1953//731 +f 1939//732 1940//732 1955//732 1954//732 +f 1940//733 1941//733 1956//733 1955//733 +f 1941//734 1927//734 1942//734 1956//734 +f 1942//735 1943//735 1958//735 1957//735 +f 1943//736 1944//736 1959//736 1958//736 +f 1944//149 1945//149 1960//149 1959//149 +f 1945//737 1946//737 1961//737 1960//737 +f 1946//738 1947//738 1962//738 1961//738 +f 1947//739 1948//739 1963//739 1962//739 +f 1948//740 1949//740 1964//740 1963//740 +f 1949//741 1950//741 1965//741 1964//741 +f 1950//742 1951//742 1966//742 1965//742 +f 1951//107 1952//107 1967//107 1966//107 +f 1952//180 1953//180 1968//180 1967//180 +f 1953//743 1954//743 1969//743 1968//743 +f 1954//744 1955//744 1970//744 1969//744 +f 1955//745 1956//745 1971//745 1970//745 +f 1956//746 1942//746 1957//746 1971//746 +f 1972//45 1986//45 1985//45 +f 2015//46 2016//46 2002//46 +f 1972//45 1985//45 1984//45 +f 2014//46 2015//46 2002//46 +f 1972//45 1984//45 1983//45 +f 2013//46 2014//46 2002//46 +f 1983//45 1982//45 1981//45 +f 2011//46 2012//46 2013//46 +f 1983//45 1981//45 1980//45 +f 2010//46 2011//46 2013//46 +f 1972//45 1983//45 1980//45 +f 2010//46 2013//46 2002//46 +f 1972//45 1980//45 1979//45 +f 2009//46 2010//46 2002//46 +f 1972//45 1979//45 1978//45 +f 2008//46 2009//46 2002//46 +f 1972//45 1978//45 1977//45 +f 2007//46 2008//46 2002//46 +f 1972//45 1977//45 1976//45 +f 2006//46 2007//46 2002//46 +f 1976//45 1975//45 1974//45 +f 2004//46 2005//46 2006//46 +f 1976//45 1974//45 1973//45 +f 2003//46 2004//46 2006//46 +f 1976//45 1973//45 1972//45 +f 2002//46 2003//46 2006//46 +f 1972//747 1973//747 1988//747 1987//747 +f 1973//748 1974//748 1989//748 1988//748 +f 1974//159 1975//159 1990//159 1989//159 +f 1975//749 1976//749 1991//749 1990//749 +f 1976//750 1977//750 1992//750 1991//750 +f 1977//751 1978//751 1993//751 1992//751 +f 1978//752 1979//752 1994//752 1993//752 +f 1979//753 1980//753 1995//753 1994//753 +f 1980//754 1981//754 1996//754 1995//754 +f 1981//126 1982//126 1997//126 1996//126 +f 1982//190 1983//190 1998//190 1997//190 +f 1983//755 1984//755 1999//755 1998//755 +f 1984//756 1985//756 2000//756 1999//756 +f 1985//757 1986//757 2001//757 2000//757 +f 1986//758 1972//758 1987//758 2001//758 +f 1987//724 1988//724 2003//724 2002//724 +f 1988//725 1989//725 2004//725 2003//725 +f 1989//58 1990//58 2005//58 2004//58 +f 1990//726 1991//726 2006//726 2005//726 +f 1991//727 1992//727 2007//727 2006//727 +f 1992//728 1993//728 2008//728 2007//728 +f 1993//391 1994//391 2009//391 2008//391 +f 1994//729 1995//729 2010//729 2009//729 +f 1995//730 1996//730 2011//730 2010//730 +f 1996//88 1997//88 2012//88 2011//88 +f 1997//174 1998//174 2013//174 2012//174 +f 1998//731 1999//731 2014//731 2013//731 +f 1999//732 2000//732 2015//732 2014//732 +f 2000//733 2001//733 2016//733 2015//733 +f 2001//734 1987//734 2002//734 2016//734 +f 2017//45 2030//45 2029//45 +f 2057//46 2058//46 2045//46 +f 2029//45 2028//45 2027//45 +f 2055//46 2056//46 2057//46 +f 2017//45 2029//45 2027//45 +f 2055//46 2057//46 2045//46 +f 2017//45 2027//45 2026//45 +f 2054//46 2055//46 2045//46 +f 2017//45 2026//45 2025//45 +f 2053//46 2054//46 2045//46 +f 2017//45 2025//45 2024//45 +f 2052//46 2053//46 2045//46 +f 2017//45 2024//45 2023//45 +f 2051//46 2052//46 2045//46 +f 2017//45 2023//45 2022//45 +f 2050//46 2051//46 2045//46 +f 2017//45 2022//45 2021//45 +f 2049//46 2050//46 2045//46 +f 2021//45 2020//45 2019//45 +f 2047//46 2048//46 2049//46 +f 2017//45 2021//45 2019//45 +f 2047//46 2049//46 2045//46 +f 2019//45 2018//45 2017//45 +f 2045//46 2046//46 2047//46 +f 2017//759 2018//759 2032//759 2031//759 +f 2018//760 2019//760 2033//760 2032//760 +f 2019//257 2020//257 2034//257 2033//257 +f 2020//258 2021//258 2035//258 2034//258 +f 2021//87 2022//87 2036//87 2035//87 +f 2022//144 2023//144 2037//144 2036//144 +f 2023//761 2024//761 2038//761 2037//761 +f 2024//762 2025//762 2039//762 2038//762 +f 2025//763 2026//763 2040//763 2039//763 +f 2026//764 2027//764 2041//764 2040//764 +f 2027//765 2028//765 2042//765 2041//765 +f 2028//766 2029//766 2043//766 2042//766 +f 2029//596 2030//596 2044//596 2043//596 +f 2030//519 2017//519 2031//519 2044//519 +f 2031//767 2032//767 2046//767 2045//767 +f 2032//768 2033//768 2047//768 2046//768 +f 2033//769 2034//769 2048//769 2047//769 +f 2034//770 2035//770 2049//770 2048//770 +f 2035//106 2036//106 2050//106 2049//106 +f 2036//154 2037//154 2051//154 2050//154 +f 2037//771 2038//771 2052//771 2051//771 +f 2038//772 2039//772 2053//772 2052//772 +f 2039//773 2040//773 2054//773 2053//773 +f 2040//774 2041//774 2055//774 2054//774 +f 2041//775 2042//775 2056//775 2055//775 +f 2042//776 2043//776 2057//776 2056//776 +f 2043//777 2044//777 2058//777 2057//777 +f 2044//778 2031//778 2045//778 2058//778 +f 2059//45 2072//45 2071//45 +f 2099//46 2100//46 2087//46 +f 2071//45 2070//45 2069//45 +f 2097//46 2098//46 2099//46 +f 2059//45 2071//45 2069//45 +f 2097//46 2099//46 2087//46 +f 2059//45 2069//45 2068//45 +f 2096//46 2097//46 2087//46 +f 2059//45 2068//45 2067//45 +f 2095//46 2096//46 2087//46 +f 2059//45 2067//45 2066//45 +f 2094//46 2095//46 2087//46 +f 2059//45 2066//45 2065//45 +f 2093//46 2094//46 2087//46 +f 2059//45 2065//45 2064//45 +f 2092//46 2093//46 2087//46 +f 2059//45 2064//45 2063//45 +f 2091//46 2092//46 2087//46 +f 2063//45 2062//45 2061//45 +f 2089//46 2090//46 2091//46 +f 2059//45 2063//45 2061//45 +f 2089//46 2091//46 2087//46 +f 2061//45 2060//45 2059//45 +f 2087//46 2088//46 2089//46 +f 2059//779 2060//779 2074//779 2073//779 +f 2060//780 2061//780 2075//780 2074//780 +f 2061//781 2062//781 2076//781 2075//781 +f 2062//782 2063//782 2077//782 2076//782 +f 2063//125 2064//125 2078//125 2077//125 +f 2064//164 2065//164 2079//164 2078//164 +f 2065//783 2066//783 2080//783 2079//783 +f 2066//784 2067//784 2081//784 2080//784 +f 2067//785 2068//785 2082//785 2081//785 +f 2068//786 2069//786 2083//786 2082//786 +f 2069//787 2070//787 2084//787 2083//787 +f 2070//788 2071//788 2085//788 2084//788 +f 2071//789 2072//789 2086//789 2085//789 +f 2072//790 2059//790 2073//790 2086//790 +f 2073//759 2074//759 2088//759 2087//759 +f 2074//760 2075//760 2089//760 2088//760 +f 2075//257 2076//257 2090//257 2089//257 +f 2076//258 2077//258 2091//258 2090//258 +f 2077//87 2078//87 2092//87 2091//87 +f 2078//144 2079//144 2093//144 2092//144 +f 2079//761 2080//761 2094//761 2093//761 +f 2080//762 2081//762 2095//762 2094//762 +f 2081//763 2082//763 2096//763 2095//763 +f 2082//764 2083//764 2097//764 2096//764 +f 2083//765 2084//765 2098//765 2097//765 +f 2084//766 2085//766 2099//766 2098//766 +f 2085//596 2086//596 2100//596 2099//596 +f 2086//519 2073//519 2087//519 2100//519 +usemtl glass +f 1789//45 1796//45 1795//45 +f 1811//46 1812//46 1805//46 +f 1789//45 1795//45 1794//45 +f 1810//46 1811//46 1805//46 +f 1789//45 1794//45 1793//45 +f 1809//46 1810//46 1805//46 +f 1789//45 1793//45 1792//45 +f 1808//46 1809//46 1805//46 +f 1789//45 1792//45 1791//45 +f 1807//46 1808//46 1805//46 +f 1791//45 1790//45 1789//45 +f 1805//46 1806//46 1807//46 +f 1789//791 1790//791 1798//791 1797//791 +f 1790//792 1791//792 1799//792 1798//792 +f 1791//793 1792//793 1800//793 1799//793 +f 1792//794 1793//794 1801//794 1800//794 +f 1793//795 1794//795 1802//795 1801//795 +f 1794//88 1795//88 1803//88 1802//88 +f 1795//208 1796//208 1804//208 1803//208 +f 1796//796 1789//796 1797//796 1804//796 +f 1797//797 1798//797 1806//797 1805//797 +f 1798//798 1799//798 1807//798 1806//798 +f 1799//799 1800//799 1808//799 1807//799 +f 1800//711 1801//711 1809//711 1808//711 +f 1801//800 1802//800 1810//800 1809//800 +f 1802//107 1803//107 1811//107 1810//107 +f 1803//801 1804//801 1812//801 1811//801 +f 1804//802 1797//802 1805//802 1812//802 +f 1813//45 1820//45 1819//45 +f 1835//46 1836//46 1829//46 +f 1813//45 1819//45 1818//45 +f 1834//46 1835//46 1829//46 +f 1813//45 1818//45 1817//45 +f 1833//46 1834//46 1829//46 +f 1813//45 1817//45 1816//45 +f 1832//46 1833//46 1829//46 +f 1813//45 1816//45 1815//45 +f 1831//46 1832//46 1829//46 +f 1815//45 1814//45 1813//45 +f 1829//46 1830//46 1831//46 +f 1813//803 1814//803 1822//803 1821//803 +f 1814//804 1815//804 1823//804 1822//804 +f 1815//805 1816//805 1824//805 1823//805 +f 1816//718 1817//718 1825//718 1824//718 +f 1817//806 1818//806 1826//806 1825//806 +f 1818//126 1819//126 1827//126 1826//126 +f 1819//807 1820//807 1828//807 1827//807 +f 1820//808 1813//808 1821//808 1828//808 +f 1821//791 1822//791 1830//791 1829//791 +f 1822//792 1823//792 1831//792 1830//792 +f 1823//793 1824//793 1832//793 1831//793 +f 1824//794 1825//794 1833//794 1832//794 +f 1825//795 1826//795 1834//795 1833//795 +f 1826//88 1827//88 1835//88 1834//88 +f 1827//208 1828//208 1836//208 1835//208 +f 1828//796 1821//796 1829//796 1836//796 +f 2101//45 2108//45 2107//45 +f 2123//46 2124//46 2117//46 +f 2101//45 2107//45 2106//45 +f 2122//46 2123//46 2117//46 +f 2101//45 2106//45 2105//45 +f 2121//46 2122//46 2117//46 +f 2101//45 2105//45 2104//45 +f 2120//46 2121//46 2117//46 +f 2101//45 2104//45 2103//45 +f 2119//46 2120//46 2117//46 +f 2103//45 2102//45 2101//45 +f 2117//46 2118//46 2119//46 +f 2101//809 2102//809 2110//809 2109//809 +f 2102//810 2103//810 2111//810 2110//810 +f 2103//811 2104//811 2112//811 2111//811 +f 2104//812 2105//812 2113//812 2112//812 +f 2105//813 2106//813 2114//813 2113//813 +f 2106//814 2107//814 2115//814 2114//814 +f 2107//732 2108//732 2116//732 2115//732 +f 2108//815 2101//815 2109//815 2116//815 +f 2109//816 2110//816 2118//816 2117//816 +f 2110//817 2111//817 2119//817 2118//817 +f 2111//818 2112//818 2120//818 2119//818 +f 2112//819 2113//819 2121//819 2120//819 +f 2113//820 2114//820 2122//820 2121//820 +f 2114//821 2115//821 2123//821 2122//821 +f 2115//822 2116//822 2124//822 2123//822 +f 2116//823 2109//823 2117//823 2124//823 +f 2125//45 2132//45 2131//45 +f 2147//46 2148//46 2141//46 +f 2125//45 2131//45 2130//45 +f 2146//46 2147//46 2141//46 +f 2125//45 2130//45 2129//45 +f 2145//46 2146//46 2141//46 +f 2125//45 2129//45 2128//45 +f 2144//46 2145//46 2141//46 +f 2125//45 2128//45 2127//45 +f 2143//46 2144//46 2141//46 +f 2127//45 2126//45 2125//45 +f 2141//46 2142//46 2143//46 +f 2125//824 2126//824 2134//824 2133//824 +f 2126//825 2127//825 2135//825 2134//825 +f 2127//826 2128//826 2136//826 2135//826 +f 2128//827 2129//827 2137//827 2136//827 +f 2129//828 2130//828 2138//828 2137//828 +f 2130//829 2131//829 2139//829 2138//829 +f 2131//830 2132//830 2140//830 2139//830 +f 2132//831 2125//831 2133//831 2140//831 +f 2133//809 2134//809 2142//809 2141//809 +f 2134//810 2135//810 2143//810 2142//810 +f 2135//811 2136//811 2144//811 2143//811 +f 2136//812 2137//812 2145//812 2144//812 +f 2137//813 2138//813 2146//813 2145//813 +f 2138//814 2139//814 2147//814 2146//814 +f 2139//732 2140//732 2148//732 2147//732 +f 2140//815 2133//815 2141//815 2148//815 +usemtl dark +f 1837//45 1851//45 1850//45 +f 1880//46 1881//46 1867//46 +f 1837//45 1850//45 1849//45 +f 1879//46 1880//46 1867//46 +f 1848//45 1847//45 1846//45 +f 1876//46 1877//46 1878//46 +f 1848//45 1846//45 1845//45 +f 1875//46 1876//46 1878//46 +f 1849//45 1848//45 1845//45 +f 1875//46 1878//46 1879//46 +f 1837//45 1849//45 1845//45 +f 1875//46 1879//46 1867//46 +f 1837//45 1845//45 1844//45 +f 1874//46 1875//46 1867//46 +f 1844//45 1843//45 1842//45 +f 1872//46 1873//46 1874//46 +f 1844//45 1842//45 1841//45 +f 1871//46 1872//46 1874//46 +f 1837//45 1844//45 1841//45 +f 1871//46 1874//46 1867//46 +f 1837//45 1841//45 1840//45 +f 1870//46 1871//46 1867//46 +f 1840//45 1839//45 1838//45 +f 1868//46 1869//46 1870//46 +f 1840//45 1838//45 1837//45 +f 1867//46 1868//46 1870//46 +f 1837//832 1838//832 1853//832 1852//832 +f 1838//833 1839//833 1854//833 1853//833 +f 1839//479 1840//479 1855//479 1854//479 +f 1840//834 1841//834 1856//834 1855//834 +f 1841//144 1842//144 1857//144 1856//144 +f 1842//835 1843//835 1858//835 1857//835 +f 1843//836 1844//836 1859//836 1858//836 +f 1844//837 1845//837 1860//837 1859//837 +f 1845//838 1846//838 1861//838 1860//838 +f 1846//839 1847//839 1862//839 1861//839 +f 1847//519 1848//519 1863//519 1862//519 +f 1848//840 1849//840 1864//840 1863//840 +f 1849//841 1850//841 1865//841 1864//841 +f 1850//842 1851//842 1866//842 1865//842 +f 1851//843 1837//843 1852//843 1866//843 +f 1852//844 1853//844 1868//844 1867//844 +f 1853//845 1854//845 1869//845 1868//845 +f 1854//272 1855//272 1870//272 1869//272 +f 1855//846 1856//846 1871//846 1870//846 +f 1856//620 1857//620 1872//620 1871//620 +f 1857//847 1858//847 1873//847 1872//847 +f 1858//848 1859//848 1874//848 1873//848 +f 1859//849 1860//849 1875//849 1874//849 +f 1860//850 1861//850 1876//850 1875//850 +f 1861//851 1862//851 1877//851 1876//851 +f 1862//185 1863//185 1878//185 1877//185 +f 1863//852 1864//852 1879//852 1878//852 +f 1864//853 1865//853 1880//853 1879//853 +f 1865//854 1866//854 1881//854 1880//854 +f 1866//855 1852//855 1867//855 1881//855 +f 1882//45 1896//45 1895//45 +f 1925//46 1926//46 1912//46 +f 1882//45 1895//45 1894//45 +f 1924//46 1925//46 1912//46 +f 1893//45 1892//45 1891//45 +f 1921//46 1922//46 1923//46 +f 1893//45 1891//45 1890//45 +f 1920//46 1921//46 1923//46 +f 1894//45 1893//45 1890//45 +f 1920//46 1923//46 1924//46 +f 1882//45 1894//45 1890//45 +f 1920//46 1924//46 1912//46 +f 1882//45 1890//45 1889//45 +f 1919//46 1920//46 1912//46 +f 1889//45 1888//45 1887//45 +f 1917//46 1918//46 1919//46 +f 1889//45 1887//45 1886//45 +f 1916//46 1917//46 1919//46 +f 1882//45 1889//45 1886//45 +f 1916//46 1919//46 1912//46 +f 1882//45 1886//45 1885//45 +f 1915//46 1916//46 1912//46 +f 1885//45 1884//45 1883//45 +f 1913//46 1914//46 1915//46 +f 1885//45 1883//45 1882//45 +f 1912//46 1913//46 1915//46 +f 1882//856 1883//856 1898//856 1897//856 +f 1883//857 1884//857 1899//857 1898//857 +f 1884//287 1885//287 1900//287 1899//287 +f 1885//858 1886//858 1901//858 1900//858 +f 1886//628 1887//628 1902//628 1901//628 +f 1887//859 1888//859 1903//859 1902//859 +f 1888//860 1889//860 1904//860 1903//860 +f 1889//861 1890//861 1905//861 1904//861 +f 1890//862 1891//862 1906//862 1905//862 +f 1891//863 1892//863 1907//863 1906//863 +f 1892//195 1893//195 1908//195 1907//195 +f 1893//864 1894//864 1909//864 1908//864 +f 1894//865 1895//865 1910//865 1909//865 +f 1895//866 1896//866 1911//866 1910//866 +f 1896//867 1882//867 1897//867 1911//867 +f 1897//832 1898//832 1913//832 1912//832 +f 1898//833 1899//833 1914//833 1913//833 +f 1899//479 1900//479 1915//479 1914//479 +f 1900//834 1901//834 1916//834 1915//834 +f 1901//144 1902//144 1917//144 1916//144 +f 1902//835 1903//835 1918//835 1917//835 +f 1903//836 1904//836 1919//836 1918//836 +f 1904//837 1905//837 1920//837 1919//837 +f 1905//838 1906//838 1921//838 1920//838 +f 1906//839 1907//839 1922//839 1921//839 +f 1907//519 1908//519 1923//519 1922//519 +f 1908//840 1909//840 1924//840 1923//840 +f 1909//841 1910//841 1925//841 1924//841 +f 1910//842 1911//842 1926//842 1925//842 +f 1911//843 1897//843 1912//843 1926//843 +o hindLegL +usemtl frame +f 2149//868 2150//868 2163//868 2162//868 +f 2150//662 2151//662 2164//662 2163//662 +f 2151//869 2152//869 2165//869 2164//869 +f 2152//662 2153//662 2166//662 2165//662 +f 2153//870 2154//870 2167//870 2166//870 +f 2154//662 2155//662 2168//662 2167//662 +f 2155//871 2156//871 2169//871 2168//871 +f 2156//872 2157//872 2170//872 2169//872 +f 2157//873 2158//873 2171//873 2170//873 +f 2158//872 2159//872 2172//872 2171//872 +f 2159//874 2160//874 2173//874 2172//874 +f 2160//875 2161//875 2174//875 2173//875 +f 2161//662 2149//662 2162//662 2174//662 +f 2162//876 2163//876 2176//876 2175//876 +f 2163//683 2164//683 2177//683 2176//683 +f 2164//877 2165//877 2178//877 2177//877 +f 2165//683 2166//683 2179//683 2178//683 +f 2166//878 2167//878 2180//878 2179//878 +f 2167//683 2168//683 2181//683 2180//683 +f 2168//879 2169//879 2182//879 2181//879 +f 2169//880 2170//880 2183//880 2182//880 +f 2170//881 2171//881 2184//881 2183//881 +f 2171//880 2172//880 2185//880 2184//880 +f 2172//882 2173//882 2186//882 2185//882 +f 2173//883 2174//883 2187//883 2186//883 +f 2174//683 2162//683 2175//683 2187//683 +f 2149//45 2161//45 2160//45 +f 2186//46 2187//46 2175//46 +f 2149//45 2160//45 2159//45 +f 2185//46 2186//46 2175//46 +f 2149//45 2159//45 2158//45 +f 2184//46 2185//46 2175//46 +f 2149//45 2158//45 2157//45 +f 2183//46 2184//46 2175//46 +f 2149//45 2157//45 2156//45 +f 2182//46 2183//46 2175//46 +f 2156//45 2155//45 2154//45 +f 2180//46 2181//46 2182//46 +f 2156//45 2154//45 2153//45 +f 2179//46 2180//46 2182//46 +f 2149//45 2156//45 2153//45 +f 2179//46 2182//46 2175//46 +f 2149//45 2153//45 2152//45 +f 2178//46 2179//46 2175//46 +f 2149//45 2152//45 2151//45 +f 2177//46 2178//46 2175//46 +f 2151//45 2150//45 2149//45 +f 2175//46 2176//46 2177//46 +usemtl dark +f 2188//45 2197//45 2196//45 +f 2216//46 2217//46 2208//46 +f 2188//45 2196//45 2195//45 +f 2215//46 2216//46 2208//46 +f 2188//45 2195//45 2194//45 +f 2214//46 2215//46 2208//46 +f 2188//45 2194//45 2193//45 +f 2213//46 2214//46 2208//46 +f 2188//45 2193//45 2192//45 +f 2212//46 2213//46 2208//46 +f 2192//45 2191//45 2190//45 +f 2210//46 2211//46 2212//46 +f 2188//45 2192//45 2190//45 +f 2210//46 2212//46 2208//46 +f 2190//45 2189//45 2188//45 +f 2208//46 2209//46 2210//46 +f 2188//519 2189//519 2199//519 2198//519 +f 2189//884 2190//884 2200//884 2199//884 +f 2190//538 2191//538 2201//538 2200//538 +f 2191//885 2192//885 2202//885 2201//885 +f 2192//886 2193//886 2203//886 2202//886 +f 2193//887 2194//887 2204//887 2203//887 +f 2194//87 2195//87 2205//87 2204//87 +f 2195//888 2196//888 2206//888 2205//888 +f 2196//889 2197//889 2207//889 2206//889 +f 2197//890 2188//890 2198//890 2207//890 +f 2198//778 2199//778 2209//778 2208//778 +f 2199//891 2200//891 2210//891 2209//891 +f 2200//892 2201//892 2211//892 2210//892 +f 2201//893 2202//893 2212//893 2211//893 +f 2202//894 2203//894 2213//894 2212//894 +f 2203//895 2204//895 2214//895 2213//895 +f 2204//106 2205//106 2215//106 2214//106 +f 2205//896 2206//896 2216//896 2215//896 +f 2206//897 2207//897 2217//897 2216//897 +f 2207//898 2198//898 2208//898 2217//898 +f 2218//45 2227//45 2226//45 +f 2246//46 2247//46 2238//46 +f 2218//45 2226//45 2225//45 +f 2245//46 2246//46 2238//46 +f 2218//45 2225//45 2224//45 +f 2244//46 2245//46 2238//46 +f 2218//45 2224//45 2223//45 +f 2243//46 2244//46 2238//46 +f 2218//45 2223//45 2222//45 +f 2242//46 2243//46 2238//46 +f 2222//45 2221//45 2220//45 +f 2240//46 2241//46 2242//46 +f 2218//45 2222//45 2220//45 +f 2240//46 2242//46 2238//46 +f 2220//45 2219//45 2218//45 +f 2238//46 2239//46 2240//46 +f 2218//790 2219//790 2229//790 2228//790 +f 2219//899 2220//899 2230//899 2229//899 +f 2220//900 2221//900 2231//900 2230//900 +f 2221//901 2222//901 2232//901 2231//901 +f 2222//902 2223//902 2233//902 2232//902 +f 2223//903 2224//903 2234//903 2233//903 +f 2224//125 2225//125 2235//125 2234//125 +f 2225//904 2226//904 2236//904 2235//904 +f 2226//905 2227//905 2237//905 2236//905 +f 2227//906 2218//906 2228//906 2237//906 +f 2228//519 2229//519 2239//519 2238//519 +f 2229//884 2230//884 2240//884 2239//884 +f 2230//538 2231//538 2241//538 2240//538 +f 2231//885 2232//885 2242//885 2241//885 +f 2232//886 2233//886 2243//886 2242//886 +f 2233//887 2234//887 2244//887 2243//887 +f 2234//87 2235//87 2245//87 2244//87 +f 2235//888 2236//888 2246//888 2245//888 +f 2236//889 2237//889 2247//889 2246//889 +f 2237//890 2228//890 2238//890 2247//890 +f 2248//45 2256//45 2255//45 +f 2273//46 2274//46 2266//46 +f 2248//45 2255//45 2254//45 +f 2272//46 2273//46 2266//46 +f 2254//45 2253//45 2252//45 +f 2270//46 2271//46 2272//46 +f 2248//45 2254//45 2252//45 +f 2270//45 2272//45 2266//45 +f 2248//45 2252//45 2251//45 +f 2269//46 2270//46 2266//46 +f 2248//45 2251//45 2250//45 +f 2268//46 2269//46 2266//46 +f 2250//45 2249//45 2248//45 +f 2266//46 2267//46 2268//46 +f 2248//95 2249//95 2258//95 2257//95 +f 2249//58 2250//58 2259//58 2258//58 +f 2250//907 2251//907 2260//907 2259//907 +f 2251//908 2252//908 2261//908 2260//908 +f 2252//88 2253//88 2262//88 2261//88 +f 2253//909 2254//909 2263//909 2262//909 +f 2254//910 2255//910 2264//910 2263//910 +f 2255//911 2256//911 2265//911 2264//911 +f 2256//147 2248//147 2257//147 2265//147 +f 2257//114 2258//114 2267//114 2266//114 +f 2258//149 2259//149 2268//149 2267//149 +f 2259//912 2260//912 2269//912 2268//912 +f 2260//913 2261//913 2270//913 2269//913 +f 2261//107 2262//107 2271//107 2270//107 +f 2262//914 2263//914 2272//914 2271//914 +f 2263//915 2264//915 2273//915 2272//915 +f 2264//916 2265//916 2274//916 2273//916 +f 2265//149 2257//149 2266//149 2274//149 +f 2275//45 2283//45 2282//45 +f 2300//46 2301//46 2293//46 +f 2275//45 2282//45 2281//45 +f 2299//46 2300//46 2293//46 +f 2281//45 2280//45 2279//45 +f 2297//46 2298//46 2299//46 +f 2275//46 2281//46 2279//46 +f 2297//46 2299//46 2293//46 +f 2275//45 2279//45 2278//45 +f 2296//46 2297//46 2293//46 +f 2275//45 2278//45 2277//45 +f 2295//46 2296//46 2293//46 +f 2277//45 2276//45 2275//45 +f 2293//46 2294//46 2295//46 +f 2275//133 2276//133 2285//133 2284//133 +f 2276//159 2277//159 2286//159 2285//159 +f 2277//917 2278//917 2287//917 2286//917 +f 2278//918 2279//918 2288//918 2287//918 +f 2279//126 2280//126 2289//126 2288//126 +f 2280//919 2281//919 2290//919 2289//919 +f 2281//920 2282//920 2291//920 2290//920 +f 2282//921 2283//921 2292//921 2291//921 +f 2283//159 2275//159 2284//159 2292//159 +f 2284//95 2285//95 2294//95 2293//95 +f 2285//58 2286//58 2295//58 2294//58 +f 2286//907 2287//907 2296//907 2295//907 +f 2287//908 2288//908 2297//908 2296//908 +f 2288//88 2289//88 2298//88 2297//88 +f 2289//909 2290//909 2299//909 2298//909 +f 2290//910 2291//910 2300//910 2299//910 +f 2291//911 2292//911 2301//911 2300//911 +f 2292//147 2284//147 2293//147 2301//147 +usemtl champagne +f 2302//45 2314//45 2313//45 +f 2339//46 2340//46 2328//46 +f 2302//45 2313//45 2312//45 +f 2338//46 2339//46 2328//46 +f 2302//45 2312//45 2311//45 +f 2337//46 2338//46 2328//46 +f 2309//45 2308//45 2307//45 +f 2333//46 2334//46 2335//46 +f 2309//45 2307//45 2306//45 +f 2332//46 2333//46 2335//46 +f 2310//45 2309//45 2306//45 +f 2332//46 2335//46 2336//46 +f 2311//45 2310//45 2306//45 +f 2332//46 2336//46 2337//46 +f 2311//45 2306//45 2305//45 +f 2331//46 2332//46 2337//46 +f 2311//45 2305//45 2304//45 +f 2330//46 2331//46 2337//46 +f 2311//45 2304//45 2303//45 +f 2329//46 2330//46 2337//46 +f 2311//45 2303//45 2302//45 +f 2328//46 2329//46 2337//46 +f 2302//139 2303//139 2316//139 2315//139 +f 2303//922 2304//922 2317//922 2316//922 +f 2304//923 2305//923 2318//923 2317//923 +f 2305//139 2306//139 2319//139 2318//139 +f 2306//924 2307//924 2320//924 2319//924 +f 2307//87 2308//87 2321//87 2320//87 +f 2308//88 2309//88 2322//88 2321//88 +f 2309//87 2310//87 2323//87 2322//87 +f 2310//925 2311//925 2324//925 2323//925 +f 2311//88 2312//88 2325//88 2324//88 +f 2312//926 2313//926 2326//926 2325//926 +f 2313//927 2314//927 2327//927 2326//927 +f 2314//58 2302//58 2315//58 2327//58 +f 2315//148 2316//148 2329//148 2328//148 +f 2316//928 2317//928 2330//928 2329//928 +f 2317//929 2318//929 2331//929 2330//929 +f 2318//148 2319//148 2332//148 2331//148 +f 2319//930 2320//930 2333//930 2332//930 +f 2320//106 2321//106 2334//106 2333//106 +f 2321//107 2322//107 2335//107 2334//107 +f 2322//106 2323//106 2336//106 2335//106 +f 2323//931 2324//931 2337//931 2336//931 +f 2324//107 2325//107 2338//107 2337//107 +f 2325//932 2326//932 2339//932 2338//932 +f 2326//933 2327//933 2340//933 2339//933 +f 2327//149 2315//149 2328//149 2340//149 +f 2341//45 2353//45 2352//45 +f 2378//46 2379//46 2367//46 +f 2341//45 2352//45 2351//45 +f 2377//46 2378//46 2367//46 +f 2341//45 2351//45 2350//45 +f 2376//46 2377//46 2367//46 +f 2348//45 2347//45 2346//45 +f 2372//46 2373//46 2374//46 +f 2348//45 2346//45 2345//45 +f 2371//46 2372//46 2374//46 +f 2349//45 2348//45 2345//45 +f 2371//46 2374//46 2375//46 +f 2350//45 2349//45 2345//45 +f 2371//46 2375//46 2376//46 +f 2350//45 2345//45 2344//45 +f 2370//46 2371//46 2376//46 +f 2350//45 2344//45 2343//45 +f 2369//46 2370//46 2376//46 +f 2350//45 2343//45 2342//45 +f 2368//46 2369//46 2376//46 +f 2350//45 2342//45 2341//45 +f 2367//46 2368//46 2376//46 +f 2341//158 2342//158 2355//158 2354//158 +f 2342//934 2343//934 2356//934 2355//934 +f 2343//935 2344//935 2357//935 2356//935 +f 2344//158 2345//158 2358//158 2357//158 +f 2345//936 2346//936 2359//936 2358//936 +f 2346//125 2347//125 2360//125 2359//125 +f 2347//126 2348//126 2361//126 2360//126 +f 2348//125 2349//125 2362//125 2361//125 +f 2349//937 2350//937 2363//937 2362//937 +f 2350//126 2351//126 2364//126 2363//126 +f 2351//938 2352//938 2365//938 2364//938 +f 2352//939 2353//939 2366//939 2365//939 +f 2353//159 2341//159 2354//159 2366//159 +f 2354//139 2355//139 2368//139 2367//139 +f 2355//922 2356//922 2369//922 2368//922 +f 2356//923 2357//923 2370//923 2369//923 +f 2357//139 2358//139 2371//139 2370//139 +f 2358//924 2359//924 2372//924 2371//924 +f 2359//87 2360//87 2373//87 2372//87 +f 2360//88 2361//88 2374//88 2373//88 +f 2361//87 2362//87 2375//87 2374//87 +f 2362//925 2363//925 2376//925 2375//925 +f 2363//88 2364//88 2377//88 2376//88 +f 2364//926 2365//926 2378//926 2377//926 +f 2365//927 2366//927 2379//927 2378//927 +f 2366//58 2354//58 2367//58 2379//58 +o foreLegL +usemtl frame +f 2380//940 2381//940 2387//940 2386//940 +f 2381//941 2382//941 2388//941 2387//941 +f 2382//942 2383//942 2389//942 2388//942 +f 2383//943 2384//943 2390//943 2389//943 +f 2384//944 2385//944 2391//944 2390//944 +f 2385//945 2380//945 2386//945 2391//945 +f 2386//946 2387//946 2393//946 2392//946 +f 2387//947 2388//947 2394//947 2393//947 +f 2388//948 2389//948 2395//948 2394//948 +f 2389//949 2390//949 2396//949 2395//949 +f 2390//950 2391//950 2397//950 2396//950 +f 2391//951 2386//951 2392//951 2397//951 +f 2380//45 2385//45 2384//45 +f 2396//46 2397//46 2392//46 +f 2380//45 2384//45 2383//45 +f 2395//46 2396//46 2392//46 +f 2380//45 2383//45 2382//45 +f 2394//46 2395//46 2392//46 +f 2382//45 2381//45 2380//45 +f 2392//46 2393//46 2394//46 +usemtl glass +f 2398//45 2409//45 2408//45 +f 2432//46 2433//46 2422//46 +f 2398//45 2408//45 2407//45 +f 2431//46 2432//46 2422//46 +f 2398//45 2407//45 2406//45 +f 2430//46 2431//46 2422//46 +f 2398//45 2406//45 2405//45 +f 2429//46 2430//46 2422//46 +f 2398//45 2405//45 2404//45 +f 2428//46 2429//46 2422//46 +f 2398//45 2404//45 2403//45 +f 2427//46 2428//46 2422//46 +f 2398//45 2403//45 2402//45 +f 2426//46 2427//46 2422//46 +f 2398//45 2402//45 2401//45 +f 2425//46 2426//46 2422//46 +f 2398//45 2401//45 2400//45 +f 2424//46 2425//46 2422//46 +f 2400//45 2399//45 2398//45 +f 2422//46 2423//46 2424//46 +f 2398//952 2399//952 2411//952 2410//952 +f 2399//953 2400//953 2412//953 2411//953 +f 2400//47 2401//47 2413//47 2412//47 +f 2401//954 2402//954 2414//954 2413//954 +f 2402//539 2403//539 2415//539 2414//539 +f 2403//955 2404//955 2416//955 2415//955 +f 2404//639 2405//639 2417//639 2416//639 +f 2405//956 2406//956 2418//956 2417//956 +f 2406//957 2407//957 2419//957 2418//957 +f 2407//958 2408//958 2420//958 2419//958 +f 2408//596 2409//596 2421//596 2420//596 +f 2409//959 2398//959 2410//959 2421//959 +f 2410//960 2411//960 2423//960 2422//960 +f 2411//961 2412//961 2424//961 2423//961 +f 2412//103 2413//103 2425//103 2424//103 +f 2413//962 2414//962 2426//962 2425//962 +f 2414//963 2415//963 2427//963 2426//963 +f 2415//964 2416//964 2428//964 2427//964 +f 2416//965 2417//965 2429//965 2428//965 +f 2417//966 2418//966 2430//966 2429//966 +f 2418//967 2419//967 2431//967 2430//967 +f 2419//968 2420//968 2432//968 2431//968 +f 2420//969 2421//969 2433//969 2432//969 +f 2421//970 2410//970 2422//970 2433//970 +f 2434//45 2445//45 2444//45 +f 2468//46 2469//46 2458//46 +f 2434//45 2444//45 2443//45 +f 2467//46 2468//46 2458//46 +f 2434//45 2443//45 2442//45 +f 2466//46 2467//46 2458//46 +f 2434//45 2442//45 2441//45 +f 2465//46 2466//46 2458//46 +f 2434//45 2441//45 2440//45 +f 2464//46 2465//46 2458//46 +f 2434//45 2440//45 2439//45 +f 2463//46 2464//46 2458//46 +f 2434//45 2439//45 2438//45 +f 2462//46 2463//46 2458//46 +f 2434//45 2438//45 2437//45 +f 2461//46 2462//46 2458//46 +f 2434//45 2437//45 2436//45 +f 2460//46 2461//46 2458//46 +f 2436//45 2435//45 2434//45 +f 2458//46 2459//46 2460//46 +f 2434//971 2435//971 2447//971 2446//971 +f 2435//972 2436//972 2448//972 2447//972 +f 2436//122 2437//122 2449//122 2448//122 +f 2437//973 2438//973 2450//973 2449//973 +f 2438//974 2439//974 2451//974 2450//974 +f 2439//975 2440//975 2452//975 2451//975 +f 2440//976 2441//976 2453//976 2452//976 +f 2441//977 2442//977 2454//977 2453//977 +f 2442//978 2443//978 2455//978 2454//978 +f 2443//979 2444//979 2456//979 2455//979 +f 2444//980 2445//980 2457//980 2456//980 +f 2445//981 2434//981 2446//981 2457//981 +f 2446//952 2447//952 2459//952 2458//952 +f 2447//953 2448//953 2460//953 2459//953 +f 2448//47 2449//47 2461//47 2460//47 +f 2449//954 2450//954 2462//954 2461//954 +f 2450//539 2451//539 2463//539 2462//539 +f 2451//955 2452//955 2464//955 2463//955 +f 2452//639 2453//639 2465//639 2464//639 +f 2453//956 2454//956 2466//956 2465//956 +f 2454//957 2455//957 2467//957 2466//957 +f 2455//958 2456//958 2468//958 2467//958 +f 2456//596 2457//596 2469//596 2468//596 +f 2457//959 2446//959 2458//959 2469//959 +usemtl dark +f 2470//45 2478//45 2477//45 +f 2495//46 2496//46 2488//46 +f 2470//45 2477//45 2476//45 +f 2494//46 2495//46 2488//46 +f 2470//45 2476//45 2475//45 +f 2493//46 2494//46 2488//46 +f 2470//45 2475//45 2474//45 +f 2492//46 2493//46 2488//46 +f 2474//45 2473//45 2472//45 +f 2490//46 2491//46 2492//46 +f 2474//45 2472//45 2471//45 +f 2489//45 2490//45 2492//45 +f 2474//45 2471//45 2470//45 +f 2488//45 2489//45 2492//45 +f 2470//982 2471//982 2480//982 2479//982 +f 2471//983 2472//983 2481//983 2480//983 +f 2472//984 2473//984 2482//984 2481//984 +f 2473//206 2474//206 2483//206 2482//206 +f 2474//985 2475//985 2484//985 2483//985 +f 2475//986 2476//986 2485//986 2484//986 +f 2476//987 2477//987 2486//987 2485//987 +f 2477//596 2478//596 2487//596 2486//596 +f 2478//519 2470//519 2479//519 2487//519 +f 2479//988 2480//988 2489//988 2488//988 +f 2480//989 2481//989 2490//989 2489//989 +f 2481//990 2482//990 2491//990 2490//990 +f 2482//211 2483//211 2492//211 2491//211 +f 2483//991 2484//991 2493//991 2492//991 +f 2484//992 2485//992 2494//992 2493//992 +f 2485//993 2486//993 2495//993 2494//993 +f 2486//994 2487//994 2496//994 2495//994 +f 2487//527 2479//527 2488//527 2496//527 +f 2497//45 2505//45 2504//45 +f 2522//46 2523//46 2515//46 +f 2497//45 2504//45 2503//45 +f 2521//46 2522//46 2515//46 +f 2497//45 2503//45 2502//45 +f 2520//46 2521//46 2515//46 +f 2497//45 2502//45 2501//45 +f 2519//46 2520//46 2515//46 +f 2501//45 2500//45 2499//45 +f 2517//46 2518//46 2519//46 +f 2501//46 2499//46 2498//46 +f 2516//46 2517//46 2519//46 +f 2501//46 2498//46 2497//46 +f 2515//46 2516//46 2519//46 +f 2497//995 2498//995 2507//995 2506//995 +f 2498//996 2499//996 2508//996 2507//996 +f 2499//997 2500//997 2509//997 2508//997 +f 2500//216 2501//216 2510//216 2509//216 +f 2501//998 2502//998 2511//998 2510//998 +f 2502//999 2503//999 2512//999 2511//999 +f 2503//1000 2504//1000 2513//1000 2512//1000 +f 2504//1001 2505//1001 2514//1001 2513//1001 +f 2505//535 2497//535 2506//535 2514//535 +f 2506//982 2507//982 2516//982 2515//982 +f 2507//983 2508//983 2517//983 2516//983 +f 2508//984 2509//984 2518//984 2517//984 +f 2509//206 2510//206 2519//206 2518//206 +f 2510//985 2511//985 2520//985 2519//985 +f 2511//986 2512//986 2521//986 2520//986 +f 2512//987 2513//987 2522//987 2521//987 +f 2513//596 2514//596 2523//596 2522//596 +f 2514//519 2506//519 2515//519 2523//519 +o hornL +usemtl frame +f 2524//1002 2525//1002 2530//1002 2529//1002 +f 2525//1003 2526//1003 2531//1003 2530//1003 +f 2526//1004 2527//1004 2532//1004 2531//1004 +f 2527//1005 2528//1005 2533//1005 2532//1005 +f 2528//1006 2524//1006 2529//1006 2533//1006 +f 2529//1007 2530//1007 2535//1007 2534//1007 +f 2530//1008 2531//1008 2536//1008 2535//1008 +f 2531//1009 2532//1009 2537//1009 2536//1009 +f 2532//1010 2533//1010 2538//1010 2537//1010 +f 2533//1011 2529//1011 2534//1011 2538//1011 +f 2524//45 2528//45 2527//45 +f 2537//46 2538//46 2534//46 +f 2524//45 2527//45 2526//45 +f 2536//46 2537//46 2534//46 +f 2526//45 2525//45 2524//45 +f 2534//46 2535//46 2536//46 +usemtl dark +f 2539//45 2544//45 2543//45 +f 2555//46 2556//46 2551//46 +f 2539//45 2543//45 2542//45 +f 2554//46 2555//46 2551//46 +f 2539//45 2542//45 2541//45 +f 2553//46 2554//46 2551//46 +f 2541//45 2540//45 2539//45 +f 2551//46 2552//46 2553//46 +f 2539//1012 2540//1012 2546//1012 2545//1012 +f 2540//1013 2541//1013 2547//1013 2546//1013 +f 2541//1014 2542//1014 2548//1014 2547//1014 +f 2542//1015 2543//1015 2549//1015 2548//1015 +f 2543//1016 2544//1016 2550//1016 2549//1016 +f 2544//58 2539//58 2545//58 2550//58 +f 2545//1017 2546//1017 2552//1017 2551//1017 +f 2546//1018 2547//1018 2553//1018 2552//1018 +f 2547//1019 2548//1019 2554//1019 2553//1019 +f 2548//1020 2549//1020 2555//1020 2554//1020 +f 2549//1021 2550//1021 2556//1021 2555//1021 +f 2550//149 2545//149 2551//149 2556//149 +f 2557//45 2562//45 2561//45 +f 2573//46 2574//46 2569//46 +f 2557//45 2561//45 2560//45 +f 2572//46 2573//46 2569//46 +f 2557//45 2560//45 2559//45 +f 2571//46 2572//46 2569//46 +f 2559//45 2558//45 2557//45 +f 2569//46 2570//46 2571//46 +f 2557//1022 2558//1022 2564//1022 2563//1022 +f 2558//1023 2559//1023 2565//1023 2564//1023 +f 2559//1024 2560//1024 2566//1024 2565//1024 +f 2560//1025 2561//1025 2567//1025 2566//1025 +f 2561//1026 2562//1026 2568//1026 2567//1026 +f 2562//159 2557//159 2563//159 2568//159 +f 2563//1012 2564//1012 2570//1012 2569//1012 +f 2564//1013 2565//1013 2571//1013 2570//1013 +f 2565//1014 2566//1014 2572//1014 2571//1014 +f 2566//1015 2567//1015 2573//1015 2572//1015 +f 2567//1016 2568//1016 2574//1016 2573//1016 +f 2568//58 2563//58 2569//58 2574//58 +o hindLegR +usemtl frame +f 2575//868 2576//868 2589//868 2588//868 +f 2576//662 2577//662 2590//662 2589//662 +f 2577//869 2578//869 2591//869 2590//869 +f 2578//662 2579//662 2592//662 2591//662 +f 2579//870 2580//870 2593//870 2592//870 +f 2580//662 2581//662 2594//662 2593//662 +f 2581//871 2582//871 2595//871 2594//871 +f 2582//872 2583//872 2596//872 2595//872 +f 2583//873 2584//873 2597//873 2596//873 +f 2584//872 2585//872 2598//872 2597//872 +f 2585//874 2586//874 2599//874 2598//874 +f 2586//875 2587//875 2600//875 2599//875 +f 2587//662 2575//662 2588//662 2600//662 +f 2588//876 2589//876 2602//876 2601//876 +f 2589//683 2590//683 2603//683 2602//683 +f 2590//877 2591//877 2604//877 2603//877 +f 2591//683 2592//683 2605//683 2604//683 +f 2592//878 2593//878 2606//878 2605//878 +f 2593//683 2594//683 2607//683 2606//683 +f 2594//879 2595//879 2608//879 2607//879 +f 2595//880 2596//880 2609//880 2608//880 +f 2596//881 2597//881 2610//881 2609//881 +f 2597//880 2598//880 2611//880 2610//880 +f 2598//882 2599//882 2612//882 2611//882 +f 2599//883 2600//883 2613//883 2612//883 +f 2600//683 2588//683 2601//683 2613//683 +f 2575//45 2587//45 2586//45 +f 2612//46 2613//46 2601//46 +f 2575//45 2586//45 2585//45 +f 2611//46 2612//46 2601//46 +f 2575//45 2585//45 2584//45 +f 2610//46 2611//46 2601//46 +f 2575//45 2584//45 2583//45 +f 2609//46 2610//46 2601//46 +f 2575//45 2583//45 2582//45 +f 2608//46 2609//46 2601//46 +f 2582//45 2581//45 2580//45 +f 2606//46 2607//46 2608//46 +f 2582//45 2580//45 2579//45 +f 2605//46 2606//46 2608//46 +f 2575//45 2582//45 2579//45 +f 2605//46 2608//46 2601//46 +f 2575//45 2579//45 2578//45 +f 2604//46 2605//46 2601//46 +f 2575//45 2578//45 2577//45 +f 2603//46 2604//46 2601//46 +f 2577//45 2576//45 2575//45 +f 2601//46 2602//46 2603//46 +usemtl dark +f 2614//45 2623//45 2622//45 +f 2642//46 2643//46 2634//46 +f 2614//45 2622//45 2621//45 +f 2641//46 2642//46 2634//46 +f 2614//45 2621//45 2620//45 +f 2640//46 2641//46 2634//46 +f 2614//45 2620//45 2619//45 +f 2639//46 2640//46 2634//46 +f 2614//45 2619//45 2618//45 +f 2638//46 2639//46 2634//46 +f 2618//45 2617//45 2616//45 +f 2636//46 2637//46 2638//46 +f 2614//45 2618//45 2616//45 +f 2636//46 2638//46 2634//46 +f 2616//45 2615//45 2614//45 +f 2634//46 2635//46 2636//46 +f 2614//519 2615//519 2625//519 2624//519 +f 2615//884 2616//884 2626//884 2625//884 +f 2616//538 2617//538 2627//538 2626//538 +f 2617//885 2618//885 2628//885 2627//885 +f 2618//886 2619//886 2629//886 2628//886 +f 2619//887 2620//887 2630//887 2629//887 +f 2620//87 2621//87 2631//87 2630//87 +f 2621//888 2622//888 2632//888 2631//888 +f 2622//889 2623//889 2633//889 2632//889 +f 2623//890 2614//890 2624//890 2633//890 +f 2624//778 2625//778 2635//778 2634//778 +f 2625//891 2626//891 2636//891 2635//891 +f 2626//892 2627//892 2637//892 2636//892 +f 2627//893 2628//893 2638//893 2637//893 +f 2628//894 2629//894 2639//894 2638//894 +f 2629//895 2630//895 2640//895 2639//895 +f 2630//106 2631//106 2641//106 2640//106 +f 2631//896 2632//896 2642//896 2641//896 +f 2632//897 2633//897 2643//897 2642//897 +f 2633//898 2624//898 2634//898 2643//898 +f 2644//45 2653//45 2652//45 +f 2672//46 2673//46 2664//46 +f 2644//45 2652//45 2651//45 +f 2671//46 2672//46 2664//46 +f 2644//45 2651//45 2650//45 +f 2670//46 2671//46 2664//46 +f 2644//45 2650//45 2649//45 +f 2669//46 2670//46 2664//46 +f 2644//45 2649//45 2648//45 +f 2668//46 2669//46 2664//46 +f 2648//45 2647//45 2646//45 +f 2666//46 2667//46 2668//46 +f 2644//45 2648//45 2646//45 +f 2666//46 2668//46 2664//46 +f 2646//45 2645//45 2644//45 +f 2664//46 2665//46 2666//46 +f 2644//790 2645//790 2655//790 2654//790 +f 2645//899 2646//899 2656//899 2655//899 +f 2646//900 2647//900 2657//900 2656//900 +f 2647//901 2648//901 2658//901 2657//901 +f 2648//902 2649//902 2659//902 2658//902 +f 2649//903 2650//903 2660//903 2659//903 +f 2650//125 2651//125 2661//125 2660//125 +f 2651//904 2652//904 2662//904 2661//904 +f 2652//905 2653//905 2663//905 2662//905 +f 2653//906 2644//906 2654//906 2663//906 +f 2654//519 2655//519 2665//519 2664//519 +f 2655//884 2656//884 2666//884 2665//884 +f 2656//538 2657//538 2667//538 2666//538 +f 2657//885 2658//885 2668//885 2667//885 +f 2658//886 2659//886 2669//886 2668//886 +f 2659//887 2660//887 2670//887 2669//887 +f 2660//87 2661//87 2671//87 2670//87 +f 2661//888 2662//888 2672//888 2671//888 +f 2662//889 2663//889 2673//889 2672//889 +f 2663//890 2654//890 2664//890 2673//890 +f 2674//45 2682//45 2681//45 +f 2699//46 2700//46 2692//46 +f 2674//45 2681//45 2680//45 +f 2698//46 2699//46 2692//46 +f 2680//45 2679//45 2678//45 +f 2696//46 2697//46 2698//46 +f 2674//45 2680//45 2678//45 +f 2696//45 2698//45 2692//45 +f 2674//45 2678//45 2677//45 +f 2695//46 2696//46 2692//46 +f 2674//45 2677//45 2676//45 +f 2694//46 2695//46 2692//46 +f 2676//45 2675//45 2674//45 +f 2692//46 2693//46 2694//46 +f 2674//95 2675//95 2684//95 2683//95 +f 2675//58 2676//58 2685//58 2684//58 +f 2676//907 2677//907 2686//907 2685//907 +f 2677//908 2678//908 2687//908 2686//908 +f 2678//88 2679//88 2688//88 2687//88 +f 2679//909 2680//909 2689//909 2688//909 +f 2680//910 2681//910 2690//910 2689//910 +f 2681//911 2682//911 2691//911 2690//911 +f 2682//147 2674//147 2683//147 2691//147 +f 2683//114 2684//114 2693//114 2692//114 +f 2684//149 2685//149 2694//149 2693//149 +f 2685//912 2686//912 2695//912 2694//912 +f 2686//913 2687//913 2696//913 2695//913 +f 2687//107 2688//107 2697//107 2696//107 +f 2688//914 2689//914 2698//914 2697//914 +f 2689//915 2690//915 2699//915 2698//915 +f 2690//916 2691//916 2700//916 2699//916 +f 2691//149 2683//149 2692//149 2700//149 +f 2701//45 2709//45 2708//45 +f 2726//46 2727//46 2719//46 +f 2701//45 2708//45 2707//45 +f 2725//46 2726//46 2719//46 +f 2707//45 2706//45 2705//45 +f 2723//46 2724//46 2725//46 +f 2701//46 2707//46 2705//46 +f 2723//46 2725//46 2719//46 +f 2701//45 2705//45 2704//45 +f 2722//46 2723//46 2719//46 +f 2701//45 2704//45 2703//45 +f 2721//46 2722//46 2719//46 +f 2703//45 2702//45 2701//45 +f 2719//46 2720//46 2721//46 +f 2701//133 2702//133 2711//133 2710//133 +f 2702//159 2703//159 2712//159 2711//159 +f 2703//917 2704//917 2713//917 2712//917 +f 2704//918 2705//918 2714//918 2713//918 +f 2705//126 2706//126 2715//126 2714//126 +f 2706//919 2707//919 2716//919 2715//919 +f 2707//920 2708//920 2717//920 2716//920 +f 2708//921 2709//921 2718//921 2717//921 +f 2709//159 2701//159 2710//159 2718//159 +f 2710//95 2711//95 2720//95 2719//95 +f 2711//58 2712//58 2721//58 2720//58 +f 2712//907 2713//907 2722//907 2721//907 +f 2713//908 2714//908 2723//908 2722//908 +f 2714//88 2715//88 2724//88 2723//88 +f 2715//909 2716//909 2725//909 2724//909 +f 2716//910 2717//910 2726//910 2725//910 +f 2717//911 2718//911 2727//911 2726//911 +f 2718//147 2710//147 2719//147 2727//147 +usemtl champagne +f 2728//45 2740//45 2739//45 +f 2765//46 2766//46 2754//46 +f 2728//45 2739//45 2738//45 +f 2764//46 2765//46 2754//46 +f 2728//45 2738//45 2737//45 +f 2763//46 2764//46 2754//46 +f 2735//45 2734//45 2733//45 +f 2759//46 2760//46 2761//46 +f 2735//45 2733//45 2732//45 +f 2758//46 2759//46 2761//46 +f 2736//45 2735//45 2732//45 +f 2758//46 2761//46 2762//46 +f 2737//45 2736//45 2732//45 +f 2758//46 2762//46 2763//46 +f 2737//45 2732//45 2731//45 +f 2757//46 2758//46 2763//46 +f 2737//45 2731//45 2730//45 +f 2756//46 2757//46 2763//46 +f 2737//45 2730//45 2729//45 +f 2755//46 2756//46 2763//46 +f 2737//45 2729//45 2728//45 +f 2754//46 2755//46 2763//46 +f 2728//139 2729//139 2742//139 2741//139 +f 2729//922 2730//922 2743//922 2742//922 +f 2730//923 2731//923 2744//923 2743//923 +f 2731//139 2732//139 2745//139 2744//139 +f 2732//924 2733//924 2746//924 2745//924 +f 2733//87 2734//87 2747//87 2746//87 +f 2734//88 2735//88 2748//88 2747//88 +f 2735//87 2736//87 2749//87 2748//87 +f 2736//925 2737//925 2750//925 2749//925 +f 2737//88 2738//88 2751//88 2750//88 +f 2738//926 2739//926 2752//926 2751//926 +f 2739//927 2740//927 2753//927 2752//927 +f 2740//58 2728//58 2741//58 2753//58 +f 2741//148 2742//148 2755//148 2754//148 +f 2742//928 2743//928 2756//928 2755//928 +f 2743//929 2744//929 2757//929 2756//929 +f 2744//148 2745//148 2758//148 2757//148 +f 2745//930 2746//930 2759//930 2758//930 +f 2746//106 2747//106 2760//106 2759//106 +f 2747//107 2748//107 2761//107 2760//107 +f 2748//106 2749//106 2762//106 2761//106 +f 2749//931 2750//931 2763//931 2762//931 +f 2750//107 2751//107 2764//107 2763//107 +f 2751//932 2752//932 2765//932 2764//932 +f 2752//933 2753//933 2766//933 2765//933 +f 2753//149 2741//149 2754//149 2766//149 +f 2767//45 2779//45 2778//45 +f 2804//46 2805//46 2793//46 +f 2767//45 2778//45 2777//45 +f 2803//46 2804//46 2793//46 +f 2767//45 2777//45 2776//45 +f 2802//46 2803//46 2793//46 +f 2774//45 2773//45 2772//45 +f 2798//46 2799//46 2800//46 +f 2774//45 2772//45 2771//45 +f 2797//46 2798//46 2800//46 +f 2775//45 2774//45 2771//45 +f 2797//46 2800//46 2801//46 +f 2776//45 2775//45 2771//45 +f 2797//46 2801//46 2802//46 +f 2776//45 2771//45 2770//45 +f 2796//46 2797//46 2802//46 +f 2776//45 2770//45 2769//45 +f 2795//46 2796//46 2802//46 +f 2776//45 2769//45 2768//45 +f 2794//46 2795//46 2802//46 +f 2776//45 2768//45 2767//45 +f 2793//46 2794//46 2802//46 +f 2767//158 2768//158 2781//158 2780//158 +f 2768//934 2769//934 2782//934 2781//934 +f 2769//935 2770//935 2783//935 2782//935 +f 2770//158 2771//158 2784//158 2783//158 +f 2771//936 2772//936 2785//936 2784//936 +f 2772//125 2773//125 2786//125 2785//125 +f 2773//126 2774//126 2787//126 2786//126 +f 2774//125 2775//125 2788//125 2787//125 +f 2775//937 2776//937 2789//937 2788//937 +f 2776//126 2777//126 2790//126 2789//126 +f 2777//938 2778//938 2791//938 2790//938 +f 2778//939 2779//939 2792//939 2791//939 +f 2779//159 2767//159 2780//159 2792//159 +f 2780//139 2781//139 2794//139 2793//139 +f 2781//922 2782//922 2795//922 2794//922 +f 2782//923 2783//923 2796//923 2795//923 +f 2783//139 2784//139 2797//139 2796//139 +f 2784//924 2785//924 2798//924 2797//924 +f 2785//87 2786//87 2799//87 2798//87 +f 2786//88 2787//88 2800//88 2799//88 +f 2787//87 2788//87 2801//87 2800//87 +f 2788//925 2789//925 2802//925 2801//925 +f 2789//88 2790//88 2803//88 2802//88 +f 2790//926 2791//926 2804//926 2803//926 +f 2791//927 2792//927 2805//927 2804//927 +f 2792//58 2780//58 2793//58 2805//58 +o foreLegR +usemtl frame +f 2806//940 2807//940 2813//940 2812//940 +f 2807//941 2808//941 2814//941 2813//941 +f 2808//942 2809//942 2815//942 2814//942 +f 2809//943 2810//943 2816//943 2815//943 +f 2810//944 2811//944 2817//944 2816//944 +f 2811//945 2806//945 2812//945 2817//945 +f 2812//946 2813//946 2819//946 2818//946 +f 2813//947 2814//947 2820//947 2819//947 +f 2814//948 2815//948 2821//948 2820//948 +f 2815//949 2816//949 2822//949 2821//949 +f 2816//950 2817//950 2823//950 2822//950 +f 2817//951 2812//951 2818//951 2823//951 +f 2806//45 2811//45 2810//45 +f 2822//46 2823//46 2818//46 +f 2806//45 2810//45 2809//45 +f 2821//46 2822//46 2818//46 +f 2806//45 2809//45 2808//45 +f 2820//46 2821//46 2818//46 +f 2808//45 2807//45 2806//45 +f 2818//46 2819//46 2820//46 +usemtl glass +f 2824//45 2835//45 2834//45 +f 2858//46 2859//46 2848//46 +f 2824//45 2834//45 2833//45 +f 2857//46 2858//46 2848//46 +f 2824//45 2833//45 2832//45 +f 2856//46 2857//46 2848//46 +f 2824//45 2832//45 2831//45 +f 2855//46 2856//46 2848//46 +f 2824//45 2831//45 2830//45 +f 2854//46 2855//46 2848//46 +f 2824//45 2830//45 2829//45 +f 2853//46 2854//46 2848//46 +f 2824//45 2829//45 2828//45 +f 2852//46 2853//46 2848//46 +f 2824//45 2828//45 2827//45 +f 2851//46 2852//46 2848//46 +f 2824//45 2827//45 2826//45 +f 2850//46 2851//46 2848//46 +f 2826//45 2825//45 2824//45 +f 2848//46 2849//46 2850//46 +f 2824//952 2825//952 2837//952 2836//952 +f 2825//953 2826//953 2838//953 2837//953 +f 2826//47 2827//47 2839//47 2838//47 +f 2827//954 2828//954 2840//954 2839//954 +f 2828//539 2829//539 2841//539 2840//539 +f 2829//955 2830//955 2842//955 2841//955 +f 2830//639 2831//639 2843//639 2842//639 +f 2831//956 2832//956 2844//956 2843//956 +f 2832//957 2833//957 2845//957 2844//957 +f 2833//958 2834//958 2846//958 2845//958 +f 2834//596 2835//596 2847//596 2846//596 +f 2835//959 2824//959 2836//959 2847//959 +f 2836//960 2837//960 2849//960 2848//960 +f 2837//961 2838//961 2850//961 2849//961 +f 2838//103 2839//103 2851//103 2850//103 +f 2839//962 2840//962 2852//962 2851//962 +f 2840//963 2841//963 2853//963 2852//963 +f 2841//964 2842//964 2854//964 2853//964 +f 2842//965 2843//965 2855//965 2854//965 +f 2843//966 2844//966 2856//966 2855//966 +f 2844//967 2845//967 2857//967 2856//967 +f 2845//968 2846//968 2858//968 2857//968 +f 2846//969 2847//969 2859//969 2858//969 +f 2847//970 2836//970 2848//970 2859//970 +f 2860//45 2871//45 2870//45 +f 2894//46 2895//46 2884//46 +f 2860//45 2870//45 2869//45 +f 2893//46 2894//46 2884//46 +f 2860//45 2869//45 2868//45 +f 2892//46 2893//46 2884//46 +f 2860//45 2868//45 2867//45 +f 2891//46 2892//46 2884//46 +f 2860//45 2867//45 2866//45 +f 2890//46 2891//46 2884//46 +f 2860//45 2866//45 2865//45 +f 2889//46 2890//46 2884//46 +f 2860//45 2865//45 2864//45 +f 2888//46 2889//46 2884//46 +f 2860//45 2864//45 2863//45 +f 2887//46 2888//46 2884//46 +f 2860//45 2863//45 2862//45 +f 2886//46 2887//46 2884//46 +f 2862//45 2861//45 2860//45 +f 2884//46 2885//46 2886//46 +f 2860//971 2861//971 2873//971 2872//971 +f 2861//972 2862//972 2874//972 2873//972 +f 2862//122 2863//122 2875//122 2874//122 +f 2863//973 2864//973 2876//973 2875//973 +f 2864//974 2865//974 2877//974 2876//974 +f 2865//975 2866//975 2878//975 2877//975 +f 2866//976 2867//976 2879//976 2878//976 +f 2867//977 2868//977 2880//977 2879//977 +f 2868//978 2869//978 2881//978 2880//978 +f 2869//979 2870//979 2882//979 2881//979 +f 2870//980 2871//980 2883//980 2882//980 +f 2871//981 2860//981 2872//981 2883//981 +f 2872//952 2873//952 2885//952 2884//952 +f 2873//953 2874//953 2886//953 2885//953 +f 2874//47 2875//47 2887//47 2886//47 +f 2875//954 2876//954 2888//954 2887//954 +f 2876//539 2877//539 2889//539 2888//539 +f 2877//955 2878//955 2890//955 2889//955 +f 2878//639 2879//639 2891//639 2890//639 +f 2879//956 2880//956 2892//956 2891//956 +f 2880//957 2881//957 2893//957 2892//957 +f 2881//958 2882//958 2894//958 2893//958 +f 2882//596 2883//596 2895//596 2894//596 +f 2883//959 2872//959 2884//959 2895//959 +usemtl dark +f 2896//45 2904//45 2903//45 +f 2921//46 2922//46 2914//46 +f 2896//45 2903//45 2902//45 +f 2920//46 2921//46 2914//46 +f 2896//45 2902//45 2901//45 +f 2919//46 2920//46 2914//46 +f 2896//45 2901//45 2900//45 +f 2918//46 2919//46 2914//46 +f 2900//45 2899//45 2898//45 +f 2916//46 2917//46 2918//46 +f 2900//45 2898//45 2897//45 +f 2915//45 2916//45 2918//45 +f 2900//45 2897//45 2896//45 +f 2914//45 2915//45 2918//45 +f 2896//982 2897//982 2906//982 2905//982 +f 2897//983 2898//983 2907//983 2906//983 +f 2898//984 2899//984 2908//984 2907//984 +f 2899//206 2900//206 2909//206 2908//206 +f 2900//985 2901//985 2910//985 2909//985 +f 2901//986 2902//986 2911//986 2910//986 +f 2902//987 2903//987 2912//987 2911//987 +f 2903//596 2904//596 2913//596 2912//596 +f 2904//519 2896//519 2905//519 2913//519 +f 2905//988 2906//988 2915//988 2914//988 +f 2906//989 2907//989 2916//989 2915//989 +f 2907//990 2908//990 2917//990 2916//990 +f 2908//211 2909//211 2918//211 2917//211 +f 2909//991 2910//991 2919//991 2918//991 +f 2910//992 2911//992 2920//992 2919//992 +f 2911//993 2912//993 2921//993 2920//993 +f 2912//994 2913//994 2922//994 2921//994 +f 2913//527 2905//527 2914//527 2922//527 +f 2923//45 2931//45 2930//45 +f 2948//46 2949//46 2941//46 +f 2923//45 2930//45 2929//45 +f 2947//46 2948//46 2941//46 +f 2923//45 2929//45 2928//45 +f 2946//46 2947//46 2941//46 +f 2923//45 2928//45 2927//45 +f 2945//46 2946//46 2941//46 +f 2927//45 2926//45 2925//45 +f 2943//46 2944//46 2945//46 +f 2927//46 2925//46 2924//46 +f 2942//46 2943//46 2945//46 +f 2927//46 2924//46 2923//46 +f 2941//46 2942//46 2945//46 +f 2923//995 2924//995 2933//995 2932//995 +f 2924//996 2925//996 2934//996 2933//996 +f 2925//997 2926//997 2935//997 2934//997 +f 2926//216 2927//216 2936//216 2935//216 +f 2927//998 2928//998 2937//998 2936//998 +f 2928//999 2929//999 2938//999 2937//999 +f 2929//1000 2930//1000 2939//1000 2938//1000 +f 2930//1001 2931//1001 2940//1001 2939//1001 +f 2931//535 2923//535 2932//535 2940//535 +f 2932//982 2933//982 2942//982 2941//982 +f 2933//983 2934//983 2943//983 2942//983 +f 2934//984 2935//984 2944//984 2943//984 +f 2935//206 2936//206 2945//206 2944//206 +f 2936//985 2937//985 2946//985 2945//985 +f 2937//986 2938//986 2947//986 2946//986 +f 2938//987 2939//987 2948//987 2947//987 +f 2939//596 2940//596 2949//596 2948//596 +f 2940//519 2932//519 2941//519 2949//519 +o hornR +usemtl frame +f 2950//1002 2951//1002 2956//1002 2955//1002 +f 2951//1003 2952//1003 2957//1003 2956//1003 +f 2952//1004 2953//1004 2958//1004 2957//1004 +f 2953//1005 2954//1005 2959//1005 2958//1005 +f 2954//1006 2950//1006 2955//1006 2959//1006 +f 2955//1007 2956//1007 2961//1007 2960//1007 +f 2956//1008 2957//1008 2962//1008 2961//1008 +f 2957//1009 2958//1009 2963//1009 2962//1009 +f 2958//1010 2959//1010 2964//1010 2963//1010 +f 2959//1011 2955//1011 2960//1011 2964//1011 +f 2950//45 2954//45 2953//45 +f 2963//46 2964//46 2960//46 +f 2950//45 2953//45 2952//45 +f 2962//46 2963//46 2960//46 +f 2952//45 2951//45 2950//45 +f 2960//46 2961//46 2962//46 +usemtl dark +f 2965//45 2970//45 2969//45 +f 2981//46 2982//46 2977//46 +f 2965//45 2969//45 2968//45 +f 2980//46 2981//46 2977//46 +f 2965//45 2968//45 2967//45 +f 2979//46 2980//46 2977//46 +f 2967//45 2966//45 2965//45 +f 2977//46 2978//46 2979//46 +f 2965//1012 2966//1012 2972//1012 2971//1012 +f 2966//1013 2967//1013 2973//1013 2972//1013 +f 2967//1014 2968//1014 2974//1014 2973//1014 +f 2968//1015 2969//1015 2975//1015 2974//1015 +f 2969//1016 2970//1016 2976//1016 2975//1016 +f 2970//58 2965//58 2971//58 2976//58 +f 2971//1017 2972//1017 2978//1017 2977//1017 +f 2972//1018 2973//1018 2979//1018 2978//1018 +f 2973//1019 2974//1019 2980//1019 2979//1019 +f 2974//1020 2975//1020 2981//1020 2980//1020 +f 2975//1021 2976//1021 2982//1021 2981//1021 +f 2976//149 2971//149 2977//149 2982//149 +f 2983//45 2988//45 2987//45 +f 2999//46 3000//46 2995//46 +f 2983//45 2987//45 2986//45 +f 2998//46 2999//46 2995//46 +f 2983//45 2986//45 2985//45 +f 2997//46 2998//46 2995//46 +f 2985//45 2984//45 2983//45 +f 2995//46 2996//46 2997//46 +f 2983//1022 2984//1022 2990//1022 2989//1022 +f 2984//1023 2985//1023 2991//1023 2990//1023 +f 2985//1024 2986//1024 2992//1024 2991//1024 +f 2986//1025 2987//1025 2993//1025 2992//1025 +f 2987//1026 2988//1026 2994//1026 2993//1026 +f 2988//159 2983//159 2989//159 2994//159 +f 2989//1012 2990//1012 2996//1012 2995//1012 +f 2990//1013 2991//1013 2997//1013 2996//1013 +f 2991//1014 2992//1014 2998//1014 2997//1014 +f 2992//1015 2993//1015 2999//1015 2998//1015 +f 2993//1016 2994//1016 3000//1016 2999//1016 +f 2994//58 2989//58 2995//58 3000//58 +o crest +usemtl frame +f 3001//1027 3002//1027 3007//1027 3006//1027 +f 3002//1028 3003//1028 3008//1028 3007//1028 +f 3003//1029 3004//1029 3009//1029 3008//1029 +f 3004//1030 3005//1030 3010//1030 3009//1030 +f 3005//1031 3001//1031 3006//1031 3010//1031 +f 3006//1032 3007//1032 3012//1032 3011//1032 +f 3007//1033 3008//1033 3013//1033 3012//1033 +f 3008//1034 3009//1034 3014//1034 3013//1034 +f 3009//1035 3010//1035 3015//1035 3014//1035 +f 3010//1036 3006//1036 3011//1036 3015//1036 +f 3001//45 3005//45 3004//45 +f 3014//46 3015//46 3011//46 +f 3001//45 3004//45 3003//45 +f 3013//46 3014//46 3011//46 +f 3003//45 3002//45 3001//45 +f 3011//46 3012//46 3013//46 +usemtl champagne +f 3016//45 3023//45 3022//45 +f 3038//46 3039//46 3032//46 +f 3016//45 3022//45 3021//45 +f 3037//46 3038//46 3032//46 +f 3016//45 3021//45 3020//45 +f 3036//46 3037//46 3032//46 +f 3016//45 3020//45 3019//45 +f 3035//46 3036//46 3032//46 +f 3016//45 3019//45 3018//45 +f 3034//46 3035//46 3032//46 +f 3018//45 3017//45 3016//45 +f 3032//46 3033//46 3034//46 +f 3016//1037 3017//1037 3025//1037 3024//1037 +f 3017//1038 3018//1038 3026//1038 3025//1038 +f 3018//47 3019//47 3027//47 3026//47 +f 3019//1039 3020//1039 3028//1039 3027//1039 +f 3020//1014 3021//1014 3029//1014 3028//1014 +f 3021//1040 3022//1040 3030//1040 3029//1040 +f 3022//1041 3023//1041 3031//1041 3030//1041 +f 3023//58 3016//58 3024//58 3031//58 +f 3024//1042 3025//1042 3033//1042 3032//1042 +f 3025//1043 3026//1043 3034//1043 3033//1043 +f 3026//1044 3027//1044 3035//1044 3034//1044 +f 3027//1045 3028//1045 3036//1045 3035//1045 +f 3028//1046 3029//1046 3037//1046 3036//1046 +f 3029//744 3030//744 3038//744 3037//744 +f 3030//1047 3031//1047 3039//1047 3038//1047 +f 3031//185 3024//185 3032//185 3039//185 +f 3040//45 3047//45 3046//45 +f 3062//46 3063//46 3056//46 +f 3040//45 3046//45 3045//45 +f 3061//46 3062//46 3056//46 +f 3040//45 3045//45 3044//45 +f 3060//46 3061//46 3056//46 +f 3040//45 3044//45 3043//45 +f 3059//46 3060//46 3056//46 +f 3040//45 3043//45 3042//45 +f 3058//46 3059//46 3056//46 +f 3042//45 3041//45 3040//45 +f 3056//46 3057//46 3058//46 +f 3040//1048 3041//1048 3049//1048 3048//1048 +f 3041//1049 3042//1049 3050//1049 3049//1049 +f 3042//1050 3043//1050 3051//1050 3050//1050 +f 3043//1051 3044//1051 3052//1051 3051//1051 +f 3044//1052 3045//1052 3053//1052 3052//1052 +f 3045//756 3046//756 3054//756 3053//756 +f 3046//1053 3047//1053 3055//1053 3054//1053 +f 3047//195 3040//195 3048//195 3055//195 +f 3048//1037 3049//1037 3057//1037 3056//1037 +f 3049//1038 3050//1038 3058//1038 3057//1038 +f 3050//47 3051//47 3059//47 3058//47 +f 3051//1039 3052//1039 3060//1039 3059//1039 +f 3052//1014 3053//1014 3061//1014 3060//1014 +f 3053//1040 3054//1040 3062//1040 3061//1040 +f 3054//1041 3055//1041 3063//1041 3062//1041 +f 3055//58 3048//58 3056//58 3063//58 +o wingL +usemtl frame +f 3064//1054 3065//1054 3079//1054 3078//1054 +f 3065//1055 3066//1055 3080//1055 3079//1055 +f 3066//1056 3067//1056 3081//1056 3080//1056 +f 3067//1057 3068//1057 3082//1057 3081//1057 +f 3068//1058 3069//1058 3083//1058 3082//1058 +f 3069//1059 3070//1059 3084//1059 3083//1059 +f 3070//1060 3071//1060 3085//1060 3084//1060 +f 3071//1061 3072//1061 3086//1061 3085//1061 +f 3072//1062 3073//1062 3087//1062 3086//1062 +f 3073//1063 3074//1063 3088//1063 3087//1063 +f 3074//1064 3075//1064 3089//1064 3088//1064 +f 3075//1065 3076//1065 3090//1065 3089//1065 +f 3076//1066 3077//1066 3091//1066 3090//1066 +f 3077//1067 3064//1067 3078//1067 3091//1067 +f 3078//1068 3079//1068 3093//1068 3092//1068 +f 3079//1069 3080//1069 3094//1069 3093//1069 +f 3080//1070 3081//1070 3095//1070 3094//1070 +f 3081//1071 3082//1071 3096//1071 3095//1071 +f 3082//1072 3083//1072 3097//1072 3096//1072 +f 3083//1073 3084//1073 3098//1073 3097//1073 +f 3084//1074 3085//1074 3099//1074 3098//1074 +f 3085//1075 3086//1075 3100//1075 3099//1075 +f 3086//1076 3087//1076 3101//1076 3100//1076 +f 3087//1077 3088//1077 3102//1077 3101//1077 +f 3088//1078 3089//1078 3103//1078 3102//1078 +f 3089//1079 3090//1079 3104//1079 3103//1079 +f 3090//1080 3091//1080 3105//1080 3104//1080 +f 3091//1081 3078//1081 3092//1081 3105//1081 +f 3064//1082 3077//1082 3076//1082 +f 3104//1083 3105//1083 3092//1083 +f 3064//1084 3076//1084 3075//1084 +f 3103//1085 3104//1085 3092//1085 +f 3064//1086 3075//1086 3074//1086 +f 3102//1087 3103//1087 3092//1087 +f 3064//1086 3074//1086 3073//1086 +f 3101//1087 3102//1087 3092//1087 +f 3064//1086 3073//1086 3072//1086 +f 3100//1087 3101//1087 3092//1087 +f 3064//1086 3072//1086 3071//1086 +f 3099//1087 3100//1087 3092//1087 +f 3071//1086 3070//1086 3069//1086 +f 3097//1087 3098//1087 3099//1087 +f 3071//1086 3069//1086 3068//1086 +f 3096//1083 3097//1083 3099//1083 +f 3071//1086 3068//1086 3067//1086 +f 3095//1087 3096//1087 3099//1087 +f 3071//1086 3067//1086 3066//1086 +f 3094//1087 3095//1087 3099//1087 +f 3064//1086 3071//1086 3066//1086 +f 3094//1087 3099//1087 3092//1087 +f 3066//1088 3065//1088 3064//1088 +f 3092//1089 3093//1089 3094//1089 +usemtl dark +f 3106//1086 3118//1086 3117//1086 +f 3143//1090 3144//1090 3132//1090 +f 3106//1091 3117//1091 3116//1091 +f 3142//1092 3143//1092 3132//1092 +f 3106//1086 3116//1086 3115//1086 +f 3141//1093 3142//1093 3132//1093 +f 3106//1086 3115//1086 3114//1086 +f 3140//1087 3141//1087 3132//1087 +f 3106//1086 3114//1086 3113//1086 +f 3139//1094 3140//1094 3132//1094 +f 3106//1095 3113//1095 3112//1095 +f 3138//1096 3139//1096 3132//1096 +f 3106//1086 3112//1086 3111//1086 +f 3137//1097 3138//1097 3132//1097 +f 3111//1082 3110//1082 3109//1082 +f 3135//1098 3136//1098 3137//1098 +f 3111//1086 3109//1086 3108//1086 +f 3134//1087 3135//1087 3137//1087 +f 3106//1086 3111//1086 3108//1086 +f 3134//1087 3137//1087 3132//1087 +f 3108//1099 3107//1099 3106//1099 +f 3132//1100 3133//1100 3134//1100 +f 3106//1101 3107//1101 3120//1101 3119//1101 +f 3107//1102 3108//1102 3121//1102 3120//1102 +f 3108//1103 3109//1103 3122//1103 3121//1103 +f 3109//1104 3110//1104 3123//1104 3122//1104 +f 3110//1105 3111//1105 3124//1105 3123//1105 +f 3111//1106 3112//1106 3125//1106 3124//1106 +f 3112//87 3113//87 3126//87 3125//87 +f 3113//1107 3114//1107 3127//1107 3126//1107 +f 3114//1108 3115//1108 3128//1108 3127//1108 +f 3115//1109 3116//1109 3129//1109 3128//1109 +f 3116//1110 3117//1110 3130//1110 3129//1110 +f 3117//1111 3118//1111 3131//1111 3130//1111 +f 3118//1112 3106//1112 3119//1112 3131//1112 +f 3119//1113 3120//1113 3133//1113 3132//1113 +f 3120//1114 3121//1114 3134//1114 3133//1114 +f 3121//1115 3122//1115 3135//1115 3134//1115 +f 3122//1116 3123//1116 3136//1116 3135//1116 +f 3123//1117 3124//1117 3137//1117 3136//1117 +f 3124//1118 3125//1118 3138//1118 3137//1118 +f 3125//1119 3126//1119 3139//1119 3138//1119 +f 3126//1120 3127//1120 3140//1120 3139//1120 +f 3127//1121 3128//1121 3141//1121 3140//1121 +f 3128//1122 3129//1122 3142//1122 3141//1122 +f 3129//1123 3130//1123 3143//1123 3142//1123 +f 3130//1124 3131//1124 3144//1124 3143//1124 +f 3131//1125 3119//1125 3132//1125 3144//1125 +f 3145//1126 3157//1126 3156//1126 +f 3182//1087 3183//1087 3171//1087 +f 3145//1127 3156//1127 3155//1127 +f 3181//1128 3182//1128 3171//1128 +f 3145//1129 3155//1129 3154//1129 +f 3180//1094 3181//1094 3171//1094 +f 3145//1086 3154//1086 3153//1086 +f 3179//1087 3180//1087 3171//1087 +f 3145//1086 3153//1086 3152//1086 +f 3178//1087 3179//1087 3171//1087 +f 3145//1130 3152//1130 3151//1130 +f 3177//1131 3178//1131 3171//1131 +f 3145//1132 3151//1132 3150//1132 +f 3176//1087 3177//1087 3171//1087 +f 3150//1086 3149//1086 3148//1086 +f 3174//1087 3175//1087 3176//1087 +f 3150//1086 3148//1086 3147//1086 +f 3173//1083 3174//1083 3176//1083 +f 3145//1086 3150//1086 3147//1086 +f 3173//1087 3176//1087 3171//1087 +f 3147//1133 3146//1133 3145//1133 +f 3171//1134 3172//1134 3173//1134 +f 3145//1135 3146//1135 3159//1135 3158//1135 +f 3146//1136 3147//1136 3160//1136 3159//1136 +f 3147//1137 3148//1137 3161//1137 3160//1137 +f 3148//1138 3149//1138 3162//1138 3161//1138 +f 3149//1139 3150//1139 3163//1139 3162//1139 +f 3150//1140 3151//1140 3164//1140 3163//1140 +f 3151//1141 3152//1141 3165//1141 3164//1141 +f 3152//1142 3153//1142 3166//1142 3165//1142 +f 3153//1143 3154//1143 3167//1143 3166//1143 +f 3154//1144 3155//1144 3168//1144 3167//1144 +f 3155//1145 3156//1145 3169//1145 3168//1145 +f 3156//1146 3157//1146 3170//1146 3169//1146 +f 3157//1147 3145//1147 3158//1147 3170//1147 +f 3158//1148 3159//1148 3172//1148 3171//1148 +f 3159//1149 3160//1149 3173//1149 3172//1149 +f 3160//1150 3161//1150 3174//1150 3173//1150 +f 3161//1151 3162//1151 3175//1151 3174//1151 +f 3162//1152 3163//1152 3176//1152 3175//1152 +f 3163//1106 3164//1106 3177//1106 3176//1106 +f 3164//87 3165//87 3178//87 3177//87 +f 3165//1153 3166//1153 3179//1153 3178//1153 +f 3166//1108 3167//1108 3180//1108 3179//1108 +f 3167//1154 3168//1154 3181//1154 3180//1154 +f 3168//1155 3169//1155 3182//1155 3181//1155 +f 3169//1156 3170//1156 3183//1156 3182//1156 +f 3170//1157 3158//1157 3171//1157 3183//1157 +usemtl glass +f 3184//1082 3194//1082 3193//1082 +f 3215//1158 3216//1158 3206//1158 +f 3193//1086 3192//1086 3191//1086 +f 3213//1087 3214//1087 3215//1087 +f 3184//1159 3193//1159 3191//1159 +f 3213//1094 3215//1094 3206//1094 +f 3184//1086 3191//1086 3190//1086 +f 3212//1083 3213//1083 3206//1083 +f 3184//1160 3190//1160 3189//1160 +f 3211//1161 3212//1161 3206//1161 +f 3184//1082 3189//1082 3188//1082 +f 3210//1162 3211//1162 3206//1162 +f 3184//1086 3188//1086 3187//1086 +f 3209//1087 3210//1087 3206//1087 +f 3184//1163 3187//1163 3186//1163 +f 3208//1134 3209//1134 3206//1134 +f 3186//1129 3185//1129 3184//1129 +f 3206//1164 3207//1164 3208//1164 +f 3184//1165 3185//1165 3196//1165 3195//1165 +f 3185//1166 3186//1166 3197//1166 3196//1166 +f 3186//1167 3187//1167 3198//1167 3197//1167 +f 3187//1168 3188//1168 3199//1168 3198//1168 +f 3188//1169 3189//1169 3200//1169 3199//1169 +f 3189//1170 3190//1170 3201//1170 3200//1170 +f 3190//1171 3191//1171 3202//1171 3201//1171 +f 3191//1172 3192//1172 3203//1172 3202//1172 +f 3192//1173 3193//1173 3204//1173 3203//1173 +f 3193//1174 3194//1174 3205//1174 3204//1174 +f 3194//1175 3184//1175 3195//1175 3205//1175 +f 3195//1176 3196//1176 3207//1176 3206//1176 +f 3196//1177 3197//1177 3208//1177 3207//1177 +f 3197//1178 3198//1178 3209//1178 3208//1178 +f 3198//1179 3199//1179 3210//1179 3209//1179 +f 3199//1180 3200//1180 3211//1180 3210//1180 +f 3200//1181 3201//1181 3212//1181 3211//1181 +f 3201//1182 3202//1182 3213//1182 3212//1182 +f 3202//1183 3203//1183 3214//1183 3213//1183 +f 3203//1184 3204//1184 3215//1184 3214//1184 +f 3204//1185 3205//1185 3216//1185 3215//1185 +f 3205//1186 3195//1186 3206//1186 3216//1186 +f 3217//1187 3227//1187 3226//1187 +f 3248//1087 3249//1087 3239//1087 +f 3226//1082 3225//1082 3224//1082 +f 3246//1087 3247//1087 3248//1087 +f 3217//1188 3226//1188 3224//1188 +f 3246//1189 3248//1189 3239//1189 +f 3217//1190 3224//1190 3223//1190 +f 3245//1087 3246//1087 3239//1087 +f 3217//1190 3223//1190 3222//1190 +f 3244//1191 3245//1191 3239//1191 +f 3217//1192 3222//1192 3221//1192 +f 3243//1085 3244//1085 3239//1085 +f 3217//1086 3221//1086 3220//1086 +f 3242//1087 3243//1087 3239//1087 +f 3217//1130 3220//1130 3219//1130 +f 3241//1193 3242//1193 3239//1193 +f 3219//1194 3218//1194 3217//1194 +f 3239//1195 3240//1195 3241//1195 +f 3217//1196 3218//1196 3229//1196 3228//1196 +f 3218//1197 3219//1197 3230//1197 3229//1197 +f 3219//1198 3220//1198 3231//1198 3230//1198 +f 3220//1199 3221//1199 3232//1199 3231//1199 +f 3221//1200 3222//1200 3233//1200 3232//1200 +f 3222//1201 3223//1201 3234//1201 3233//1201 +f 3223//1202 3224//1202 3235//1202 3234//1202 +f 3224//1203 3225//1203 3236//1203 3235//1203 +f 3225//1204 3226//1204 3237//1204 3236//1204 +f 3226//1205 3227//1205 3238//1205 3237//1205 +f 3227//1137 3217//1137 3228//1137 3238//1137 +f 3228//1206 3229//1206 3240//1206 3239//1206 +f 3229//1207 3230//1207 3241//1207 3240//1207 +f 3230//1208 3231//1208 3242//1208 3241//1208 +f 3231//1209 3232//1209 3243//1209 3242//1209 +f 3232//1210 3233//1210 3244//1210 3243//1210 +f 3233//1211 3234//1211 3245//1211 3244//1211 +f 3234//1212 3235//1212 3246//1212 3245//1212 +f 3235//1213 3236//1213 3247//1213 3246//1213 +f 3236//1173 3237//1173 3248//1173 3247//1173 +f 3237//1214 3238//1214 3249//1214 3248//1214 +f 3238//1175 3228//1175 3239//1175 3249//1175 +f 3250//1086 3262//1086 3261//1086 +f 3287//1087 3288//1087 3276//1087 +f 3250//1086 3261//1086 3260//1086 +f 3286//1087 3287//1087 3276//1087 +f 3250//1132 3260//1132 3259//1132 +f 3285//1087 3286//1087 3276//1087 +f 3250//1129 3259//1129 3258//1129 +f 3284//1083 3285//1083 3276//1083 +f 3250//1086 3258//1086 3257//1086 +f 3283//1087 3284//1087 3276//1087 +f 3250//1086 3257//1086 3256//1086 +f 3282//1087 3283//1087 3276//1087 +f 3250//1086 3256//1086 3255//1086 +f 3281//1215 3282//1215 3276//1215 +f 3250//1216 3255//1216 3254//1216 +f 3280//1217 3281//1217 3276//1217 +f 3250//1218 3254//1218 3253//1218 +f 3279//1215 3280//1215 3276//1215 +f 3253//1082 3252//1082 3251//1082 +f 3277//1098 3278//1098 3279//1098 +f 3253//1219 3251//1219 3250//1219 +f 3276//1220 3277//1220 3279//1220 +f 3250//1221 3251//1221 3264//1221 3263//1221 +f 3251//95 3252//95 3265//95 3264//95 +f 3252//1222 3253//1222 3266//1222 3265//1222 +f 3253//1223 3254//1223 3267//1223 3266//1223 +f 3254//1224 3255//1224 3268//1224 3267//1224 +f 3255//1225 3256//1225 3269//1225 3268//1225 +f 3256//1226 3257//1226 3270//1226 3269//1226 +f 3257//1227 3258//1227 3271//1227 3270//1227 +f 3258//1228 3259//1228 3272//1228 3271//1228 +f 3259//1229 3260//1229 3273//1229 3272//1229 +f 3260//1230 3261//1230 3274//1230 3273//1230 +f 3261//1231 3262//1231 3275//1231 3274//1231 +f 3262//1232 3250//1232 3263//1232 3275//1232 +f 3263//1233 3264//1233 3277//1233 3276//1233 +f 3264//1234 3265//1234 3278//1234 3277//1234 +f 3265//1235 3266//1235 3279//1235 3278//1235 +f 3266//1236 3267//1236 3280//1236 3279//1236 +f 3267//1237 3268//1237 3281//1237 3280//1237 +f 3268//1238 3269//1238 3282//1238 3281//1238 +f 3269//1239 3270//1239 3283//1239 3282//1239 +f 3270//1240 3271//1240 3284//1240 3283//1240 +f 3271//1241 3272//1241 3285//1241 3284//1241 +f 3272//1242 3273//1242 3286//1242 3285//1242 +f 3273//1243 3274//1243 3287//1243 3286//1243 +f 3274//1244 3275//1244 3288//1244 3287//1244 +f 3275//1245 3263//1245 3276//1245 3288//1245 +f 3289//1086 3301//1086 3300//1086 +f 3326//1087 3327//1087 3315//1087 +f 3289//1086 3300//1086 3299//1086 +f 3325//1087 3326//1087 3315//1087 +f 3289//1246 3299//1246 3298//1246 +f 3324//1093 3325//1093 3315//1093 +f 3289//1132 3298//1132 3297//1132 +f 3323//1087 3324//1087 3315//1087 +f 3289//1086 3297//1086 3296//1086 +f 3322//1087 3323//1087 3315//1087 +f 3289//1086 3296//1086 3295//1086 +f 3321//1087 3322//1087 3315//1087 +f 3289//1247 3295//1247 3294//1247 +f 3320//1098 3321//1098 3315//1098 +f 3289//1248 3294//1248 3293//1248 +f 3319//1249 3320//1249 3315//1249 +f 3289//1250 3293//1250 3292//1250 +f 3318//1087 3319//1087 3315//1087 +f 3292//1251 3291//1251 3290//1251 +f 3316//1252 3317//1252 3318//1252 +f 3292//1253 3290//1253 3289//1253 +f 3315//1254 3316//1254 3318//1254 +f 3289//1255 3290//1255 3303//1255 3302//1255 +f 3290//1256 3291//1256 3304//1256 3303//1256 +f 3291//1257 3292//1257 3305//1257 3304//1257 +f 3292//1258 3293//1258 3306//1258 3305//1258 +f 3293//1259 3294//1259 3307//1259 3306//1259 +f 3294//1260 3295//1260 3308//1260 3307//1260 +f 3295//1261 3296//1261 3309//1261 3308//1261 +f 3296//1262 3297//1262 3310//1262 3309//1262 +f 3297//1263 3298//1263 3311//1263 3310//1263 +f 3298//1200 3299//1200 3312//1200 3311//1200 +f 3299//1264 3300//1264 3313//1264 3312//1264 +f 3300//1265 3301//1265 3314//1265 3313//1265 +f 3301//1266 3289//1266 3302//1266 3314//1266 +f 3302//1267 3303//1267 3316//1267 3315//1267 +f 3303//95 3304//95 3317//95 3316//95 +f 3304//1268 3305//1268 3318//1268 3317//1268 +f 3305//1269 3306//1269 3319//1269 3318//1269 +f 3306//1270 3307//1270 3320//1270 3319//1270 +f 3307//1271 3308//1271 3321//1271 3320//1271 +f 3308//1272 3309//1272 3322//1272 3321//1272 +f 3309//1227 3310//1227 3323//1227 3322//1227 +f 3310//1273 3311//1273 3324//1273 3323//1273 +f 3311//1274 3312//1274 3325//1274 3324//1274 +f 3312//1230 3313//1230 3326//1230 3325//1230 +f 3313//1275 3314//1275 3327//1275 3326//1275 +f 3314//1276 3302//1276 3315//1276 3327//1276 +o wingR +usemtl frame +f 3328//1277 3329//1277 3343//1277 3342//1277 +f 3329//1278 3330//1278 3344//1278 3343//1278 +f 3330//1279 3331//1279 3345//1279 3344//1279 +f 3331//1280 3332//1280 3346//1280 3345//1280 +f 3332//1281 3333//1281 3347//1281 3346//1281 +f 3333//1282 3334//1282 3348//1282 3347//1282 +f 3334//1283 3335//1283 3349//1283 3348//1283 +f 3335//1284 3336//1284 3350//1284 3349//1284 +f 3336//1285 3337//1285 3351//1285 3350//1285 +f 3337//1286 3338//1286 3352//1286 3351//1286 +f 3338//1287 3339//1287 3353//1287 3352//1287 +f 3339//1288 3340//1288 3354//1288 3353//1288 +f 3340//1289 3341//1289 3355//1289 3354//1289 +f 3341//1290 3328//1290 3342//1290 3355//1290 +f 3342//1291 3343//1291 3357//1291 3356//1291 +f 3343//1292 3344//1292 3358//1292 3357//1292 +f 3344//1293 3345//1293 3359//1293 3358//1293 +f 3345//1294 3346//1294 3360//1294 3359//1294 +f 3346//1295 3347//1295 3361//1295 3360//1295 +f 3347//1296 3348//1296 3362//1296 3361//1296 +f 3348//1297 3349//1297 3363//1297 3362//1297 +f 3349//1298 3350//1298 3364//1298 3363//1298 +f 3350//1299 3351//1299 3365//1299 3364//1299 +f 3351//1300 3352//1300 3366//1300 3365//1300 +f 3352//1301 3353//1301 3367//1301 3366//1301 +f 3353//1302 3354//1302 3368//1302 3367//1302 +f 3354//1303 3355//1303 3369//1303 3368//1303 +f 3355//1304 3342//1304 3356//1304 3369//1304 +f 3328//1305 3341//1305 3340//1305 +f 3368//1306 3369//1306 3356//1306 +f 3328//1307 3340//1307 3339//1307 +f 3367//1308 3368//1308 3356//1308 +f 3328//1309 3339//1309 3338//1309 +f 3366//1310 3367//1310 3356//1310 +f 3328//1309 3338//1309 3337//1309 +f 3365//1310 3366//1310 3356//1310 +f 3328//1309 3337//1309 3336//1309 +f 3364//1310 3365//1310 3356//1310 +f 3328//1309 3336//1309 3335//1309 +f 3363//1310 3364//1310 3356//1310 +f 3335//1309 3334//1309 3333//1309 +f 3361//1310 3362//1310 3363//1310 +f 3335//1305 3333//1305 3332//1305 +f 3360//1310 3361//1310 3363//1310 +f 3335//1309 3332//1309 3331//1309 +f 3359//1310 3360//1310 3363//1310 +f 3335//1309 3331//1309 3330//1309 +f 3358//1310 3359//1310 3363//1310 +f 3328//1309 3335//1309 3330//1309 +f 3358//1310 3363//1310 3356//1310 +f 3330//1311 3329//1311 3328//1311 +f 3356//1312 3357//1312 3358//1312 +usemtl dark +f 3370//1309 3382//1309 3381//1309 +f 3407//1313 3408//1313 3396//1313 +f 3370//1314 3381//1314 3380//1314 +f 3406//1315 3407//1315 3396//1315 +f 3370//1316 3380//1316 3379//1316 +f 3405//1317 3406//1317 3396//1317 +f 3370//1309 3379//1309 3378//1309 +f 3404//1310 3405//1310 3396//1310 +f 3370//1309 3378//1309 3377//1309 +f 3403//1310 3404//1310 3396//1310 +f 3370//1318 3377//1318 3376//1318 +f 3402//1319 3403//1319 3396//1319 +f 3370//1309 3376//1309 3375//1309 +f 3401//1320 3402//1320 3396//1320 +f 3375//1309 3374//1309 3373//1309 +f 3399//1310 3400//1310 3401//1310 +f 3375//1305 3373//1305 3372//1305 +f 3398//1310 3399//1310 3401//1310 +f 3370//1309 3375//1309 3372//1309 +f 3398//1310 3401//1310 3396//1310 +f 3372//1321 3371//1321 3370//1321 +f 3396//1322 3397//1322 3398//1322 +f 3370//1323 3371//1323 3384//1323 3383//1323 +f 3371//1324 3372//1324 3385//1324 3384//1324 +f 3372//1325 3373//1325 3386//1325 3385//1325 +f 3373//1326 3374//1326 3387//1326 3386//1326 +f 3374//1327 3375//1327 3388//1327 3387//1327 +f 3375//1328 3376//1328 3389//1328 3388//1328 +f 3376//87 3377//87 3390//87 3389//87 +f 3377//1329 3378//1329 3391//1329 3390//1329 +f 3378//1330 3379//1330 3392//1330 3391//1330 +f 3379//1331 3380//1331 3393//1331 3392//1331 +f 3380//1332 3381//1332 3394//1332 3393//1332 +f 3381//1333 3382//1333 3395//1333 3394//1333 +f 3382//1334 3370//1334 3383//1334 3395//1334 +f 3383//1335 3384//1335 3397//1335 3396//1335 +f 3384//1336 3385//1336 3398//1336 3397//1336 +f 3385//1337 3386//1337 3399//1337 3398//1337 +f 3386//1338 3387//1338 3400//1338 3399//1338 +f 3387//1339 3388//1339 3401//1339 3400//1339 +f 3388//1340 3389//1340 3402//1340 3401//1340 +f 3389//1341 3390//1341 3403//1341 3402//1341 +f 3390//1342 3391//1342 3404//1342 3403//1342 +f 3391//1343 3392//1343 3405//1343 3404//1343 +f 3392//1344 3393//1344 3406//1344 3405//1344 +f 3393//1345 3394//1345 3407//1345 3406//1345 +f 3394//1346 3395//1346 3408//1346 3407//1346 +f 3395//1347 3383//1347 3396//1347 3408//1347 +f 3409//1348 3421//1348 3420//1348 +f 3446//1310 3447//1310 3435//1310 +f 3409//1349 3420//1349 3419//1349 +f 3445//1350 3446//1350 3435//1350 +f 3409//1351 3419//1351 3418//1351 +f 3444//1310 3445//1310 3435//1310 +f 3409//1309 3418//1309 3417//1309 +f 3443//1310 3444//1310 3435//1310 +f 3409//1316 3417//1316 3416//1316 +f 3442//1310 3443//1310 3435//1310 +f 3409//1352 3416//1352 3415//1352 +f 3441//1353 3442//1353 3435//1353 +f 3409//1354 3415//1354 3414//1354 +f 3440//1310 3441//1310 3435//1310 +f 3414//1355 3413//1355 3412//1355 +f 3438//1306 3439//1306 3440//1306 +f 3414//1309 3412//1309 3411//1309 +f 3437//1310 3438//1310 3440//1310 +f 3409//1309 3414//1309 3411//1309 +f 3437//1310 3440//1310 3435//1310 +f 3411//1356 3410//1356 3409//1356 +f 3435//1357 3436//1357 3437//1357 +f 3409//1358 3410//1358 3423//1358 3422//1358 +f 3410//1359 3411//1359 3424//1359 3423//1359 +f 3411//1360 3412//1360 3425//1360 3424//1360 +f 3412//1361 3413//1361 3426//1361 3425//1361 +f 3413//1362 3414//1362 3427//1362 3426//1362 +f 3414//1363 3415//1363 3428//1363 3427//1363 +f 3415//1364 3416//1364 3429//1364 3428//1364 +f 3416//1365 3417//1365 3430//1365 3429//1365 +f 3417//1366 3418//1366 3431//1366 3430//1366 +f 3418//1367 3419//1367 3432//1367 3431//1367 +f 3419//1368 3420//1368 3433//1368 3432//1368 +f 3420//1369 3421//1369 3434//1369 3433//1369 +f 3421//1370 3409//1370 3422//1370 3434//1370 +f 3422//1371 3423//1371 3436//1371 3435//1371 +f 3423//1372 3424//1372 3437//1372 3436//1372 +f 3424//1373 3425//1373 3438//1373 3437//1373 +f 3425//1374 3426//1374 3439//1374 3438//1374 +f 3426//1375 3427//1375 3440//1375 3439//1375 +f 3427//1328 3428//1328 3441//1328 3440//1328 +f 3428//87 3429//87 3442//87 3441//87 +f 3429//1376 3430//1376 3443//1376 3442//1376 +f 3430//1330 3431//1330 3444//1330 3443//1330 +f 3431//1377 3432//1377 3445//1377 3444//1377 +f 3432//1378 3433//1378 3446//1378 3445//1378 +f 3433//1379 3434//1379 3447//1379 3446//1379 +f 3434//1380 3422//1380 3435//1380 3447//1380 +usemtl glass +f 3448//1309 3458//1309 3457//1309 +f 3479//1381 3480//1381 3470//1381 +f 3457//1309 3456//1309 3455//1309 +f 3477//1306 3478//1306 3479//1306 +f 3448//1382 3457//1382 3455//1382 +f 3477//1383 3479//1383 3470//1383 +f 3448//1309 3455//1309 3454//1309 +f 3476//1384 3477//1384 3470//1384 +f 3448//1385 3454//1385 3453//1385 +f 3475//1384 3476//1384 3470//1384 +f 3448//1307 3453//1307 3452//1307 +f 3474//1386 3475//1386 3470//1386 +f 3448//1309 3452//1309 3451//1309 +f 3473//1310 3474//1310 3470//1310 +f 3448//1387 3451//1387 3450//1387 +f 3472//1319 3473//1319 3470//1319 +f 3450//1388 3449//1388 3448//1388 +f 3470//1389 3471//1389 3472//1389 +f 3448//1390 3449//1390 3460//1390 3459//1390 +f 3449//1391 3450//1391 3461//1391 3460//1391 +f 3450//1392 3451//1392 3462//1392 3461//1392 +f 3451//1393 3452//1393 3463//1393 3462//1393 +f 3452//1394 3453//1394 3464//1394 3463//1394 +f 3453//1395 3454//1395 3465//1395 3464//1395 +f 3454//1396 3455//1396 3466//1396 3465//1396 +f 3455//1397 3456//1397 3467//1397 3466//1397 +f 3456//1398 3457//1398 3468//1398 3467//1398 +f 3457//1399 3458//1399 3469//1399 3468//1399 +f 3458//1400 3448//1400 3459//1400 3469//1400 +f 3459//1401 3460//1401 3471//1401 3470//1401 +f 3460//1402 3461//1402 3472//1402 3471//1402 +f 3461//1403 3462//1403 3473//1403 3472//1403 +f 3462//1404 3463//1404 3474//1404 3473//1404 +f 3463//1405 3464//1405 3475//1405 3474//1405 +f 3464//1406 3465//1406 3476//1406 3475//1406 +f 3465//1407 3466//1407 3477//1407 3476//1407 +f 3466//1408 3467//1408 3478//1408 3477//1408 +f 3467//1409 3468//1409 3479//1409 3478//1409 +f 3468//1410 3469//1410 3480//1410 3479//1410 +f 3469//1337 3459//1337 3470//1337 3480//1337 +f 3481//1411 3491//1411 3490//1411 +f 3512//1306 3513//1306 3503//1306 +f 3490//1309 3489//1309 3488//1309 +f 3510//1310 3511//1310 3512//1310 +f 3481//1316 3490//1316 3488//1316 +f 3510//1412 3512//1412 3503//1412 +f 3481//1305 3488//1305 3487//1305 +f 3509//1310 3510//1310 3503//1310 +f 3481//1413 3487//1413 3486//1413 +f 3508//1414 3509//1414 3503//1414 +f 3481//1415 3486//1415 3485//1415 +f 3507//1306 3508//1306 3503//1306 +f 3481//1309 3485//1309 3484//1309 +f 3506//1310 3507//1310 3503//1310 +f 3481//1321 3484//1321 3483//1321 +f 3505//1416 3506//1416 3503//1416 +f 3483//1417 3482//1417 3481//1417 +f 3503//1317 3504//1317 3505//1317 +f 3481//1418 3482//1418 3493//1418 3492//1418 +f 3482//1419 3483//1419 3494//1419 3493//1419 +f 3483//1420 3484//1420 3495//1420 3494//1420 +f 3484//1421 3485//1421 3496//1421 3495//1421 +f 3485//1422 3486//1422 3497//1422 3496//1422 +f 3486//1423 3487//1423 3498//1423 3497//1423 +f 3487//1424 3488//1424 3499//1424 3498//1424 +f 3488//1425 3489//1425 3500//1425 3499//1425 +f 3489//1426 3490//1426 3501//1426 3500//1426 +f 3490//1427 3491//1427 3502//1427 3501//1427 +f 3491//1428 3481//1428 3492//1428 3502//1428 +f 3492//1429 3493//1429 3504//1429 3503//1429 +f 3493//1430 3494//1430 3505//1430 3504//1430 +f 3494//1431 3495//1431 3506//1431 3505//1431 +f 3495//1432 3496//1432 3507//1432 3506//1432 +f 3496//1433 3497//1433 3508//1433 3507//1433 +f 3497//1434 3498//1434 3509//1434 3508//1434 +f 3498//1435 3499//1435 3510//1435 3509//1435 +f 3499//1436 3500//1436 3511//1436 3510//1436 +f 3500//1398 3501//1398 3512//1398 3511//1398 +f 3501//1437 3502//1437 3513//1437 3512//1437 +f 3502//1400 3492//1400 3503//1400 3513//1400 +f 3514//1309 3526//1309 3525//1309 +f 3551//1310 3552//1310 3540//1310 +f 3514//1309 3525//1309 3524//1309 +f 3550//1310 3551//1310 3540//1310 +f 3514//1351 3524//1351 3523//1351 +f 3549//1438 3550//1438 3540//1438 +f 3514//1309 3523//1309 3522//1309 +f 3548//1320 3549//1320 3540//1320 +f 3514//1309 3522//1309 3521//1309 +f 3547//1310 3548//1310 3540//1310 +f 3514//1309 3521//1309 3520//1309 +f 3546//1310 3547//1310 3540//1310 +f 3514//1355 3520//1355 3519//1355 +f 3545//1439 3546//1439 3540//1439 +f 3514//1440 3519//1440 3518//1440 +f 3544//1441 3545//1441 3540//1441 +f 3514//1309 3518//1309 3517//1309 +f 3543//1442 3544//1442 3540//1442 +f 3517//1443 3516//1443 3515//1443 +f 3541//1444 3542//1444 3543//1444 +f 3517//1445 3515//1445 3514//1445 +f 3540//1446 3541//1446 3543//1446 +f 3514//1447 3515//1447 3528//1447 3527//1447 +f 3515//95 3516//95 3529//95 3528//95 +f 3516//1448 3517//1448 3530//1448 3529//1448 +f 3517//1449 3518//1449 3531//1449 3530//1449 +f 3518//1450 3519//1450 3532//1450 3531//1450 +f 3519//1451 3520//1451 3533//1451 3532//1451 +f 3520//1452 3521//1452 3534//1452 3533//1452 +f 3521//1453 3522//1453 3535//1453 3534//1453 +f 3522//1454 3523//1454 3536//1454 3535//1454 +f 3523//1455 3524//1455 3537//1455 3536//1455 +f 3524//1456 3525//1456 3538//1456 3537//1456 +f 3525//1457 3526//1457 3539//1457 3538//1457 +f 3526//1458 3514//1458 3527//1458 3539//1458 +f 3527//1459 3528//1459 3541//1459 3540//1459 +f 3528//1460 3529//1460 3542//1460 3541//1460 +f 3529//1461 3530//1461 3543//1461 3542//1461 +f 3530//1462 3531//1462 3544//1462 3543//1462 +f 3531//1463 3532//1463 3545//1463 3544//1463 +f 3532//1464 3533//1464 3546//1464 3545//1464 +f 3533//1465 3534//1465 3547//1465 3546//1465 +f 3534//1466 3535//1466 3548//1466 3547//1466 +f 3535//1467 3536//1467 3549//1467 3548//1467 +f 3536//1405 3537//1405 3550//1405 3549//1405 +f 3537//1468 3538//1468 3551//1468 3550//1468 +f 3538//1469 3539//1469 3552//1469 3551//1469 +f 3539//1470 3527//1470 3540//1470 3552//1470 +f 3553//1309 3565//1309 3564//1309 +f 3590//1310 3591//1310 3579//1310 +f 3553//1309 3564//1309 3563//1309 +f 3589//1310 3590//1310 3579//1310 +f 3553//1309 3563//1309 3562//1309 +f 3588//1320 3589//1320 3579//1320 +f 3553//1305 3562//1305 3561//1305 +f 3587//1317 3588//1317 3579//1317 +f 3553//1309 3561//1309 3560//1309 +f 3586//1310 3587//1310 3579//1310 +f 3553//1309 3560//1309 3559//1309 +f 3585//1310 3586//1310 3579//1310 +f 3553//1471 3559//1471 3558//1471 +f 3584//1310 3585//1310 3579//1310 +f 3553//1472 3558//1472 3557//1472 +f 3583//1473 3584//1473 3579//1473 +f 3553//1471 3557//1471 3556//1471 +f 3582//1474 3583//1474 3579//1474 +f 3556//1355 3555//1355 3554//1355 +f 3580//1306 3581//1306 3582//1306 +f 3556//1475 3554//1475 3553//1475 +f 3579//1476 3580//1476 3582//1476 +f 3553//1477 3554//1477 3567//1477 3566//1477 +f 3554//1478 3555//1478 3568//1478 3567//1478 +f 3555//1479 3556//1479 3569//1479 3568//1479 +f 3556//1480 3557//1480 3570//1480 3569//1480 +f 3557//1481 3558//1481 3571//1481 3570//1481 +f 3558//1482 3559//1482 3572//1482 3571//1482 +f 3559//1483 3560//1483 3573//1483 3572//1483 +f 3560//1484 3561//1484 3574//1484 3573//1484 +f 3561//1485 3562//1485 3575//1485 3574//1485 +f 3562//1486 3563//1486 3576//1486 3575//1486 +f 3563//1487 3564//1487 3577//1487 3576//1487 +f 3564//1488 3565//1488 3578//1488 3577//1488 +f 3565//1489 3553//1489 3566//1489 3578//1489 +f 3566//1490 3567//1490 3580//1490 3579//1490 +f 3567//95 3568//95 3581//95 3580//95 +f 3568//1491 3569//1491 3582//1491 3581//1491 +f 3569//1492 3570//1492 3583//1492 3582//1492 +f 3570//1493 3571//1493 3584//1493 3583//1493 +f 3571//1494 3572//1494 3585//1494 3584//1494 +f 3572//1495 3573//1495 3586//1495 3585//1495 +f 3573//1453 3574//1453 3587//1453 3586//1453 +f 3574//1496 3575//1496 3588//1496 3587//1496 +f 3575//1497 3576//1497 3589//1497 3588//1497 +f 3576//1456 3577//1456 3590//1456 3589//1456 +f 3577//1498 3578//1498 3591//1498 3590//1498 +f 3578//1499 3566//1499 3579//1499 3591//1499 diff --git a/assets/DragonRelief.glb b/assets/DragonRelief.glb new file mode 100644 index 000000000..c472d3eda --- /dev/null +++ b/assets/DragonRelief.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4605df2316cb9ef156274aa08b25fc965a705e45fc3385b457249ba6333490c0 +size 69820 diff --git a/assets/Plaque-champagne.stl b/assets/Plaque-champagne.stl new file mode 100644 index 000000000..4a685d088 --- /dev/null +++ b/assets/Plaque-champagne.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12df65596e863c20e961404f3d496931526b87ac18cd8217061c49b2e7397baf +size 51184 diff --git a/assets/Plaque-dark.stl b/assets/Plaque-dark.stl new file mode 100644 index 000000000..c31af71af --- /dev/null +++ b/assets/Plaque-dark.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc9dfd4ee3722a2acd67e9886f26412235005fc6dddaf1edd9d2e4617cd26995 +size 72784 diff --git a/assets/Plaque-frame.stl b/assets/Plaque-frame.stl new file mode 100644 index 000000000..d7adfc684 --- /dev/null +++ b/assets/Plaque-frame.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12c7cdad97ed954350660f3626e9bccb1b8f6a6b529d5e46866817b66c26e983 +size 53284 diff --git a/assets/Plaque-glass.stl b/assets/Plaque-glass.stl new file mode 100644 index 000000000..dc5837a8f --- /dev/null +++ b/assets/Plaque-glass.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33c3c05ceb34ffb9f5322f7202af6e37d4d2503297def34989213a416d3f8bd8 +size 36884 diff --git a/assets/Plaque-plate.stl b/assets/Plaque-plate.stl new file mode 100644 index 000000000..72b5bd80a --- /dev/null +++ b/assets/Plaque-plate.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2107f68d8ae6aab1b98cf25ba4a7335153ecf75c5271f87b54eb155de598f2ac +size 33884 diff --git a/assets/Plaque.mtl b/assets/Plaque.mtl new file mode 100644 index 000000000..e6ecc7a8e --- /dev/null +++ b/assets/Plaque.mtl @@ -0,0 +1,56 @@ +# Materials for Plaque.obj +newmtl dark +Ka 0.060 0.062 0.066 +Kd 0.300 0.310 0.330 +Ks 0.700 0.700 0.700 +Ns 180.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.35 + +newmtl champagne +Ka 0.144 0.128 0.108 +Kd 0.720 0.640 0.540 +Ks 0.850 0.800 0.700 +Ns 120.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.40 + +newmtl glass +Ka 0.176 0.184 0.180 +Kd 0.880 0.920 0.900 +Ks 0.950 0.950 0.950 +Ns 300.0 +d 0.55 +Ni 1.50 +illum 4 +Pm 0.00 +Pr 0.15 + +newmtl frame +Ka 0.024 0.022 0.020 +Kd 0.120 0.110 0.100 +Ks 0.300 0.300 0.300 +Ns 40.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.00 +Pr 0.70 + +newmtl plate +Ka 0.110 0.106 0.100 +Kd 0.550 0.530 0.500 +Ks 0.200 0.200 0.200 +Ns 30.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.00 +Pr 0.80 + diff --git a/assets/Plaque.obj b/assets/Plaque.obj new file mode 100644 index 000000000..b4fa11ad4 --- /dev/null +++ b/assets/Plaque.obj @@ -0,0 +1,7040 @@ +# Singe plaque: the logotype and the dragon in relief on one plate, built by util/plaqueModel.py +# Units: 0.01 per image pixel; plate in the XY plane resting on Y = 0, Z up (print orientation), centred on X = 0 +mtllib Plaque.mtl +v -0.01000 12.50000 -0.16000 +v 0.16000 12.24000 -0.16000 +v -0.03000 12.05000 -0.16000 +v -0.85000 12.39000 -0.16000 +v -1.02000 12.23000 -0.16000 +v 0.02000 11.20000 -0.16000 +v 0.10000 11.20000 -0.16000 +v 0.29000 11.01000 -0.16000 +v 0.02000 10.74000 -0.16000 +v -0.05000 9.96000 -0.16000 +v -0.24000 9.77000 -0.16000 +v -0.43000 9.96000 -0.16000 +v -0.52000 10.08000 -0.16000 +v -0.49000 10.11000 -0.16000 +v -0.65000 10.27000 -0.16000 +v -3.36000 11.61000 -0.16000 +v -3.52000 11.45000 -0.16000 +v -2.96000 10.75000 -0.16000 +v -3.15000 10.56000 -0.16000 +v -3.57000 10.72000 -0.16000 +v -3.73000 10.56000 -0.16000 +v -2.76000 9.27000 -0.16000 +v -3.03000 9.01000 -0.16000 +v -4.20000 8.11000 -0.16000 +v -4.24000 8.14000 -0.16000 +v -4.40000 7.98000 -0.16000 +v -4.54000 6.48000 -0.16000 +v -4.50000 6.44000 -0.16000 +v -4.52000 6.41000 -0.16000 +v -3.21000 5.11000 -0.16000 +v -3.40000 4.92000 -0.16000 +v -3.87000 4.65000 -0.16000 +v -4.58000 4.14000 -0.16000 +v -5.21000 3.57000 -0.16000 +v -5.57000 3.15000 -0.16000 +v -5.57000 3.10000 -0.16000 +v -5.42000 2.95000 -0.16000 +v -4.51000 2.36000 -0.16000 +v -4.31000 2.16000 -0.16000 +v -4.53000 1.94000 -0.16000 +v -5.90000 1.05000 -0.16000 +v -6.06000 0.89000 -0.16000 +v -5.83000 0.06000 -0.16000 +v -5.67000 -0.11000 -0.16000 +v -5.54000 -0.04000 -0.16000 +v -4.64000 0.71000 -0.16000 +v -3.46000 1.61000 -0.16000 +v -3.22000 1.83000 -0.16000 +v -3.03000 1.64000 -0.16000 +v -3.24000 0.70000 -0.16000 +v -3.24000 0.65000 -0.16000 +v -3.10000 0.51000 -0.16000 +v -2.43000 0.77000 -0.16000 +v -2.27000 0.91000 -0.16000 +v -2.08000 0.72000 -0.16000 +v -2.11000 0.56000 -0.16000 +v -1.98000 0.43000 -0.16000 +v -1.32000 0.67000 -0.16000 +v -1.18000 0.83000 -0.16000 +v -0.96000 1.83000 -0.16000 +v -0.76000 2.02000 -0.16000 +v -0.57000 1.83000 -0.16000 +v -0.19000 0.88000 -0.16000 +v -0.04000 0.73000 -0.16000 +v 0.34000 0.90000 -0.16000 +v 0.47000 1.03000 -0.16000 +v 0.55000 1.36000 -0.16000 +v 0.74000 1.55000 -0.16000 +v 1.23000 1.01000 -0.16000 +v 1.40000 0.91000 -0.16000 +v 1.63000 0.87000 -0.16000 +v 1.82000 0.68000 -0.16000 +v 1.65000 0.10000 -0.16000 +v 1.65000 0.05000 -0.16000 +v 1.79000 -0.09000 -0.16000 +v 2.41000 0.15000 -0.16000 +v 2.53000 0.27000 -0.16000 +v 2.60000 0.51000 -0.16000 +v 2.79000 0.70000 -0.16000 +v 2.99000 0.54000 -0.16000 +v 4.48000 1.00000 -0.16000 +v 5.31000 1.22000 -0.16000 +v 5.51000 1.43000 -0.16000 +v 5.72000 2.01000 -0.16000 +v 5.72000 2.06000 -0.16000 +v 5.59000 2.19000 -0.16000 +v 4.29000 1.86000 -0.16000 +v 4.08000 2.05000 -0.16000 +v 4.11000 2.16000 -0.16000 +v 4.30000 2.35000 -0.16000 +v 5.01000 2.54000 -0.16000 +v 5.18000 2.70000 -0.16000 +v 5.37000 3.23000 -0.16000 +v 5.22000 3.37000 -0.16000 +v 4.66000 3.24000 -0.16000 +v 4.47000 3.43000 -0.16000 +v 4.66000 3.62000 -0.16000 +v 5.69000 3.90000 -0.16000 +v 5.83000 4.04000 -0.16000 +v 6.06000 4.61000 -0.16000 +v 6.06000 4.66000 -0.16000 +v 5.92000 4.80000 -0.16000 +v 5.01000 4.60000 -0.16000 +v 4.10000 4.35000 -0.16000 +v 3.67000 4.21000 -0.16000 +v 3.53000 4.07000 -0.16000 +v 3.17000 4.43000 -0.16000 +v 2.98000 4.55000 -0.16000 +v 2.77000 4.60000 -0.16000 +v 2.48000 4.58000 -0.16000 +v 2.26000 4.51000 -0.16000 +v 1.96000 4.34000 -0.16000 +v 1.48000 3.89000 -0.16000 +v 1.29000 4.08000 -0.16000 +v 1.49000 4.71000 -0.16000 +v 1.49000 4.76000 -0.16000 +v 1.33000 4.89000 -0.16000 +v 0.62000 4.64000 -0.16000 +v 0.46000 4.49000 -0.16000 +v 0.19000 3.39000 -0.16000 +v -0.01000 3.19000 -0.16000 +v -0.20000 3.38000 -0.16000 +v -0.49000 4.16000 -0.16000 +v -0.64000 4.32000 -0.16000 +v -1.22000 4.07000 -0.16000 +v -1.41000 4.26000 -0.16000 +v -1.22000 4.89000 -0.16000 +v -1.03000 5.08000 -0.16000 +v 0.22000 5.04000 -0.16000 +v 0.38000 5.20000 -0.16000 +v -0.51000 6.23000 -0.16000 +v -0.54000 6.21000 -0.16000 +v -0.59000 6.26000 -0.16000 +v -1.39000 6.11000 -0.16000 +v -1.58000 6.30000 -0.16000 +v -0.92000 6.99000 -0.16000 +v 0.80000 5.72000 -0.16000 +v 0.86000 5.78000 -0.16000 +v 0.91000 5.73000 -0.16000 +v 1.82000 5.68000 -0.16000 +v 1.93000 5.59000 -0.16000 +v 2.96000 5.02000 -0.16000 +v 3.07000 5.13000 -0.16000 +v 4.35000 5.06000 -0.16000 +v 4.51000 5.22000 -0.16000 +v 3.57000 6.25000 -0.16000 +v 3.54000 6.22000 -0.16000 +v 2.92000 6.19000 -0.16000 +v 2.73000 6.38000 -0.16000 +v 3.08000 7.41000 -0.16000 +v 2.90000 8.37000 -0.16000 +v 2.93000 8.44000 -0.16000 +v 1.75000 9.85000 -0.16000 +v 1.94000 10.05000 -0.16000 +v 2.50000 10.16000 -0.16000 +v 3.02000 9.73000 -0.16000 +v 3.18000 9.89000 -0.16000 +v 3.44000 10.41000 -0.16000 +v 3.36000 10.50000 -0.16000 +v 3.48000 10.62000 -0.16000 +v 3.67000 11.00000 -0.16000 +v 3.66000 11.03000 -0.16000 +v 3.37000 11.44000 -0.16000 +v 3.20000 11.61000 -0.16000 +v 2.98000 11.39000 -0.16000 +v 2.41000 11.47000 -0.16000 +v 1.92000 11.80000 -0.16000 +v 1.47000 11.81000 -0.16000 +v 1.33000 11.93000 -0.16000 +v 0.15000 12.66000 -0.16000 +v -0.01000 12.50000 0.00000 +v 0.16000 12.24000 0.00000 +v -0.03000 12.05000 0.00000 +v -0.85000 12.39000 0.00000 +v -1.02000 12.23000 0.00000 +v 0.02000 11.20000 0.00000 +v 0.10000 11.20000 0.00000 +v 0.29000 11.01000 0.00000 +v 0.02000 10.74000 0.00000 +v -0.05000 9.96000 0.00000 +v -0.24000 9.77000 0.00000 +v -0.43000 9.96000 0.00000 +v -0.52000 10.08000 0.00000 +v -0.49000 10.11000 0.00000 +v -0.65000 10.27000 0.00000 +v -3.36000 11.61000 0.00000 +v -3.52000 11.45000 0.00000 +v -2.96000 10.75000 0.00000 +v -3.15000 10.56000 0.00000 +v -3.57000 10.72000 0.00000 +v -3.73000 10.56000 0.00000 +v -2.76000 9.27000 0.00000 +v -3.03000 9.01000 0.00000 +v -4.20000 8.11000 0.00000 +v -4.24000 8.14000 0.00000 +v -4.40000 7.98000 0.00000 +v -4.54000 6.48000 0.00000 +v -4.50000 6.44000 0.00000 +v -4.52000 6.41000 0.00000 +v -3.21000 5.11000 0.00000 +v -3.40000 4.92000 0.00000 +v -3.87000 4.65000 0.00000 +v -4.58000 4.14000 0.00000 +v -5.21000 3.57000 0.00000 +v -5.57000 3.15000 0.00000 +v -5.57000 3.10000 0.00000 +v -5.42000 2.95000 0.00000 +v -4.51000 2.36000 0.00000 +v -4.31000 2.16000 0.00000 +v -4.53000 1.94000 0.00000 +v -5.90000 1.05000 0.00000 +v -6.06000 0.89000 0.00000 +v -5.83000 0.06000 0.00000 +v -5.67000 -0.11000 0.00000 +v -5.54000 -0.04000 0.00000 +v -4.64000 0.71000 0.00000 +v -3.46000 1.61000 0.00000 +v -3.22000 1.83000 0.00000 +v -3.03000 1.64000 0.00000 +v -3.24000 0.70000 0.00000 +v -3.24000 0.65000 0.00000 +v -3.10000 0.51000 0.00000 +v -2.43000 0.77000 0.00000 +v -2.27000 0.91000 0.00000 +v -2.08000 0.72000 0.00000 +v -2.11000 0.56000 0.00000 +v -1.98000 0.43000 0.00000 +v -1.32000 0.67000 0.00000 +v -1.18000 0.83000 0.00000 +v -0.96000 1.83000 0.00000 +v -0.76000 2.02000 0.00000 +v -0.57000 1.83000 0.00000 +v -0.19000 0.88000 0.00000 +v -0.04000 0.73000 0.00000 +v 0.34000 0.90000 0.00000 +v 0.47000 1.03000 0.00000 +v 0.55000 1.36000 0.00000 +v 0.74000 1.55000 0.00000 +v 1.23000 1.01000 0.00000 +v 1.40000 0.91000 0.00000 +v 1.63000 0.87000 0.00000 +v 1.82000 0.68000 0.00000 +v 1.65000 0.10000 0.00000 +v 1.65000 0.05000 0.00000 +v 1.79000 -0.09000 0.00000 +v 2.41000 0.15000 0.00000 +v 2.53000 0.27000 0.00000 +v 2.60000 0.51000 0.00000 +v 2.79000 0.70000 0.00000 +v 2.99000 0.54000 0.00000 +v 4.48000 1.00000 0.00000 +v 5.31000 1.22000 0.00000 +v 5.51000 1.43000 0.00000 +v 5.72000 2.01000 0.00000 +v 5.72000 2.06000 0.00000 +v 5.59000 2.19000 0.00000 +v 4.29000 1.86000 0.00000 +v 4.08000 2.05000 0.00000 +v 4.11000 2.16000 0.00000 +v 4.30000 2.35000 0.00000 +v 5.01000 2.54000 0.00000 +v 5.18000 2.70000 0.00000 +v 5.37000 3.23000 0.00000 +v 5.22000 3.37000 0.00000 +v 4.66000 3.24000 0.00000 +v 4.47000 3.43000 0.00000 +v 4.66000 3.62000 0.00000 +v 5.69000 3.90000 0.00000 +v 5.83000 4.04000 0.00000 +v 6.06000 4.61000 0.00000 +v 6.06000 4.66000 0.00000 +v 5.92000 4.80000 0.00000 +v 5.01000 4.60000 0.00000 +v 4.10000 4.35000 0.00000 +v 3.67000 4.21000 0.00000 +v 3.53000 4.07000 0.00000 +v 3.17000 4.43000 0.00000 +v 2.98000 4.55000 0.00000 +v 2.77000 4.60000 0.00000 +v 2.48000 4.58000 0.00000 +v 2.26000 4.51000 0.00000 +v 1.96000 4.34000 0.00000 +v 1.48000 3.89000 0.00000 +v 1.29000 4.08000 0.00000 +v 1.49000 4.71000 0.00000 +v 1.49000 4.76000 0.00000 +v 1.33000 4.89000 0.00000 +v 0.62000 4.64000 0.00000 +v 0.46000 4.49000 0.00000 +v 0.19000 3.39000 0.00000 +v -0.01000 3.19000 0.00000 +v -0.20000 3.38000 0.00000 +v -0.49000 4.16000 0.00000 +v -0.64000 4.32000 0.00000 +v -1.22000 4.07000 0.00000 +v -1.41000 4.26000 0.00000 +v -1.22000 4.89000 0.00000 +v -1.03000 5.08000 0.00000 +v 0.22000 5.04000 0.00000 +v 0.38000 5.20000 0.00000 +v -0.51000 6.23000 0.00000 +v -0.54000 6.21000 0.00000 +v -0.59000 6.26000 0.00000 +v -1.39000 6.11000 0.00000 +v -1.58000 6.30000 0.00000 +v -0.92000 6.99000 0.00000 +v 0.80000 5.72000 0.00000 +v 0.86000 5.78000 0.00000 +v 0.91000 5.73000 0.00000 +v 1.82000 5.68000 0.00000 +v 1.93000 5.59000 0.00000 +v 2.96000 5.02000 0.00000 +v 3.07000 5.13000 0.00000 +v 4.35000 5.06000 0.00000 +v 4.51000 5.22000 0.00000 +v 3.57000 6.25000 0.00000 +v 3.54000 6.22000 0.00000 +v 2.92000 6.19000 0.00000 +v 2.73000 6.38000 0.00000 +v 3.08000 7.41000 0.00000 +v 2.90000 8.37000 0.00000 +v 2.93000 8.44000 0.00000 +v 1.75000 9.85000 0.00000 +v 1.94000 10.05000 0.00000 +v 2.50000 10.16000 0.00000 +v 3.02000 9.73000 0.00000 +v 3.18000 9.89000 0.00000 +v 3.44000 10.41000 0.00000 +v 3.36000 10.50000 0.00000 +v 3.48000 10.62000 0.00000 +v 3.67000 11.00000 0.00000 +v 3.66000 11.03000 0.00000 +v 3.37000 11.44000 0.00000 +v 3.20000 11.61000 0.00000 +v 2.98000 11.39000 0.00000 +v 2.41000 11.47000 0.00000 +v 1.92000 11.80000 0.00000 +v 1.47000 11.81000 0.00000 +v 1.33000 11.93000 0.00000 +v 0.15000 12.66000 0.00000 +v -3.62750 4.67000 0.00000 +v -4.25750 4.25500 0.00000 +v -4.97250 3.67000 0.00000 +v -5.24250 3.40000 0.00000 +v -5.44250 3.15500 0.00000 +v -5.45250 3.10000 0.00000 +v -5.39250 3.04500 0.00000 +v -4.35250 2.37000 0.00000 +v -4.07250 2.15000 0.00000 +v -4.65250 1.73000 0.00000 +v -5.93250 0.89000 0.00000 +v -5.94250 0.83000 0.00000 +v -5.74250 0.16000 0.00000 +v -5.67250 0.00500 0.00000 +v -5.61250 0.02000 0.00000 +v -4.73750 0.74500 0.00000 +v -3.46250 1.72500 0.00000 +v -3.19250 1.97000 0.00000 +v -2.96750 2.23000 0.00000 +v -2.97250 2.27500 0.00000 +v -3.04250 2.34500 0.00000 +v -3.26750 2.52000 0.00000 +v -4.33750 3.17500 0.00000 +v -4.10250 3.38500 0.00000 +v -3.67750 3.67000 0.00000 +v -2.68750 4.19000 0.00000 +v -2.88750 4.86500 0.00000 +v -2.93250 4.97500 0.00000 +v -2.97250 4.99000 0.00000 +v -3.62750 4.67000 0.24000 +v -4.25750 4.25500 0.24000 +v -4.97250 3.67000 0.24000 +v -5.24250 3.40000 0.24000 +v -5.44250 3.15500 0.24000 +v -5.45250 3.10000 0.24000 +v -5.39250 3.04500 0.24000 +v -4.35250 2.37000 0.24000 +v -4.07250 2.15000 0.24000 +v -4.65250 1.73000 0.24000 +v -5.93250 0.89000 0.24000 +v -5.94250 0.83000 0.24000 +v -5.74250 0.16000 0.24000 +v -5.67250 0.00500 0.24000 +v -5.61250 0.02000 0.24000 +v -4.73750 0.74500 0.24000 +v -3.46250 1.72500 0.24000 +v -3.19250 1.97000 0.24000 +v -2.96750 2.23000 0.24000 +v -2.97250 2.27500 0.24000 +v -3.04250 2.34500 0.24000 +v -3.26750 2.52000 0.24000 +v -4.33750 3.17500 0.24000 +v -4.10250 3.38500 0.24000 +v -3.67750 3.67000 0.24000 +v -2.68750 4.19000 0.24000 +v -2.88750 4.86500 0.24000 +v -2.93250 4.97500 0.24000 +v -2.97250 4.99000 0.24000 +v -3.62750 4.67000 0.24000 +v -4.25750 4.25500 0.24000 +v -4.97250 3.67000 0.24000 +v -5.24250 3.40000 0.24000 +v -5.44250 3.15500 0.24000 +v -5.45250 3.10000 0.24000 +v -5.39250 3.04500 0.24000 +v -4.35250 2.37000 0.24000 +v -4.07250 2.15000 0.24000 +v -4.65250 1.73000 0.24000 +v -5.93250 0.89000 0.24000 +v -5.94250 0.83000 0.24000 +v -5.74250 0.16000 0.24000 +v -5.67250 0.00500 0.24000 +v -5.61250 0.02000 0.24000 +v -4.73750 0.74500 0.24000 +v -3.46250 1.72500 0.24000 +v -3.19250 1.97000 0.24000 +v -2.96750 2.23000 0.24000 +v -2.97250 2.27500 0.24000 +v -3.04250 2.34500 0.24000 +v -3.26750 2.52000 0.24000 +v -4.33750 3.17500 0.24000 +v -4.10250 3.38500 0.24000 +v -3.67750 3.67000 0.24000 +v -2.68750 4.19000 0.24000 +v -2.88750 4.86500 0.24000 +v -2.93250 4.97500 0.24000 +v -2.97250 4.99000 0.24000 +v -3.62750 4.67000 0.25000 +v -4.25750 4.25500 0.25000 +v -4.97250 3.67000 0.25000 +v -5.24250 3.40000 0.25000 +v -5.44250 3.15500 0.25000 +v -5.45250 3.10000 0.25000 +v -5.39250 3.04500 0.25000 +v -4.35250 2.37000 0.25000 +v -4.07250 2.15000 0.25000 +v -4.65250 1.73000 0.25000 +v -5.93250 0.89000 0.25000 +v -5.94250 0.83000 0.25000 +v -5.74250 0.16000 0.25000 +v -5.67250 0.00500 0.25000 +v -5.61250 0.02000 0.25000 +v -4.73750 0.74500 0.25000 +v -3.46250 1.72500 0.25000 +v -3.19250 1.97000 0.25000 +v -2.96750 2.23000 0.25000 +v -2.97250 2.27500 0.25000 +v -3.04250 2.34500 0.25000 +v -3.26750 2.52000 0.25000 +v -4.33750 3.17500 0.25000 +v -4.10250 3.38500 0.25000 +v -3.67750 3.67000 0.25000 +v -2.68750 4.19000 0.25000 +v -2.88750 4.86500 0.25000 +v -2.93250 4.97500 0.25000 +v -2.97250 4.99000 0.25000 +v -3.60267 4.62648 0.30000 +v -4.22784 4.21467 0.30000 +v -4.93891 3.63288 0.30000 +v -5.20536 3.36643 0.30000 +v -5.39561 3.13337 0.30000 +v -5.39837 3.11821 0.30000 +v -5.36178 3.08467 0.30000 +v -4.32338 2.41071 0.30000 +v -3.98950 2.14837 0.30000 +v -4.62411 1.68883 0.30000 +v -5.88678 0.86020 0.30000 +v -5.89128 0.83320 0.30000 +v -5.69555 0.17752 0.30000 +v -5.64412 0.06363 0.30000 +v -5.63565 0.06575 0.30000 +v -4.76870 0.78409 0.30000 +v -3.49459 1.76340 0.30000 +v -3.22833 2.00500 0.30000 +v -3.01961 2.24620 0.30000 +v -3.02026 2.25205 0.30000 +v -3.07565 2.30744 0.30000 +v -3.29599 2.47882 0.30000 +v -4.42097 3.16747 0.30000 +v -4.13323 3.42459 0.30000 +v -3.70312 3.71302 0.30000 +v -2.74711 4.21517 0.30000 +v -2.93473 4.84839 0.30000 +v -2.97051 4.93585 0.30000 +v -2.96990 4.93562 0.30000 +v -2.44250 3.48500 0.00000 +v -2.50250 3.43500 0.00000 +v -3.09250 0.83000 0.00000 +v -3.12250 0.67000 0.00000 +v -3.10250 0.62500 0.00000 +v -2.53750 0.84000 0.00000 +v -2.37750 0.92500 0.00000 +v -2.04750 2.34000 0.00000 +v -1.69750 3.70000 0.00000 +v -1.70250 3.74500 0.00000 +v -1.73250 3.75000 0.00000 +v -2.44250 3.48500 0.24000 +v -2.50250 3.43500 0.24000 +v -3.09250 0.83000 0.24000 +v -3.12250 0.67000 0.24000 +v -3.10250 0.62500 0.24000 +v -2.53750 0.84000 0.24000 +v -2.37750 0.92500 0.24000 +v -2.04750 2.34000 0.24000 +v -1.69750 3.70000 0.24000 +v -1.70250 3.74500 0.24000 +v -1.73250 3.75000 0.24000 +v -2.44250 3.48500 0.24000 +v -2.50250 3.43500 0.24000 +v -3.09250 0.83000 0.24000 +v -3.12250 0.67000 0.24000 +v -3.10250 0.62500 0.24000 +v -2.53750 0.84000 0.24000 +v -2.37750 0.92500 0.24000 +v -2.04750 2.34000 0.24000 +v -1.69750 3.70000 0.24000 +v -1.70250 3.74500 0.24000 +v -1.73250 3.75000 0.24000 +v -2.44250 3.48500 0.25000 +v -2.50250 3.43500 0.25000 +v -3.09250 0.83000 0.25000 +v -3.12250 0.67000 0.25000 +v -3.10250 0.62500 0.25000 +v -2.53750 0.84000 0.25000 +v -2.37750 0.92500 0.25000 +v -2.04750 2.34000 0.25000 +v -1.69750 3.70000 0.25000 +v -1.70250 3.74500 0.25000 +v -1.73250 3.75000 0.25000 +v -2.41704 3.44114 0.30000 +v -2.45748 3.40743 0.30000 +v -3.04353 0.81987 0.30000 +v -3.07049 0.67608 0.30000 +v -3.07604 0.68857 0.30000 +v -2.55820 0.88562 0.30000 +v -2.42103 0.95849 0.30000 +v -2.09606 2.35191 0.30000 +v -1.74821 3.70359 0.30000 +v -1.74802 3.70190 0.30000 +v -1.72754 3.69848 0.30000 +v -2.26750 4.54500 0.00000 +v -2.29250 4.51500 0.00000 +v -2.45750 3.81500 0.00000 +v -2.41750 3.80000 0.00000 +v -2.31750 3.83500 0.00000 +v -1.58250 4.13500 0.00000 +v -1.33750 4.83500 0.00000 +v -1.33750 4.87500 0.00000 +v -1.36750 4.88500 0.00000 +v -2.26750 4.54500 0.24000 +v -2.29250 4.51500 0.24000 +v -2.45750 3.81500 0.24000 +v -2.41750 3.80000 0.24000 +v -2.31750 3.83500 0.24000 +v -1.58250 4.13500 0.24000 +v -1.33750 4.83500 0.24000 +v -1.33750 4.87500 0.24000 +v -1.36750 4.88500 0.24000 +v -2.26750 4.54500 0.24000 +v -2.29250 4.51500 0.24000 +v -2.45750 3.81500 0.24000 +v -2.41750 3.80000 0.24000 +v -2.31750 3.83500 0.24000 +v -1.58250 4.13500 0.24000 +v -1.33750 4.83500 0.24000 +v -1.33750 4.87500 0.24000 +v -1.36750 4.88500 0.24000 +v -2.26750 4.54500 0.25000 +v -2.29250 4.51500 0.25000 +v -2.45750 3.81500 0.25000 +v -2.41750 3.80000 0.25000 +v -2.31750 3.83500 0.25000 +v -1.58250 4.13500 0.25000 +v -1.33750 4.83500 0.25000 +v -1.33750 4.87500 0.25000 +v -1.36750 4.88500 0.25000 +v -2.23752 4.50288 0.30000 +v -2.24654 4.49205 0.30000 +v -2.39874 3.84636 0.30000 +v -2.41691 3.85318 0.30000 +v -2.33522 3.88177 0.30000 +v -1.62225 4.17278 0.30000 +v -1.38750 4.84350 0.30000 +v -1.38750 4.83896 0.30000 +v -1.36645 4.83195 0.30000 +v 0.58250 4.48000 0.00000 +v 0.07250 2.38500 0.00000 +v -0.62750 4.18000 0.00000 +v -0.69250 4.18500 0.00000 +v -1.21750 3.94500 0.00000 +v -1.78250 1.53500 0.00000 +v -1.98250 0.64000 0.00000 +v -1.98250 0.54500 0.00000 +v -1.92750 0.54500 0.00000 +v -1.31250 0.79500 0.00000 +v -0.84250 2.77000 0.00000 +v -0.05250 0.85000 0.00000 +v -0.00750 0.84000 0.00000 +v 0.35250 1.03000 0.00000 +v 0.89750 3.14500 0.00000 +v 1.36750 4.70000 0.00000 +v 1.37250 4.75000 0.00000 +v 1.33750 4.76500 0.00000 +v 0.58250 4.48000 0.24000 +v 0.07250 2.38500 0.24000 +v -0.62750 4.18000 0.24000 +v -0.69250 4.18500 0.24000 +v -1.21750 3.94500 0.24000 +v -1.78250 1.53500 0.24000 +v -1.98250 0.64000 0.24000 +v -1.98250 0.54500 0.24000 +v -1.92750 0.54500 0.24000 +v -1.31250 0.79500 0.24000 +v -0.84250 2.77000 0.24000 +v -0.05250 0.85000 0.24000 +v -0.00750 0.84000 0.24000 +v 0.35250 1.03000 0.24000 +v 0.89750 3.14500 0.24000 +v 1.36750 4.70000 0.24000 +v 1.37250 4.75000 0.24000 +v 1.33750 4.76500 0.24000 +v 0.58250 4.48000 0.24000 +v 0.07250 2.38500 0.24000 +v -0.62750 4.18000 0.24000 +v -0.69250 4.18500 0.24000 +v -1.21750 3.94500 0.24000 +v -1.78250 1.53500 0.24000 +v -1.98250 0.64000 0.24000 +v -1.98250 0.54500 0.24000 +v -1.92750 0.54500 0.24000 +v -1.31250 0.79500 0.24000 +v -0.84250 2.77000 0.24000 +v -0.05250 0.85000 0.24000 +v -0.00750 0.84000 0.24000 +v 0.35250 1.03000 0.24000 +v 0.89750 3.14500 0.24000 +v 1.36750 4.70000 0.24000 +v 1.37250 4.75000 0.24000 +v 1.33750 4.76500 0.24000 +v 0.58250 4.48000 0.25000 +v 0.07250 2.38500 0.25000 +v -0.62750 4.18000 0.25000 +v -0.69250 4.18500 0.25000 +v -1.21750 3.94500 0.25000 +v -1.78250 1.53500 0.25000 +v -1.98250 0.64000 0.25000 +v -1.98250 0.54500 0.25000 +v -1.92750 0.54500 0.25000 +v -1.31250 0.79500 0.25000 +v -0.84250 2.77000 0.25000 +v -0.05250 0.85000 0.25000 +v -0.00750 0.84000 0.25000 +v 0.35250 1.03000 0.25000 +v 0.89750 3.14500 0.25000 +v 1.36750 4.70000 0.25000 +v 1.37250 4.75000 0.25000 +v 1.33750 4.76500 0.25000 +v 0.62484 4.44254 0.30000 +v 0.08200 2.24246 0.30000 +v -0.66267 4.13256 0.30000 +v -0.68346 4.13416 0.30000 +v -1.17442 3.90972 0.30000 +v -1.73376 1.52384 0.30000 +v -1.93250 0.63448 0.30000 +v -1.93250 0.59500 0.30000 +v -1.93727 0.59500 0.30000 +v -1.35518 0.83162 0.30000 +v -0.85368 2.91242 0.30000 +v -0.01619 0.89315 0.30000 +v -0.01459 0.89280 0.30000 +v 0.30960 1.06390 0.30000 +v 0.84934 3.15848 0.30000 +v 1.31823 4.70981 0.30000 +v 1.31910 4.71849 0.30000 +v 1.33632 4.71111 0.30000 +v 2.31250 4.40000 0.00000 +v 2.06750 4.28000 0.00000 +v 1.84750 4.12000 0.00000 +v 1.59250 3.86000 0.00000 +v 1.35750 3.53500 0.00000 +v 1.16750 3.18000 0.00000 +v 1.01750 2.78000 0.00000 +v 0.93750 2.45500 0.00000 +v 0.89750 2.04500 0.00000 +v 0.91750 1.74500 0.00000 +v 0.99750 1.46000 0.00000 +v 1.13750 1.22000 0.00000 +v 1.33750 1.05500 0.00000 +v 1.55250 0.98500 0.00000 +v 1.78250 1.00000 0.00000 +v 2.06750 1.10000 0.00000 +v 1.76750 0.08000 0.00000 +v 1.78750 0.02500 0.00000 +v 2.40750 0.26500 0.00000 +v 3.31250 3.06000 0.00000 +v 3.27250 3.09000 0.00000 +v 3.02750 3.04500 0.00000 +v 2.05250 2.79500 0.00000 +v 1.87250 2.27500 0.00000 +v 1.93250 2.25000 0.00000 +v 2.46250 2.38000 0.00000 +v 2.47750 2.35500 0.00000 +v 2.37750 2.10000 0.00000 +v 2.15750 1.84500 0.00000 +v 1.99750 1.73500 0.00000 +v 1.87250 1.68500 0.00000 +v 1.69250 1.69000 0.00000 +v 1.58750 1.77500 0.00000 +v 1.55250 1.83500 0.00000 +v 1.50750 2.12500 0.00000 +v 1.57250 2.52000 0.00000 +v 1.70250 2.87500 0.00000 +v 1.94750 3.28500 0.00000 +v 2.20750 3.55500 0.00000 +v 2.33250 3.64000 0.00000 +v 2.47750 3.70000 0.00000 +v 2.57250 3.71000 0.00000 +v 2.66750 3.68000 0.00000 +v 2.74750 3.61000 0.00000 +v 2.82250 3.49000 0.00000 +v 2.85750 3.49000 0.00000 +v 3.31750 4.06000 0.00000 +v 3.31250 4.10500 0.00000 +v 3.23750 4.22000 0.00000 +v 3.06750 4.37500 0.00000 +v 2.87250 4.45500 0.00000 +v 2.60750 4.47000 0.00000 +v 2.31250 4.40000 0.24000 +v 2.06750 4.28000 0.24000 +v 1.84750 4.12000 0.24000 +v 1.59250 3.86000 0.24000 +v 1.35750 3.53500 0.24000 +v 1.16750 3.18000 0.24000 +v 1.01750 2.78000 0.24000 +v 0.93750 2.45500 0.24000 +v 0.89750 2.04500 0.24000 +v 0.91750 1.74500 0.24000 +v 0.99750 1.46000 0.24000 +v 1.13750 1.22000 0.24000 +v 1.33750 1.05500 0.24000 +v 1.55250 0.98500 0.24000 +v 1.78250 1.00000 0.24000 +v 2.06750 1.10000 0.24000 +v 1.76750 0.08000 0.24000 +v 1.78750 0.02500 0.24000 +v 2.40750 0.26500 0.24000 +v 3.31250 3.06000 0.24000 +v 3.27250 3.09000 0.24000 +v 3.02750 3.04500 0.24000 +v 2.05250 2.79500 0.24000 +v 1.87250 2.27500 0.24000 +v 1.93250 2.25000 0.24000 +v 2.46250 2.38000 0.24000 +v 2.47750 2.35500 0.24000 +v 2.37750 2.10000 0.24000 +v 2.15750 1.84500 0.24000 +v 1.99750 1.73500 0.24000 +v 1.87250 1.68500 0.24000 +v 1.69250 1.69000 0.24000 +v 1.58750 1.77500 0.24000 +v 1.55250 1.83500 0.24000 +v 1.50750 2.12500 0.24000 +v 1.57250 2.52000 0.24000 +v 1.70250 2.87500 0.24000 +v 1.94750 3.28500 0.24000 +v 2.20750 3.55500 0.24000 +v 2.33250 3.64000 0.24000 +v 2.47750 3.70000 0.24000 +v 2.57250 3.71000 0.24000 +v 2.66750 3.68000 0.24000 +v 2.74750 3.61000 0.24000 +v 2.82250 3.49000 0.24000 +v 2.85750 3.49000 0.24000 +v 3.31750 4.06000 0.24000 +v 3.31250 4.10500 0.24000 +v 3.23750 4.22000 0.24000 +v 3.06750 4.37500 0.24000 +v 2.87250 4.45500 0.24000 +v 2.60750 4.47000 0.24000 +v 2.31250 4.40000 0.24000 +v 2.06750 4.28000 0.24000 +v 1.84750 4.12000 0.24000 +v 1.59250 3.86000 0.24000 +v 1.35750 3.53500 0.24000 +v 1.16750 3.18000 0.24000 +v 1.01750 2.78000 0.24000 +v 0.93750 2.45500 0.24000 +v 0.89750 2.04500 0.24000 +v 0.91750 1.74500 0.24000 +v 0.99750 1.46000 0.24000 +v 1.13750 1.22000 0.24000 +v 1.33750 1.05500 0.24000 +v 1.55250 0.98500 0.24000 +v 1.78250 1.00000 0.24000 +v 2.06750 1.10000 0.24000 +v 1.76750 0.08000 0.24000 +v 1.78750 0.02500 0.24000 +v 2.40750 0.26500 0.24000 +v 3.31250 3.06000 0.24000 +v 3.27250 3.09000 0.24000 +v 3.02750 3.04500 0.24000 +v 2.05250 2.79500 0.24000 +v 1.87250 2.27500 0.24000 +v 1.93250 2.25000 0.24000 +v 2.46250 2.38000 0.24000 +v 2.47750 2.35500 0.24000 +v 2.37750 2.10000 0.24000 +v 2.15750 1.84500 0.24000 +v 1.99750 1.73500 0.24000 +v 1.87250 1.68500 0.24000 +v 1.69250 1.69000 0.24000 +v 1.58750 1.77500 0.24000 +v 1.55250 1.83500 0.24000 +v 1.50750 2.12500 0.24000 +v 1.57250 2.52000 0.24000 +v 1.70250 2.87500 0.24000 +v 1.94750 3.28500 0.24000 +v 2.20750 3.55500 0.24000 +v 2.33250 3.64000 0.24000 +v 2.47750 3.70000 0.24000 +v 2.57250 3.71000 0.24000 +v 2.66750 3.68000 0.24000 +v 2.74750 3.61000 0.24000 +v 2.82250 3.49000 0.24000 +v 2.85750 3.49000 0.24000 +v 3.31750 4.06000 0.24000 +v 3.31250 4.10500 0.24000 +v 3.23750 4.22000 0.24000 +v 3.06750 4.37500 0.24000 +v 2.87250 4.45500 0.24000 +v 2.60750 4.47000 0.24000 +v 2.31250 4.40000 0.25000 +v 2.06750 4.28000 0.25000 +v 1.84750 4.12000 0.25000 +v 1.59250 3.86000 0.25000 +v 1.35750 3.53500 0.25000 +v 1.16750 3.18000 0.25000 +v 1.01750 2.78000 0.25000 +v 0.93750 2.45500 0.25000 +v 0.89750 2.04500 0.25000 +v 0.91750 1.74500 0.25000 +v 0.99750 1.46000 0.25000 +v 1.13750 1.22000 0.25000 +v 1.33750 1.05500 0.25000 +v 1.55250 0.98500 0.25000 +v 1.78250 1.00000 0.25000 +v 2.06750 1.10000 0.25000 +v 1.76750 0.08000 0.25000 +v 1.78750 0.02500 0.25000 +v 2.40750 0.26500 0.25000 +v 3.31250 3.06000 0.25000 +v 3.27250 3.09000 0.25000 +v 3.02750 3.04500 0.25000 +v 2.05250 2.79500 0.25000 +v 1.87250 2.27500 0.25000 +v 1.93250 2.25000 0.25000 +v 2.46250 2.38000 0.25000 +v 2.47750 2.35500 0.25000 +v 2.37750 2.10000 0.25000 +v 2.15750 1.84500 0.25000 +v 1.99750 1.73500 0.25000 +v 1.87250 1.68500 0.25000 +v 1.69250 1.69000 0.25000 +v 1.58750 1.77500 0.25000 +v 1.55250 1.83500 0.25000 +v 1.50750 2.12500 0.25000 +v 1.57250 2.52000 0.25000 +v 1.70250 2.87500 0.25000 +v 1.94750 3.28500 0.25000 +v 2.20750 3.55500 0.25000 +v 2.33250 3.64000 0.25000 +v 2.47750 3.70000 0.25000 +v 2.57250 3.71000 0.25000 +v 2.66750 3.68000 0.25000 +v 2.74750 3.61000 0.25000 +v 2.82250 3.49000 0.25000 +v 2.85750 3.49000 0.25000 +v 3.31750 4.06000 0.25000 +v 3.31250 4.10500 0.25000 +v 3.23750 4.22000 0.25000 +v 3.06750 4.37500 0.25000 +v 2.87250 4.45500 0.25000 +v 2.60750 4.47000 0.25000 +v 2.32948 4.35264 0.30000 +v 2.09340 4.23701 0.30000 +v 1.88028 4.08201 0.30000 +v 1.63082 3.82767 0.30000 +v 1.39999 3.50843 0.30000 +v 1.21315 3.15933 0.30000 +v 1.06535 2.76520 0.30000 +v 0.98691 2.44655 0.30000 +v 0.94766 2.04423 0.30000 +v 0.96704 1.75351 0.30000 +v 1.04391 1.47967 0.30000 +v 1.17620 1.25289 0.30000 +v 1.36200 1.09961 0.30000 +v 1.55884 1.03552 0.30000 +v 1.77241 1.04945 0.30000 +v 2.14299 1.17948 0.30000 +v 1.82010 0.08165 0.30000 +v 1.81705 0.09005 0.30000 +v 2.36726 0.30304 0.30000 +v 3.25393 3.04143 0.30000 +v 3.26001 3.03687 0.30000 +v 3.03824 2.99614 0.30000 +v 2.09096 2.75324 0.30000 +v 1.93513 2.30307 0.30000 +v 1.93656 2.30248 0.30000 +v 2.48640 2.43735 0.30000 +v 2.53303 2.35964 0.30000 +v 2.42093 2.07380 0.30000 +v 2.19114 1.80745 0.30000 +v 2.02124 1.69064 0.30000 +v 1.88146 1.63473 0.30000 +v 1.67419 1.64049 0.30000 +v 1.54892 1.74190 0.30000 +v 1.50454 1.81798 0.30000 +v 1.45687 2.12523 0.30000 +v 1.52393 2.53277 0.30000 +v 1.65717 2.89661 0.30000 +v 1.90751 3.31556 0.30000 +v 2.17509 3.59342 0.30000 +v 2.30863 3.68424 0.30000 +v 2.46507 3.74897 0.30000 +v 2.57762 3.76082 0.30000 +v 2.69254 3.72453 0.30000 +v 2.78598 3.64276 0.30000 +v 2.85021 3.54000 0.30000 +v 2.83360 3.54000 0.30000 +v 3.26550 4.07519 0.30000 +v 3.26412 4.08765 0.30000 +v 3.19910 4.18735 0.30000 +v 3.04034 4.33210 0.30000 +v 2.86129 4.40555 0.30000 +v 2.61195 4.41967 0.30000 +v 4.40750 4.31000 0.00000 +v 3.64750 4.06000 0.00000 +v 3.45750 3.43500 0.00000 +v 3.50250 3.41500 0.00000 +v 3.67750 3.47000 0.00000 +v 3.69250 3.45000 0.00000 +v 3.05750 1.16000 0.00000 +v 2.94250 0.68500 0.00000 +v 2.95750 0.65500 0.00000 +v 5.25750 1.32000 0.00000 +v 5.36250 1.37500 0.00000 +v 5.60250 2.02000 0.00000 +v 5.58250 2.06500 0.00000 +v 4.71750 1.85500 0.00000 +v 3.87750 1.61500 0.00000 +v 3.85250 1.64500 0.00000 +v 4.06250 2.39000 0.00000 +v 5.03250 2.66500 0.00000 +v 5.25250 3.18500 0.00000 +v 5.23750 3.23500 0.00000 +v 5.18750 3.23500 0.00000 +v 4.23750 3.01000 0.00000 +v 4.42250 3.65000 0.00000 +v 5.69750 4.02000 0.00000 +v 5.94250 4.61500 0.00000 +v 5.91750 4.67500 0.00000 +v 5.88250 4.67500 0.00000 +v 4.40750 4.31000 0.24000 +v 3.64750 4.06000 0.24000 +v 3.45750 3.43500 0.24000 +v 3.50250 3.41500 0.24000 +v 3.67750 3.47000 0.24000 +v 3.69250 3.45000 0.24000 +v 3.05750 1.16000 0.24000 +v 2.94250 0.68500 0.24000 +v 2.95750 0.65500 0.24000 +v 5.25750 1.32000 0.24000 +v 5.36250 1.37500 0.24000 +v 5.60250 2.02000 0.24000 +v 5.58250 2.06500 0.24000 +v 4.71750 1.85500 0.24000 +v 3.87750 1.61500 0.24000 +v 3.85250 1.64500 0.24000 +v 4.06250 2.39000 0.24000 +v 5.03250 2.66500 0.24000 +v 5.25250 3.18500 0.24000 +v 5.23750 3.23500 0.24000 +v 5.18750 3.23500 0.24000 +v 4.23750 3.01000 0.24000 +v 4.42250 3.65000 0.24000 +v 5.69750 4.02000 0.24000 +v 5.94250 4.61500 0.24000 +v 5.91750 4.67500 0.24000 +v 5.88250 4.67500 0.24000 +v 4.40750 4.31000 0.24000 +v 3.64750 4.06000 0.24000 +v 3.45750 3.43500 0.24000 +v 3.50250 3.41500 0.24000 +v 3.67750 3.47000 0.24000 +v 3.69250 3.45000 0.24000 +v 3.05750 1.16000 0.24000 +v 2.94250 0.68500 0.24000 +v 2.95750 0.65500 0.24000 +v 5.25750 1.32000 0.24000 +v 5.36250 1.37500 0.24000 +v 5.60250 2.02000 0.24000 +v 5.58250 2.06500 0.24000 +v 4.71750 1.85500 0.24000 +v 3.87750 1.61500 0.24000 +v 3.85250 1.64500 0.24000 +v 4.06250 2.39000 0.24000 +v 5.03250 2.66500 0.24000 +v 5.25250 3.18500 0.24000 +v 5.23750 3.23500 0.24000 +v 5.18750 3.23500 0.24000 +v 4.23750 3.01000 0.24000 +v 4.42250 3.65000 0.24000 +v 5.69750 4.02000 0.24000 +v 5.94250 4.61500 0.24000 +v 5.91750 4.67500 0.24000 +v 5.88250 4.67500 0.24000 +v 4.40750 4.31000 0.25000 +v 3.64750 4.06000 0.25000 +v 3.45750 3.43500 0.25000 +v 3.50250 3.41500 0.25000 +v 3.67750 3.47000 0.25000 +v 3.69250 3.45000 0.25000 +v 3.05750 1.16000 0.25000 +v 2.94250 0.68500 0.25000 +v 2.95750 0.65500 0.25000 +v 5.25750 1.32000 0.25000 +v 5.36250 1.37500 0.25000 +v 5.60250 2.02000 0.25000 +v 5.58250 2.06500 0.25000 +v 4.71750 1.85500 0.25000 +v 3.87750 1.61500 0.25000 +v 3.85250 1.64500 0.25000 +v 4.06250 2.39000 0.25000 +v 5.03250 2.66500 0.25000 +v 5.25250 3.18500 0.25000 +v 5.23750 3.23500 0.25000 +v 5.18750 3.23500 0.25000 +v 4.23750 3.01000 0.25000 +v 4.42250 3.65000 0.25000 +v 5.69750 4.02000 0.25000 +v 5.94250 4.61500 0.25000 +v 5.91750 4.67500 0.25000 +v 5.88250 4.67500 0.25000 +v 4.42134 4.26192 0.30000 +v 3.68779 4.02062 0.30000 +v 3.51819 3.46274 0.30000 +v 3.50554 3.46837 0.30000 +v 3.69627 3.52831 0.30000 +v 3.74725 3.46033 0.30000 +v 3.10590 1.14743 0.30000 +v 2.99540 0.69101 0.30000 +v 2.98360 0.71460 0.30000 +v 5.23877 1.36663 0.30000 +v 5.32232 1.41040 0.30000 +v 5.54853 2.01833 0.30000 +v 5.55375 2.00657 0.30000 +v 4.73027 1.80665 0.30000 +v 3.85993 1.55798 0.30000 +v 3.79723 1.63322 0.30000 +v 4.02196 2.43048 0.30000 +v 4.99579 2.70656 0.30000 +v 5.19943 3.18789 0.30000 +v 5.20030 3.18500 0.30000 +v 5.19334 3.18500 0.30000 +v 4.16568 2.94161 0.30000 +v 4.38211 3.69034 0.30000 +v 5.66044 4.06131 0.30000 +v 5.88838 4.61489 0.30000 +v 5.88417 4.62500 0.30000 +v 5.88859 4.62500 0.30000 +v 0.70500 11.69000 0.00000 +v 0.74500 11.65000 0.00000 +v 1.22500 11.40000 0.00000 +v 1.37500 11.25000 0.00000 +v 1.95500 10.87000 0.00000 +v 2.06500 10.76000 0.00000 +v 1.43500 10.11000 0.00000 +v 1.47500 10.07000 0.00000 +v 2.50500 10.28000 0.00000 +v 3.02500 9.85000 0.00000 +v 3.06500 9.89000 0.00000 +v 3.32500 10.41000 0.00000 +v 3.25500 10.48000 0.00000 +v 3.20500 10.46000 0.00000 +v 2.76500 10.56000 0.00000 +v 2.65500 10.67000 0.00000 +v 2.76500 10.78000 0.00000 +v 3.09500 10.85000 0.00000 +v 3.32500 10.58000 0.00000 +v 3.36500 10.62000 0.00000 +v 3.55500 11.01000 0.00000 +v 3.20500 11.49000 0.00000 +v 3.16500 11.38000 0.00000 +v 3.05500 11.27000 0.00000 +v 2.66500 11.30000 0.00000 +v 2.41500 11.35000 0.00000 +v 1.92500 11.68000 0.00000 +v 0.74500 11.73000 0.00000 +v 0.70500 11.69000 0.38000 +v 0.74500 11.65000 0.38000 +v 1.22500 11.40000 0.38000 +v 1.37500 11.25000 0.38000 +v 1.95500 10.87000 0.38000 +v 2.06500 10.76000 0.38000 +v 1.43500 10.11000 0.38000 +v 1.47500 10.07000 0.38000 +v 2.50500 10.28000 0.38000 +v 3.02500 9.85000 0.38000 +v 3.06500 9.89000 0.38000 +v 3.32500 10.41000 0.38000 +v 3.25500 10.48000 0.38000 +v 3.20500 10.46000 0.38000 +v 2.76500 10.56000 0.38000 +v 2.65500 10.67000 0.38000 +v 2.76500 10.78000 0.38000 +v 3.09500 10.85000 0.38000 +v 3.32500 10.58000 0.38000 +v 3.36500 10.62000 0.38000 +v 3.55500 11.01000 0.38000 +v 3.20500 11.49000 0.38000 +v 3.16500 11.38000 0.38000 +v 3.05500 11.27000 0.38000 +v 2.66500 11.30000 0.38000 +v 2.41500 11.35000 0.38000 +v 1.92500 11.68000 0.38000 +v 0.74500 11.73000 0.38000 +v 0.71349 11.69000 0.38000 +v 0.74859 11.65490 0.38000 +v 1.22859 11.40490 0.38000 +v 1.30717 11.32632 0.38000 +v 1.38547 11.33611 0.38000 +v 1.45560 11.31607 0.38000 +v 1.82484 11.34601 0.38000 +v 2.18493 11.33600 0.38000 +v 2.38239 11.34588 0.38000 +v 2.39593 11.35941 0.38000 +v 2.27157 11.45507 0.38000 +v 1.92316 11.67407 0.38000 +v 0.74738 11.72389 0.38000 +v 0.71349 11.69000 0.41250 +v 0.74859 11.65490 0.41250 +v 1.22859 11.40490 0.41250 +v 1.30717 11.32632 0.41250 +v 1.38547 11.33611 0.41250 +v 1.45560 11.31607 0.41250 +v 1.82484 11.34601 0.41250 +v 2.18493 11.33600 0.41250 +v 2.38239 11.34588 0.41250 +v 2.39593 11.35941 0.41250 +v 2.27157 11.45507 0.41250 +v 1.92316 11.67407 0.41250 +v 0.74738 11.72389 0.41250 +v 0.72409 11.69000 0.42000 +v 0.75308 11.66101 0.42000 +v 1.23308 11.41101 0.42000 +v 1.30988 11.33421 0.42000 +v 1.38606 11.34374 0.42000 +v 1.45635 11.32365 0.42000 +v 1.82464 11.35352 0.42000 +v 2.18485 11.34351 0.42000 +v 2.37913 11.35322 0.42000 +v 2.38458 11.35867 0.42000 +v 2.26728 11.44891 0.42000 +v 1.92085 11.66666 0.42000 +v 0.75035 11.71626 0.42000 +v 1.45440 11.29393 0.38000 +v 1.38458 11.31388 0.38000 +v 1.32773 11.30576 0.38000 +v 1.37881 11.25468 0.38000 +v 1.95881 10.87468 0.38000 +v 2.07844 10.75504 0.38000 +v 2.23626 10.68600 0.38000 +v 2.66251 10.68600 0.38000 +v 2.76202 10.78550 0.38000 +v 3.09694 10.85655 0.38000 +v 3.11423 10.83925 0.38000 +v 3.16191 10.90600 0.38000 +v 3.17900 10.90600 0.38000 +v 3.17900 11.21772 0.38000 +v 3.10430 11.30176 0.38000 +v 3.05710 11.26400 0.38000 +v 2.98465 11.26400 0.38000 +v 2.55582 11.31386 0.38000 +v 2.45588 11.27389 0.38000 +v 2.24054 11.29440 0.38000 +v 2.23068 11.32400 0.38000 +v 1.82524 11.32400 0.38000 +v 1.45440 11.29393 0.40500 +v 1.38458 11.31388 0.40500 +v 1.32773 11.30576 0.40500 +v 1.37881 11.25468 0.40500 +v 1.95881 10.87468 0.40500 +v 2.07844 10.75504 0.40500 +v 2.23626 10.68600 0.40500 +v 2.66251 10.68600 0.40500 +v 2.76202 10.78550 0.40500 +v 3.09694 10.85655 0.40500 +v 3.11423 10.83925 0.40500 +v 3.16191 10.90600 0.40500 +v 3.17900 10.90600 0.40500 +v 3.17900 11.21772 0.40500 +v 3.10430 11.30176 0.40500 +v 3.05710 11.26400 0.40500 +v 2.98465 11.26400 0.40500 +v 2.55582 11.31386 0.40500 +v 2.45588 11.27389 0.40500 +v 2.24054 11.29440 0.40500 +v 2.23068 11.32400 0.40500 +v 1.82524 11.32400 0.40500 +v 1.45290 11.27876 0.42000 +v 1.38354 11.29858 0.42000 +v 1.35955 11.29515 0.42000 +v 1.38832 11.26638 0.42000 +v 1.96832 10.88638 0.42000 +v 2.08705 10.76765 0.42000 +v 2.23939 10.70100 0.42000 +v 2.65630 10.70100 0.42000 +v 2.75455 10.79925 0.42000 +v 3.10179 10.87291 0.42000 +v 3.11232 10.86238 0.42000 +v 3.15419 10.92100 0.42000 +v 3.16400 10.92100 0.42000 +v 3.16400 11.21202 0.42000 +v 3.10255 11.28115 0.42000 +v 3.06237 11.24900 0.42000 +v 2.98378 11.24900 0.42000 +v 2.55786 11.29853 0.42000 +v 2.45807 11.25861 0.42000 +v 2.22940 11.28039 0.42000 +v 2.21986 11.30900 0.42000 +v 1.82585 11.30900 0.42000 +v 3.17100 11.44751 0.38000 +v 3.17100 11.37751 0.38000 +v 3.11327 11.31978 0.38000 +v 3.20100 11.22230 0.38000 +v 3.20100 10.89660 0.38000 +v 3.14982 10.86589 0.38000 +v 3.13204 10.82145 0.38000 +v 3.22984 10.72365 0.38000 +v 3.27984 10.63365 0.38000 +v 3.32500 10.58849 0.38000 +v 3.36005 10.62353 0.38000 +v 3.54807 11.00947 0.38000 +v 3.28027 11.39629 0.38000 +v 3.20474 11.48126 0.38000 +v 3.17100 11.44751 0.39000 +v 3.17100 11.37751 0.39000 +v 3.11327 11.31978 0.39000 +v 3.20100 11.22230 0.39000 +v 3.20100 10.89660 0.39000 +v 3.14982 10.86589 0.39000 +v 3.13204 10.82145 0.39000 +v 3.22984 10.72365 0.39000 +v 3.27984 10.63365 0.39000 +v 3.32500 10.58849 0.39000 +v 3.36005 10.62353 0.39000 +v 3.54807 11.00947 0.39000 +v 3.28027 11.39629 0.39000 +v 3.20474 11.48126 0.39000 +v 3.20100 11.43509 0.42000 +v 3.20100 11.36509 0.42000 +v 3.15461 11.31870 0.42000 +v 3.23100 11.23381 0.42000 +v 3.23100 10.87962 0.42000 +v 3.17392 10.84537 0.42000 +v 3.16724 10.82867 0.42000 +v 3.25402 10.74189 0.42000 +v 3.30402 10.65189 0.42000 +v 3.32500 10.63091 0.42000 +v 3.33528 10.64119 0.42000 +v 3.51341 11.00683 0.42000 +v 3.25664 11.37771 0.42000 +v 3.20346 11.43755 0.42000 +v 2.38749 11.32400 0.38000 +v 2.25100 11.32400 0.38000 +v 2.25100 11.31546 0.38000 +v 2.45469 11.29606 0.38000 +v 2.50204 11.30553 0.38000 +v 2.51651 11.32000 0.38000 +v 2.49251 11.34400 0.38000 +v 2.40749 11.34400 0.38000 +v 2.38749 11.32400 0.41625 +v 2.25100 11.32400 0.41625 +v 2.25100 11.31546 0.41625 +v 2.45469 11.29606 0.41625 +v 2.50204 11.30553 0.41625 +v 2.51651 11.32000 0.41625 +v 2.49251 11.34400 0.41625 +v 2.40749 11.34400 0.41625 +v 2.38904 11.32025 0.42000 +v 2.25475 11.32025 0.42000 +v 2.25475 11.31887 0.42000 +v 2.45450 11.29984 0.42000 +v 2.50019 11.30898 0.42000 +v 2.51121 11.32000 0.42000 +v 2.49096 11.34025 0.42000 +v 2.40904 11.34025 0.42000 +v 1.44345 10.11004 0.38000 +v 1.47695 10.07653 0.38000 +v 2.47900 10.28488 0.38000 +v 2.47900 10.31751 0.38000 +v 2.45100 10.34552 0.38000 +v 2.01549 10.69194 0.38000 +v 1.44345 10.11004 0.40500 +v 1.47695 10.07653 0.40500 +v 2.47900 10.28488 0.40500 +v 2.47900 10.31751 0.40500 +v 2.45100 10.34552 0.40500 +v 2.01549 10.69194 0.40500 +v 1.46457 10.11013 0.42000 +v 1.48183 10.09287 0.42000 +v 2.46400 10.29708 0.42000 +v 2.46400 10.31130 0.42000 +v 2.44099 10.33431 0.42000 +v 2.01672 10.67180 0.42000 +v 2.03394 10.71046 0.38000 +v 2.45903 10.36446 0.38000 +v 2.50100 10.32249 0.38000 +v 2.50100 10.28286 0.38000 +v 3.02458 9.85807 0.38000 +v 3.06007 9.89355 0.38000 +v 3.31770 10.40882 0.38000 +v 3.25355 10.47296 0.38000 +v 3.20549 10.45373 0.38000 +v 2.76198 10.55453 0.38000 +v 2.65251 10.66400 0.38000 +v 2.23366 10.66400 0.38000 +v 2.06626 10.74278 0.38000 +v 2.03394 10.71046 0.40500 +v 2.45903 10.36446 0.40500 +v 2.50100 10.32249 0.40500 +v 2.50100 10.28286 0.40500 +v 3.02458 9.85807 0.40500 +v 3.06007 9.89355 0.40500 +v 3.31770 10.40882 0.40500 +v 3.25355 10.47296 0.40500 +v 3.20549 10.45373 0.40500 +v 2.76198 10.55453 0.40500 +v 2.65251 10.66400 0.40500 +v 2.23366 10.66400 0.40500 +v 2.06626 10.74278 0.40500 +v 2.05630 10.71160 0.42000 +v 2.46909 10.37561 0.42000 +v 2.51600 10.32870 0.42000 +v 2.51600 10.29000 0.42000 +v 3.02353 9.87823 0.42000 +v 3.04774 9.90244 0.42000 +v 3.29945 10.40585 0.42000 +v 3.24994 10.45536 0.42000 +v 3.20673 10.43807 0.42000 +v 2.75444 10.54087 0.42000 +v 2.64630 10.64900 0.42000 +v 2.23031 10.64900 0.42000 +v 2.06941 10.72471 0.42000 +v 0.14500 10.74000 0.00000 +v 0.01500 9.45000 0.00000 +v 0.06500 9.41000 0.00000 +v 0.29500 9.68000 0.00000 +v 0.40500 9.57000 0.00000 +v 0.64500 9.04000 0.00000 +v 1.91500 7.33000 0.00000 +v 1.95500 7.37000 0.00000 +v 1.97500 7.95000 0.00000 +v 2.08500 8.06000 0.00000 +v 2.34500 7.72000 0.00000 +v 2.64500 8.24000 0.00000 +v 2.81500 8.44000 0.00000 +v 1.42500 10.09000 0.00000 +v 2.05500 10.74000 0.00000 +v 1.42500 11.18000 0.00000 +v 1.29500 11.31000 0.00000 +v 0.14500 10.74000 0.32000 +v 0.01500 9.45000 0.32000 +v 0.06500 9.41000 0.32000 +v 0.29500 9.68000 0.32000 +v 0.40500 9.57000 0.32000 +v 0.64500 9.04000 0.32000 +v 1.91500 7.33000 0.32000 +v 1.95500 7.37000 0.32000 +v 1.97500 7.95000 0.32000 +v 2.08500 8.06000 0.32000 +v 2.34500 7.72000 0.32000 +v 2.64500 8.24000 0.32000 +v 2.81500 8.44000 0.32000 +v 1.42500 10.09000 0.32000 +v 2.05500 10.74000 0.32000 +v 1.42500 11.18000 0.32000 +v 1.29500 11.31000 0.32000 +v 0.28763 10.82461 0.32000 +v 0.18855 10.77507 0.32000 +v 0.15078 10.73730 0.32000 +v 0.04091 9.50877 0.32000 +v 0.02186 9.45162 0.32000 +v 0.05537 9.41812 0.32000 +v 0.11038 9.46396 0.32000 +v 0.17024 9.56372 0.32000 +v 0.29500 9.68849 0.32000 +v 0.30100 9.68249 0.32000 +v 0.31929 9.79226 0.32000 +v 0.62047 10.24402 0.32000 +v 0.69054 10.30408 0.32000 +v 0.74005 10.37339 0.32000 +v 1.29032 11.21380 0.32000 +v 1.33651 11.26000 0.32000 +v 1.29375 11.30276 0.32000 +v 0.28763 10.82461 0.35250 +v 0.18855 10.77507 0.35250 +v 0.15078 10.73730 0.35250 +v 0.04091 9.50877 0.35250 +v 0.02186 9.45162 0.35250 +v 0.05537 9.41812 0.35250 +v 0.11038 9.46396 0.35250 +v 0.17024 9.56372 0.35250 +v 0.29500 9.68849 0.35250 +v 0.30100 9.68249 0.35250 +v 0.31929 9.79226 0.35250 +v 0.62047 10.24402 0.35250 +v 0.69054 10.30408 0.35250 +v 0.74005 10.37339 0.35250 +v 1.29032 11.21380 0.35250 +v 1.33651 11.26000 0.35250 +v 1.29375 11.30276 0.35250 +v 0.29092 10.81786 0.36000 +v 0.19300 10.76890 0.36000 +v 0.15801 10.73392 0.36000 +v 0.04831 9.50723 0.36000 +v 0.03045 9.45365 0.36000 +v 0.05583 9.42826 0.36000 +v 0.10460 9.46891 0.36000 +v 0.16428 9.56837 0.36000 +v 0.29500 9.69909 0.36000 +v 0.29600 9.69810 0.36000 +v 0.31216 9.79508 0.36000 +v 0.61480 10.24904 0.36000 +v 0.68497 10.30918 0.36000 +v 0.73386 10.37762 0.36000 +v 1.28447 11.21856 0.36000 +v 1.32591 11.26000 0.36000 +v 1.29219 11.29372 0.36000 +v 1.30967 11.20618 0.32000 +v 0.70162 10.28912 0.32000 +v 0.72037 10.23287 0.32000 +v 0.92707 9.96712 0.32000 +v 1.31396 10.10600 0.32000 +v 1.36623 10.10600 0.32000 +v 1.42253 10.08176 0.32000 +v 1.41773 10.09112 0.32000 +v 2.04658 10.73993 0.32000 +v 2.00118 10.78534 0.32000 +v 1.52149 11.10513 0.32000 +v 1.34550 11.24201 0.32000 +v 1.30967 11.20618 0.35625 +v 0.70162 10.28912 0.35625 +v 0.72037 10.23287 0.35625 +v 0.92707 9.96712 0.35625 +v 1.31396 10.10600 0.35625 +v 1.36623 10.10600 0.35625 +v 1.42253 10.08176 0.35625 +v 1.41773 10.09112 0.35625 +v 2.04658 10.73993 0.35625 +v 2.00118 10.78534 0.35625 +v 1.52149 11.10513 0.35625 +v 1.34550 11.24201 0.35625 +v 1.31259 11.20380 0.36000 +v 0.70575 10.28857 0.36000 +v 0.72372 10.23467 0.36000 +v 0.92836 9.97156 0.36000 +v 1.31330 10.10975 0.36000 +v 1.36700 10.10975 0.36000 +v 1.41470 10.08908 0.36000 +v 1.41317 10.09180 0.36000 +v 2.04132 10.73989 0.36000 +v 1.99879 10.78242 0.36000 +v 1.51930 11.10209 0.36000 +v 1.34581 11.23702 0.36000 +v 0.95766 9.96456 0.32000 +v 0.93100 9.94679 0.32000 +v 0.93100 9.93188 0.32000 +v 1.56943 9.01414 0.32000 +v 1.61710 8.97600 0.32000 +v 1.64032 8.97600 0.32000 +v 1.64881 9.00999 0.32000 +v 1.40900 9.95925 0.32000 +v 1.40900 10.05660 0.32000 +v 1.36334 10.08400 0.32000 +v 1.31597 10.08400 0.32000 +v 0.95766 9.96456 0.34500 +v 0.93100 9.94679 0.34500 +v 0.93100 9.93188 0.34500 +v 1.56943 9.01414 0.34500 +v 1.61710 8.97600 0.34500 +v 1.64032 8.97600 0.34500 +v 1.64881 9.00999 0.34500 +v 1.40900 9.95925 0.34500 +v 1.40900 10.05660 0.34500 +v 1.36334 10.08400 0.34500 +v 1.31597 10.08400 0.34500 +v 0.96431 9.95097 0.36000 +v 0.94600 9.93876 0.36000 +v 0.94600 9.93659 0.36000 +v 1.58050 9.02449 0.36000 +v 1.62237 8.99100 0.36000 +v 1.62860 8.99100 0.36000 +v 1.63335 9.00997 0.36000 +v 1.39400 9.95739 0.36000 +v 1.39400 10.04811 0.36000 +v 1.35918 10.06900 0.36000 +v 1.31841 10.06900 0.36000 +v 0.63948 10.23590 0.32000 +v 0.34073 9.78778 0.32000 +v 0.32139 9.66209 0.32000 +v 0.41002 9.57347 0.32000 +v 0.65021 9.04305 0.32000 +v 1.77974 7.50368 0.32000 +v 1.91543 7.33892 0.32000 +v 1.94905 7.37253 0.32000 +v 1.95901 7.90065 0.32000 +v 1.96947 7.95296 0.32000 +v 2.03841 8.02189 0.32000 +v 1.94939 8.40763 0.32000 +v 1.74969 8.71716 0.32000 +v 1.65045 8.94542 0.32000 +v 1.58135 8.97504 0.32000 +v 1.52026 9.04631 0.32000 +v 0.92951 9.88738 0.32000 +v 0.90955 9.95723 0.32000 +v 0.69974 10.22699 0.32000 +v 0.68243 10.27026 0.32000 +v 0.63948 10.23590 0.33000 +v 0.34073 9.78778 0.33000 +v 0.32139 9.66209 0.33000 +v 0.41002 9.57347 0.33000 +v 0.65021 9.04305 0.33000 +v 1.77974 7.50368 0.33000 +v 1.91543 7.33892 0.33000 +v 1.94905 7.37253 0.33000 +v 1.95901 7.90065 0.33000 +v 1.96947 7.95296 0.33000 +v 2.03841 8.02189 0.33000 +v 1.94939 8.40763 0.33000 +v 1.74969 8.71716 0.33000 +v 1.65045 8.94542 0.33000 +v 1.58135 8.97504 0.33000 +v 1.52026 9.04631 0.33000 +v 0.92951 9.88738 0.33000 +v 0.90955 9.95723 0.33000 +v 0.69974 10.22699 0.33000 +v 0.68243 10.27026 0.33000 +v 0.66185 10.21538 0.36000 +v 0.36937 9.77666 0.36000 +v 0.35336 9.67256 0.36000 +v 0.43509 9.59082 0.36000 +v 0.67625 9.05827 0.36000 +v 1.80343 7.52211 0.36000 +v 1.91758 7.38349 0.36000 +v 1.91928 7.38519 0.36000 +v 1.92907 7.90390 0.36000 +v 1.94184 7.96775 0.36000 +v 2.00544 8.03135 0.36000 +v 1.92134 8.39577 0.36000 +v 1.72316 8.70295 0.36000 +v 1.62768 8.92254 0.36000 +v 1.56312 8.95021 0.36000 +v 1.49654 9.02789 0.36000 +v 0.90205 9.87427 0.36000 +v 0.88230 9.94340 0.36000 +v 0.67346 10.21191 0.36000 +v 0.66960 10.22157 0.36000 +v 1.43100 10.05629 0.32000 +v 1.43100 9.96075 0.32000 +v 1.67115 9.01015 0.32000 +v 1.66127 8.96073 0.32000 +v 1.77028 8.72289 0.32000 +v 1.97060 8.41239 0.32000 +v 2.05836 8.04185 0.32000 +v 2.08500 8.06849 0.32000 +v 2.19987 7.95361 0.32000 +v 2.27997 7.80343 0.32000 +v 2.34529 7.72878 0.32000 +v 2.39012 7.77361 0.32000 +v 2.64008 8.24352 0.32000 +v 2.79002 8.40347 0.32000 +v 2.80788 8.43918 0.32000 +v 1.53056 9.97595 0.32000 +v 1.44382 10.06270 0.32000 +v 1.43100 10.05629 0.35250 +v 1.43100 9.96075 0.35250 +v 1.67115 9.01015 0.35250 +v 1.66127 8.96073 0.35250 +v 1.77028 8.72289 0.35250 +v 1.97060 8.41239 0.35250 +v 2.05836 8.04185 0.35250 +v 2.08500 8.06849 0.35250 +v 2.19987 7.95361 0.35250 +v 2.27997 7.80343 0.35250 +v 2.34529 7.72878 0.35250 +v 2.39012 7.77361 0.35250 +v 2.64008 8.24352 0.35250 +v 2.79002 8.40347 0.35250 +v 2.80788 8.43918 0.35250 +v 1.53056 9.97595 0.35250 +v 1.44382 10.06270 0.35250 +v 1.43850 10.05166 0.36000 +v 1.43850 9.96168 0.36000 +v 1.67884 9.01035 0.36000 +v 1.66910 8.96165 0.36000 +v 1.77687 8.72651 0.36000 +v 1.97760 8.41537 0.36000 +v 2.06256 8.05665 0.36000 +v 2.08500 8.07909 0.36000 +v 2.20597 7.95813 0.36000 +v 2.28618 7.80772 0.36000 +v 2.34566 7.73975 0.36000 +v 2.38403 7.77812 0.36000 +v 2.63392 8.24792 0.36000 +v 2.78381 8.40780 0.36000 +v 2.79898 8.43815 0.36000 +v 1.52501 9.97089 0.36000 +v 1.44233 10.05357 0.36000 +v 0.53500 8.22000 0.00000 +v 0.42500 8.11000 0.00000 +v 0.36500 8.17000 0.00000 +v -1.53500 8.34000 0.00000 +v -1.57500 8.30000 0.00000 +v -1.03500 7.24000 0.00000 +v -0.89500 7.10000 0.00000 +v 0.80500 5.84000 0.00000 +v 0.86500 5.90000 0.00000 +v 0.91500 5.85000 0.00000 +v 2.56500 5.75000 0.00000 +v 2.60500 5.79000 0.00000 +v 2.45500 5.97000 0.00000 +v 2.96500 7.41000 0.00000 +v 2.77500 8.45000 0.00000 +v 2.73500 8.49000 0.00000 +v 2.68500 8.44000 0.00000 +v 2.47500 8.04000 0.00000 +v 2.33500 7.89000 0.00000 +v 2.22500 8.00000 0.00000 +v 2.01500 8.35000 0.00000 +v 1.96500 8.40000 0.00000 +v 1.92500 8.36000 0.00000 +v 1.93500 7.60000 0.00000 +v 1.82500 7.49000 0.00000 +v 0.81500 8.83000 0.00000 +v 0.53500 8.22000 0.51000 +v 0.42500 8.11000 0.51000 +v 0.36500 8.17000 0.51000 +v -1.53500 8.34000 0.51000 +v -1.57500 8.30000 0.51000 +v -1.03500 7.24000 0.51000 +v -0.89500 7.10000 0.51000 +v 0.80500 5.84000 0.51000 +v 0.86500 5.90000 0.51000 +v 0.91500 5.85000 0.51000 +v 2.56500 5.75000 0.51000 +v 2.60500 5.79000 0.51000 +v 2.45500 5.97000 0.51000 +v 2.96500 7.41000 0.51000 +v 2.77500 8.45000 0.51000 +v 2.73500 8.49000 0.51000 +v 2.68500 8.44000 0.51000 +v 2.47500 8.04000 0.51000 +v 2.33500 7.89000 0.51000 +v 2.22500 8.00000 0.51000 +v 2.01500 8.35000 0.51000 +v 1.96500 8.40000 0.51000 +v 1.92500 8.36000 0.51000 +v 1.93500 7.60000 0.51000 +v 1.82500 7.49000 0.51000 +v 0.81500 8.83000 0.51000 +v 0.75030 8.72710 0.51000 +v 0.54017 8.21680 0.51000 +v 0.47509 8.14242 0.51000 +v 0.51837 8.12511 0.51000 +v 0.59938 8.04411 0.51000 +v 0.89031 7.71305 0.51000 +v 0.90932 7.65600 0.51000 +v 0.94749 7.65600 0.51000 +v 1.02938 7.57410 0.51000 +v 1.81923 6.67428 0.51000 +v 1.88722 6.61600 0.51000 +v 1.91941 6.61600 0.51000 +v 1.93900 6.89021 0.51000 +v 1.93900 7.28903 0.51000 +v 1.86238 7.51889 0.51000 +v 1.82500 7.48151 0.51000 +v 1.70041 7.60611 0.51000 +v 0.92026 8.69631 0.51000 +v 0.81545 8.82018 0.51000 +v 0.75030 8.72710 0.53500 +v 0.54017 8.21680 0.53500 +v 0.47509 8.14242 0.53500 +v 0.51837 8.12511 0.53500 +v 0.59938 8.04411 0.53500 +v 0.89031 7.71305 0.53500 +v 0.90932 7.65600 0.53500 +v 0.94749 7.65600 0.53500 +v 1.02938 7.57410 0.53500 +v 1.81923 6.67428 0.53500 +v 1.88722 6.61600 0.53500 +v 1.91941 6.61600 0.53500 +v 1.93900 6.89021 0.53500 +v 1.93900 7.28903 0.53500 +v 1.86238 7.51889 0.53500 +v 1.82500 7.48151 0.53500 +v 1.70041 7.60611 0.53500 +v 0.92026 8.69631 0.53500 +v 0.81545 8.82018 0.53500 +v 0.76353 8.71986 0.55000 +v 0.55309 8.20879 0.55000 +v 0.50033 8.14849 0.55000 +v 0.52680 8.13790 0.55000 +v 0.61033 8.05437 0.55000 +v 0.90358 7.72067 0.55000 +v 0.92014 7.67100 0.55000 +v 0.95370 7.67100 0.55000 +v 1.04033 7.58437 0.55000 +v 1.82980 6.68497 0.55000 +v 1.89277 6.63100 0.55000 +v 1.90545 6.63100 0.55000 +v 1.92400 6.89075 0.55000 +v 1.92400 7.28659 0.55000 +v 1.85582 7.49112 0.55000 +v 1.82500 7.46030 0.55000 +v 1.68893 7.59637 0.55000 +v 0.90842 8.68709 0.55000 +v 0.81657 8.79563 0.55000 +v -1.56768 8.29884 0.51000 +v -1.03009 7.24357 0.51000 +v -0.96500 7.17849 0.51000 +v -0.91885 7.22463 0.51000 +v -0.05822 7.81507 0.51000 +v 0.35163 8.05498 0.51000 +v 0.43050 8.11086 0.51000 +v 0.42382 8.10270 0.51000 +v 0.36230 8.16422 0.51000 +v -1.53274 8.33377 0.51000 +v -1.56768 8.29884 0.52000 +v -1.03009 7.24357 0.52000 +v -0.96500 7.17849 0.52000 +v -0.91885 7.22463 0.52000 +v -0.05822 7.81507 0.52000 +v 0.35163 8.05498 0.52000 +v 0.43050 8.11086 0.52000 +v 0.42382 8.10270 0.52000 +v 0.36230 8.16422 0.52000 +v -1.53274 8.33377 0.52000 +v -1.53105 8.29304 0.55000 +v -1.00551 7.26142 0.55000 +v -0.96500 7.22091 0.55000 +v -0.93811 7.24781 0.55000 +v -0.07430 7.84042 0.55000 +v 0.33535 8.08021 0.55000 +v 0.49318 8.16932 0.55000 +v 0.42603 8.05805 0.55000 +v 0.34878 8.13531 0.55000 +v -1.52144 8.30264 0.55000 +v 0.41898 8.07537 0.51000 +v 0.03820 7.85492 0.51000 +v -0.91087 7.20555 0.51000 +v -0.94664 7.16084 0.51000 +v -0.77129 7.01472 0.51000 +v 0.80441 5.84790 0.51000 +v 0.84903 5.89252 0.51000 +v 0.86900 7.34022 0.51000 +v 0.88888 7.66827 0.51000 +v 0.82028 7.76627 0.51000 +v 0.58063 8.03588 0.51000 +v 0.51176 8.10476 0.51000 +v 0.45707 8.12298 0.51000 +v 0.41898 8.07537 0.52000 +v 0.03820 7.85492 0.52000 +v -0.91087 7.20555 0.52000 +v -0.94664 7.16084 0.52000 +v -0.77129 7.01472 0.52000 +v 0.80441 5.84790 0.52000 +v 0.84903 5.89252 0.52000 +v 0.86900 7.34022 0.52000 +v 0.88888 7.66827 0.52000 +v 0.82028 7.76627 0.52000 +v 0.58063 8.03588 0.52000 +v 0.51176 8.10476 0.52000 +v 0.45707 8.12298 0.52000 +v 0.43889 8.05223 0.55000 +v 0.05421 7.82952 0.55000 +v -0.89024 7.18332 0.55000 +v -0.90484 7.16507 0.55000 +v -0.75275 7.03832 0.55000 +v 0.80149 5.88740 0.55000 +v 0.81921 5.90512 0.55000 +v 0.83902 7.34134 0.55000 +v 0.85831 7.65964 0.55000 +v 0.79671 7.74764 0.55000 +v 0.55880 8.01529 0.55000 +v 0.49555 8.07853 0.55000 +v 0.46743 8.08791 0.55000 +v 2.68989 8.43640 0.51000 +v 2.47994 8.03650 0.51000 +v 2.35100 7.89764 0.51000 +v 2.35100 7.76170 0.51000 +v 2.69637 7.19924 0.51000 +v 2.87133 7.35476 0.51000 +v 2.95840 7.41281 0.51000 +v 2.77933 8.41761 0.51000 +v 2.73423 8.48075 0.51000 +v 2.68989 8.43640 0.52000 +v 2.47994 8.03650 0.52000 +v 2.35100 7.89764 0.52000 +v 2.35100 7.76170 0.52000 +v 2.69637 7.19924 0.52000 +v 2.87133 7.35476 0.52000 +v 2.95840 7.41281 0.52000 +v 2.77933 8.41761 0.52000 +v 2.73423 8.48075 0.52000 +v 2.71433 8.41842 0.55000 +v 2.50464 8.01902 0.55000 +v 2.38100 7.88586 0.55000 +v 2.38100 7.77017 0.55000 +v 2.70320 7.24545 0.55000 +v 2.85295 7.37857 0.55000 +v 2.92542 7.42688 0.55000 +v 2.75098 8.40569 0.55000 +v 2.73041 8.43449 0.55000 +v 1.93103 8.35755 0.51000 +v 1.94103 7.59755 0.51000 +v 1.88100 7.53751 0.51000 +v 1.88100 7.51106 0.51000 +v 1.96100 7.29106 0.51000 +v 1.96100 6.88979 0.51000 +v 1.94137 6.61502 0.51000 +v 1.99323 6.60638 0.51000 +v 2.67720 7.19123 0.51000 +v 2.32900 7.75830 0.51000 +v 2.32900 7.88751 0.51000 +v 2.22024 7.99628 0.51000 +v 2.01024 8.34628 0.51000 +v 1.96500 8.39151 0.51000 +v 1.93103 8.35755 0.52000 +v 1.94103 7.59755 0.52000 +v 1.88100 7.53751 0.52000 +v 1.88100 7.51106 0.52000 +v 1.96100 7.29106 0.52000 +v 1.96100 6.88979 0.52000 +v 1.94137 6.61502 0.52000 +v 1.99323 6.60638 0.52000 +v 2.67720 7.19123 0.52000 +v 2.32900 7.75830 0.52000 +v 2.32900 7.88751 0.52000 +v 2.22024 7.99628 0.52000 +v 2.01024 8.34628 0.52000 +v 1.96500 8.39151 0.52000 +v 1.96120 8.34529 0.55000 +v 1.97120 7.58529 0.55000 +v 1.91100 7.52509 0.55000 +v 1.91100 7.51634 0.55000 +v 1.99100 7.29634 0.55000 +v 1.99100 6.88872 0.55000 +v 1.97324 6.64012 0.55000 +v 1.98436 6.63827 0.55000 +v 2.63823 7.19737 0.55000 +v 2.29900 7.74983 0.55000 +v 2.29900 7.87509 0.55000 +v 2.19641 7.97768 0.55000 +v 1.98641 8.32768 0.55000 +v 1.96500 8.34909 0.55000 +v 0.89100 7.33976 0.51000 +v 0.87103 5.90245 0.51000 +v 0.91763 5.85585 0.51000 +v 2.56267 5.75615 0.51000 +v 2.59670 5.79018 0.51000 +v 1.94026 6.50629 0.51000 +v 1.91900 6.53818 0.51000 +v 1.91900 6.59400 0.51000 +v 1.88275 6.59400 0.51000 +v 1.80075 6.66574 0.51000 +v 1.01062 7.56590 0.51000 +v 0.94251 7.63400 0.51000 +v 0.91061 7.63400 0.51000 +v 0.89100 7.33976 0.53500 +v 0.87103 5.90245 0.53500 +v 0.91763 5.85585 0.53500 +v 2.56267 5.75615 0.53500 +v 2.59670 5.79018 0.53500 +v 1.94026 6.50629 0.53500 +v 1.91900 6.53818 0.53500 +v 1.91900 6.59400 0.53500 +v 1.88275 6.59400 0.53500 +v 1.80075 6.66574 0.53500 +v 1.01062 7.56590 0.53500 +v 0.94251 7.63400 0.53500 +v 0.91061 7.63400 0.53500 +v 0.90599 7.33916 0.55000 +v 0.88612 5.90858 0.55000 +v 0.92422 5.87048 0.55000 +v 2.55683 5.77153 0.55000 +v 2.57593 5.79063 0.55000 +v 1.92843 6.49700 0.55000 +v 1.90400 6.53364 0.55000 +v 1.90400 6.57900 0.55000 +v 1.87711 6.57900 0.55000 +v 1.79013 6.65511 0.55000 +v 0.99967 7.55563 0.55000 +v 0.93630 7.61900 0.55000 +v 0.92465 7.61900 0.55000 +v 1.99676 6.58362 0.51000 +v 1.94100 6.59292 0.51000 +v 1.94100 6.54182 0.51000 +v 1.95975 6.51368 0.51000 +v 2.38921 6.03430 0.51000 +v 2.45419 5.97859 0.51000 +v 2.48965 6.02291 0.51000 +v 2.84932 7.10193 0.51000 +v 2.92978 7.33327 0.51000 +v 2.94487 7.34836 0.51000 +v 2.91586 7.36286 0.51000 +v 1.99676 6.58362 0.53500 +v 1.94100 6.59292 0.53500 +v 1.94100 6.54182 0.53500 +v 1.95975 6.51368 0.53500 +v 2.38921 6.03430 0.53500 +v 2.45419 5.97859 0.53500 +v 2.48965 6.02291 0.53500 +v 2.84932 7.10193 0.53500 +v 2.92978 7.33327 0.53500 +v 2.94487 7.34836 0.53500 +v 2.91586 7.36286 0.53500 +v 2.00115 6.56768 0.55000 +v 1.95600 6.57521 0.55000 +v 1.95600 6.54636 0.55000 +v 1.97164 6.52290 0.55000 +v 2.39972 6.04504 0.55000 +v 2.45217 6.00008 0.55000 +v 2.47626 6.03019 0.55000 +v 2.83512 7.10677 0.55000 +v 2.91675 7.34145 0.55000 +v 2.91955 7.34425 0.55000 +v 2.91801 7.34502 0.55000 +v -4.19500 7.99000 0.00000 +v -4.23500 8.02000 0.00000 +v -4.27500 7.98000 0.00000 +v -4.41500 6.48000 0.00000 +v -4.37500 6.44000 0.00000 +v -4.39500 6.41000 0.00000 +v -3.17500 5.22000 0.00000 +v 0.22500 5.16000 0.00000 +v 0.26500 5.20000 0.00000 +v 0.23500 5.24000 0.00000 +v -0.50500 6.11000 0.00000 +v -0.53500 6.09000 0.00000 +v -0.58500 6.14000 0.00000 +v -2.16500 5.86000 0.00000 +v -2.26500 5.76000 0.00000 +v -3.41500 6.54000 0.00000 +v -3.55500 6.68000 0.00000 +v -3.68500 7.26000 0.00000 +v -3.08500 7.93000 0.00000 +v -2.86500 8.94000 0.00000 +v -2.91500 8.98000 0.00000 +v -4.19500 7.99000 0.22000 +v -4.23500 8.02000 0.22000 +v -4.27500 7.98000 0.22000 +v -4.41500 6.48000 0.22000 +v -4.37500 6.44000 0.22000 +v -4.39500 6.41000 0.22000 +v -3.17500 5.22000 0.22000 +v 0.22500 5.16000 0.22000 +v 0.26500 5.20000 0.22000 +v 0.23500 5.24000 0.22000 +v -0.50500 6.11000 0.22000 +v -0.53500 6.09000 0.22000 +v -0.58500 6.14000 0.22000 +v -2.16500 5.86000 0.22000 +v -2.26500 5.76000 0.22000 +v -3.41500 6.54000 0.22000 +v -3.55500 6.68000 0.22000 +v -3.68500 7.26000 0.22000 +v -3.08500 7.93000 0.22000 +v -2.86500 8.94000 0.22000 +v -2.91500 8.98000 0.22000 +v -1.43138 5.37521 0.22000 +v -1.54305 5.29400 0.22000 +v -1.55900 5.29400 0.22000 +v -1.55900 5.25492 0.22000 +v -1.51249 5.24562 0.22000 +v -1.47292 5.21594 0.22000 +v 0.22259 5.16607 0.22000 +v 0.25708 5.20056 0.22000 +v 0.23031 5.23625 0.22000 +v -0.50583 6.10172 0.22000 +v -1.43138 5.37521 0.24500 +v -1.54305 5.29400 0.24500 +v -1.55900 5.29400 0.24500 +v -1.55900 5.25492 0.24500 +v -1.51249 5.24562 0.24500 +v -1.47292 5.21594 0.24500 +v 0.22259 5.16607 0.24500 +v 0.25708 5.20056 0.24500 +v 0.23031 5.23625 0.24500 +v -0.50583 6.10172 0.24500 +v -1.42234 5.36324 0.26000 +v -1.53817 5.27900 0.26000 +v -1.54400 5.27900 0.26000 +v -1.54400 5.26722 0.26000 +v -1.50621 5.25966 0.26000 +v -1.46773 5.23080 0.26000 +v 0.21656 5.18126 0.26000 +v 0.23727 5.20197 0.26000 +v 0.21859 5.22688 0.26000 +v -0.50792 6.08101 0.26000 +v -2.16184 5.85447 0.22000 +v -2.21629 5.79094 0.22000 +v -1.69147 5.39485 0.22000 +v -1.59259 5.32564 0.22000 +v -1.54652 5.31642 0.22000 +v -0.59898 6.05451 0.22000 +v -0.55349 6.10000 0.22000 +v -0.58703 6.13355 0.22000 +v -2.16184 5.85447 0.23000 +v -2.21629 5.79094 0.23000 +v -1.69147 5.39485 0.23000 +v -1.59259 5.32564 0.23000 +v -1.54652 5.31642 0.23000 +v -0.59898 6.05451 0.23000 +v -0.55349 6.10000 0.23000 +v -0.58703 6.13355 0.23000 +v -2.14604 5.82680 0.26000 +v -2.17274 5.79566 0.26000 +v -1.67383 5.41912 0.26000 +v -1.58054 5.35382 0.26000 +v -1.55411 5.34854 0.26000 +v -0.61888 6.07703 0.26000 +v -0.59591 6.10000 0.26000 +v -0.59719 6.10128 0.26000 +v -3.51881 6.54001 0.22000 +v -3.21875 5.34977 0.22000 +v -3.23814 5.29162 0.22000 +v -3.17244 5.22593 0.22000 +v -1.51741 5.17608 0.22000 +v -1.49230 5.20118 0.22000 +v -1.49931 5.21521 0.22000 +v -1.58035 5.24560 0.22000 +v -1.59051 5.30656 0.22000 +v -2.10868 5.67526 0.22000 +v -2.23466 5.78185 0.22000 +v -2.26382 5.75270 0.22000 +v -2.28804 5.76481 0.22000 +v -3.41870 6.53526 0.22000 +v -3.50158 6.60893 0.22000 +v -3.51881 6.54001 0.25250 +v -3.21875 5.34977 0.25250 +v -3.23814 5.29162 0.25250 +v -3.17244 5.22593 0.25250 +v -1.51741 5.17608 0.25250 +v -1.49230 5.20118 0.25250 +v -1.49931 5.21521 0.25250 +v -1.58035 5.24560 0.25250 +v -1.59051 5.30656 0.25250 +v -2.10868 5.67526 0.25250 +v -2.23466 5.78185 0.25250 +v -2.26382 5.75270 0.25250 +v -2.28804 5.76481 0.25250 +v -3.41870 6.53526 0.25250 +v -3.50158 6.60893 0.25250 +v -3.51108 6.54001 0.26000 +v -3.21095 5.34947 0.26000 +v -3.22955 5.29365 0.26000 +v -3.16924 5.23333 0.26000 +v -1.52042 5.18367 0.26000 +v -1.50143 5.20267 0.26000 +v -1.50470 5.20922 0.26000 +v -1.58704 5.24010 0.26000 +v -1.59739 5.30225 0.26000 +v -2.11329 5.66933 0.26000 +v -2.23424 5.77167 0.26000 +v -2.26233 5.74357 0.26000 +v -2.29185 5.75833 0.26000 +v -3.42332 6.52933 0.26000 +v -3.49731 6.59510 0.26000 +v -3.77922 7.18785 0.22000 +v -3.80012 7.15651 0.22000 +v -4.32048 6.47604 0.22000 +v -4.38649 6.41003 0.22000 +v -3.55081 5.58429 0.22000 +v -3.27181 5.31526 0.22000 +v -3.24955 5.30784 0.22000 +v -3.24115 5.34985 0.22000 +v -3.54111 6.53968 0.22000 +v -3.53066 6.61283 0.22000 +v -3.51349 6.63000 0.22000 +v -3.56048 6.67699 0.22000 +v -3.68040 7.21664 0.22000 +v -3.72761 7.25440 0.22000 +v -3.76985 7.26285 0.22000 +v -3.77922 7.18785 0.24500 +v -3.80012 7.15651 0.24500 +v -4.32048 6.47604 0.24500 +v -4.38649 6.41003 0.24500 +v -3.55081 5.58429 0.24500 +v -3.27181 5.31526 0.24500 +v -3.24955 5.30784 0.24500 +v -3.24115 5.34985 0.24500 +v -3.54111 6.53968 0.24500 +v -3.53066 6.61283 0.24500 +v -3.51349 6.63000 0.24500 +v -3.56048 6.67699 0.24500 +v -3.68040 7.21664 0.24500 +v -3.72761 7.25440 0.24500 +v -3.76985 7.26285 0.24500 +v -3.76478 7.18248 0.26000 +v -3.78790 7.14778 0.26000 +v -4.30917 6.46614 0.26000 +v -4.36521 6.41009 0.26000 +v -3.54033 5.59503 0.26000 +v -3.26385 5.32842 0.26000 +v -3.26093 5.32744 0.26000 +v -3.25652 5.34947 0.26000 +v -3.55637 6.53888 0.26000 +v -3.54480 6.61990 0.26000 +v -3.53470 6.63000 0.26000 +v -3.57417 6.66947 0.26000 +v -3.69390 7.20822 0.26000 +v -3.73413 7.24041 0.26000 +v -3.75696 7.24498 0.26000 +v -4.26940 7.97712 0.22000 +v -4.27904 7.91927 0.22000 +v -4.38908 6.53880 0.22000 +v -4.40814 6.48162 0.22000 +v -4.38251 6.45600 0.22000 +v -4.36749 6.45600 0.22000 +v -4.33952 6.48396 0.22000 +v -3.82022 7.16305 0.22000 +v -3.79118 7.23082 0.22000 +v -3.80077 7.29797 0.22000 +v -3.99024 7.61707 0.22000 +v -4.18757 7.99201 0.22000 +v -4.20618 7.98270 0.22000 +v -4.23500 8.01151 0.22000 +v -4.26940 7.97712 0.24500 +v -4.27904 7.91927 0.24500 +v -4.38908 6.53880 0.24500 +v -4.40814 6.48162 0.24500 +v -4.38251 6.45600 0.24500 +v -4.36749 6.45600 0.24500 +v -4.33952 6.48396 0.24500 +v -3.82022 7.16305 0.24500 +v -3.79118 7.23082 0.24500 +v -3.80077 7.29797 0.24500 +v -3.99024 7.61707 0.24500 +v -4.18757 7.99201 0.24500 +v -4.20618 7.98270 0.24500 +v -4.23500 8.01151 0.24500 +v -4.25539 7.96991 0.26000 +v -4.26414 7.91743 0.26000 +v -4.37427 6.53578 0.26000 +v -4.39097 6.48567 0.26000 +v -4.37630 6.47100 0.26000 +v -4.37370 6.47100 0.26000 +v -4.35083 6.49386 0.26000 +v -3.83327 7.17068 0.26000 +v -3.80662 7.23286 0.26000 +v -3.81520 7.29288 0.26000 +v -4.00333 7.60974 0.26000 +v -4.19400 7.97202 0.26000 +v -4.20915 7.96445 0.26000 +v -4.23500 7.99030 0.26000 +v -4.16738 8.00829 0.22000 +v -3.96975 7.62292 0.22000 +v -3.76113 7.27522 0.22000 +v -3.68708 7.25671 0.22000 +v -3.09056 7.93277 0.22000 +v -2.89064 8.91235 0.22000 +v -2.87304 8.93875 0.22000 +v -2.91507 8.97237 0.22000 +v -4.16738 8.00829 0.23000 +v -3.96975 7.62292 0.23000 +v -3.76113 7.27522 0.23000 +v -3.68708 7.25671 0.23000 +v -3.09056 7.93277 0.23000 +v -2.89064 8.91235 0.23000 +v -2.87304 8.93875 0.23000 +v -2.91507 8.97237 0.23000 +v -4.12929 7.99976 0.26000 +v -3.94351 7.63749 0.26000 +v -3.74180 7.30131 0.26000 +v -3.69751 7.29024 0.26000 +v -3.11835 7.94661 0.26000 +v -2.91886 8.92410 0.26000 +v -2.91326 8.93251 0.26000 +v -2.91543 8.93424 0.26000 +v 3.54500 6.10000 0.00000 +v 2.45500 6.04000 0.00000 +v 2.41500 6.00000 0.00000 +v 2.45500 5.93000 0.00000 +v 2.34500 5.82000 0.00000 +v 1.88500 5.82000 0.00000 +v 1.84500 5.78000 0.00000 +v 2.96500 5.14000 0.00000 +v 3.07500 5.25000 0.00000 +v 4.35500 5.18000 0.00000 +v 4.39500 5.22000 0.00000 +v 4.35500 5.27000 0.00000 +v 3.57500 6.13000 0.00000 +v 3.54500 6.10000 0.51000 +v 2.45500 6.04000 0.51000 +v 2.41500 6.00000 0.51000 +v 2.45500 5.93000 0.51000 +v 2.34500 5.82000 0.51000 +v 1.88500 5.82000 0.51000 +v 1.84500 5.78000 0.51000 +v 2.96500 5.14000 0.51000 +v 3.07500 5.25000 0.51000 +v 4.35500 5.18000 0.51000 +v 4.39500 5.22000 0.51000 +v 4.35500 5.27000 0.51000 +v 3.57500 6.13000 0.51000 +v 2.45765 6.03416 0.51000 +v 2.42248 5.99900 0.51000 +v 2.46223 5.92944 0.51000 +v 2.42432 5.88205 0.51000 +v 2.51834 5.83504 0.51000 +v 2.58957 5.77399 0.51000 +v 2.89827 5.29600 0.51000 +v 2.92210 5.29600 0.51000 +v 3.52857 6.06155 0.51000 +v 3.52015 6.10367 0.51000 +v 2.45765 6.03416 0.53500 +v 2.42248 5.99900 0.53500 +v 2.46223 5.92944 0.53500 +v 2.42432 5.88205 0.53500 +v 2.51834 5.83504 0.53500 +v 2.58957 5.77399 0.53500 +v 2.89827 5.29600 0.53500 +v 2.92210 5.29600 0.53500 +v 3.52857 6.06155 0.53500 +v 3.52015 6.10367 0.53500 +v 2.46426 6.01956 0.55000 +v 2.44119 5.99649 0.55000 +v 2.48031 5.92803 0.55000 +v 2.44763 5.88717 0.55000 +v 2.52670 5.84763 0.55000 +v 2.60098 5.78396 0.55000 +v 2.90644 5.31100 0.55000 +v 2.91485 5.31100 0.55000 +v 3.51250 6.06542 0.55000 +v 3.50802 6.08784 0.55000 +v 2.34749 5.81400 0.51000 +v 1.88749 5.81400 0.51000 +v 1.85405 5.78056 0.51000 +v 1.93831 5.71502 0.51000 +v 2.96395 5.14744 0.51000 +v 3.02503 5.20852 0.51000 +v 2.88080 5.28544 0.51000 +v 2.57066 5.76566 0.51000 +v 2.40581 5.87233 0.51000 +v 2.34749 5.81400 0.52000 +v 1.88749 5.81400 0.52000 +v 1.85405 5.78056 0.52000 +v 1.93831 5.71502 0.52000 +v 2.96395 5.14744 0.52000 +v 3.02503 5.20852 0.52000 +v 2.88080 5.28544 0.52000 +v 2.57066 5.76566 0.52000 +v 2.40581 5.87233 0.52000 +v 2.35991 5.78400 0.55000 +v 1.89991 5.78400 0.55000 +v 1.89929 5.78338 0.55000 +v 1.95489 5.74014 0.55000 +v 2.95871 5.18462 0.55000 +v 2.97519 5.20110 0.55000 +v 2.85982 5.26263 0.55000 +v 2.54895 5.74398 0.55000 +v 2.40988 5.83397 0.55000 +v 3.55100 6.09751 0.51000 +v 3.55100 6.05800 0.51000 +v 3.51974 6.01632 0.51000 +v 2.94100 5.29788 0.51000 +v 2.94100 5.28321 0.51000 +v 3.02682 5.22600 0.51000 +v 3.04251 5.22600 0.51000 +v 3.07251 5.25600 0.51000 +v 3.14517 5.25600 0.51000 +v 4.35266 5.18615 0.51000 +v 4.38696 5.22045 0.51000 +v 4.35043 5.26611 0.51000 +v 3.57479 6.12130 0.51000 +v 3.55100 6.09751 0.53500 +v 3.55100 6.05800 0.53500 +v 3.51974 6.01632 0.53500 +v 2.94100 5.29788 0.53500 +v 2.94100 5.28321 0.53500 +v 3.02682 5.22600 0.53500 +v 3.04251 5.22600 0.53500 +v 3.07251 5.25600 0.53500 +v 3.14517 5.25600 0.53500 +v 4.35266 5.18615 0.53500 +v 4.38696 5.22045 0.53500 +v 4.35043 5.26611 0.53500 +v 3.57479 6.12130 0.53500 +v 3.56600 6.09130 0.55000 +v 3.56600 6.05300 0.55000 +v 3.53158 6.00711 0.55000 +v 2.95600 5.29259 0.55000 +v 2.95600 5.29124 0.55000 +v 3.03136 5.24100 0.55000 +v 3.03630 5.24100 0.55000 +v 3.06630 5.27100 0.55000 +v 3.14561 5.27100 0.55000 +v 4.34681 5.20151 0.55000 +v 4.36686 5.22156 0.55000 +v 4.33901 5.25637 0.55000 +v 3.57426 6.09956 0.55000 +v -2.58500 7.37000 0.00000 +v -2.88500 6.25000 0.00000 +v -2.23500 5.75000 0.00000 +v -0.94500 7.09000 0.00000 +v -1.51500 8.18000 0.00000 +v -1.64500 8.25000 0.00000 +v -2.58500 7.37000 0.45000 +v -2.88500 6.25000 0.45000 +v -2.23500 5.75000 0.45000 +v -0.94500 7.09000 0.45000 +v -1.51500 8.18000 0.45000 +v -1.64500 8.25000 0.45000 +v -2.57965 7.36679 0.45000 +v -2.84918 6.36854 0.45000 +v -2.87841 6.24189 0.45000 +v -2.83117 6.19465 0.45000 +v -2.40139 5.90480 0.45000 +v -2.28350 5.80656 0.45000 +v -2.24965 5.81502 0.45000 +v -2.21077 5.93167 0.45000 +v -2.05081 6.58151 0.45000 +v -1.63083 8.12144 0.45000 +v -1.61157 8.20809 0.45000 +v -1.64514 8.24165 0.45000 +v -2.57965 7.36679 0.46000 +v -2.84918 6.36854 0.46000 +v -2.87841 6.24189 0.46000 +v -2.83117 6.19465 0.46000 +v -2.40139 5.90480 0.46000 +v -2.28350 5.80656 0.46000 +v -2.24965 5.81502 0.46000 +v -2.21077 5.93167 0.46000 +v -2.05081 6.58151 0.46000 +v -1.63083 8.12144 0.46000 +v -1.61157 8.20809 0.46000 +v -1.64514 8.24165 0.46000 +v -2.55292 7.35072 0.49000 +v -2.82007 6.36126 0.49000 +v -2.84544 6.25135 0.49000 +v -2.81200 6.21791 0.49000 +v -2.38336 5.92883 0.49000 +v -2.27600 5.83936 0.49000 +v -2.27290 5.84013 0.49000 +v -2.23961 5.94001 0.49000 +v -2.07985 6.58904 0.49000 +v -1.65996 8.12865 0.49000 +v -1.64443 8.19852 0.49000 +v -1.64583 8.19991 0.49000 +v -1.59961 8.18691 0.45000 +v -2.20929 5.85812 0.45000 +v -2.23028 5.80565 0.45000 +v -2.26487 5.78836 0.45000 +v -2.23486 5.75834 0.45000 +v -2.08923 5.89426 0.45000 +v -0.95231 7.09102 0.45000 +v -1.51989 8.17641 0.45000 +v -1.56500 8.22151 0.45000 +v -1.59961 8.18691 0.47500 +v -2.20929 5.85812 0.47500 +v -2.23028 5.80565 0.47500 +v -2.26487 5.78836 0.47500 +v -2.23486 5.75834 0.47500 +v -2.08923 5.89426 0.47500 +v -0.95231 7.09102 0.47500 +v -1.51989 8.17641 0.47500 +v -1.56500 8.22151 0.47500 +v -1.58613 8.17918 0.49000 +v -2.19502 5.85342 0.49000 +v -2.21847 5.79479 0.49000 +v -2.23955 5.78425 0.49000 +v -2.23450 5.77920 0.49000 +v -2.09979 5.90492 0.49000 +v -0.97057 7.09358 0.49000 +v -1.53212 8.16742 0.49000 +v -1.56500 8.20030 0.49000 +v -0.89500 12.23000 0.00000 +v 0.02500 11.32000 0.00000 +v 1.39500 11.27000 0.00000 +v 1.44500 11.31000 0.00000 +v -0.85500 12.27000 0.00000 +v -0.89500 12.23000 0.29000 +v 0.02500 11.32000 0.29000 +v 1.39500 11.27000 0.29000 +v 1.44500 11.31000 0.29000 +v -0.85500 12.27000 0.29000 +v -0.88649 12.23002 0.29000 +v 0.02756 11.32591 0.29000 +v 1.39299 11.27608 0.29000 +v 1.43551 11.31009 0.29000 +v 1.38188 11.35479 0.29000 +v -0.85357 12.26294 0.29000 +v -0.88649 12.23002 0.31500 +v 0.02756 11.32591 0.31500 +v 1.39299 11.27608 0.31500 +v 1.43551 11.31009 0.31500 +v 1.38188 11.35479 0.31500 +v -0.85357 12.26294 0.31500 +v -0.86522 12.23008 0.33000 +v 0.03395 11.34069 0.33000 +v 1.38797 11.29127 0.33000 +v 1.41180 11.31033 0.33000 +v 1.37407 11.34177 0.33000 +v -0.85000 12.24530 0.33000 +v 0.12500 12.48000 0.00000 +v 0.62500 11.76000 0.00000 +v 1.38500 11.69000 0.00000 +v 1.43500 11.73000 0.00000 +v 0.15500 12.54000 0.00000 +v 0.12500 12.48000 0.03000 +v 0.62500 11.76000 0.03000 +v 1.38500 11.69000 0.03000 +v 1.43500 11.73000 0.03000 +v 0.15500 12.54000 0.03000 +v 0.12230 12.49882 0.03000 +v 0.13019 12.48303 0.03000 +v 0.57966 11.81383 0.03000 +v 0.62771 11.76578 0.03000 +v 1.38314 11.69620 0.03000 +v 1.42540 11.73000 0.03000 +v 1.33153 11.80509 0.03000 +v 0.15588 12.53240 0.03000 +v 0.12230 12.49882 0.05500 +v 0.13019 12.48303 0.05500 +v 0.57966 11.81383 0.05500 +v 0.62771 11.76578 0.05500 +v 1.38314 11.69620 0.05500 +v 1.42540 11.73000 0.05500 +v 1.33153 11.80509 0.05500 +v 0.15588 12.53240 0.05500 +v 0.14055 12.49585 0.07000 +v 0.14318 12.49059 0.07000 +v 0.59129 11.82340 0.07000 +v 0.63448 11.78022 0.07000 +v 1.37849 11.71169 0.07000 +v 1.40138 11.73000 0.07000 +v 1.32287 11.79281 0.07000 +v 0.15809 12.51339 0.07000 +v -3.39500 11.45000 0.00000 +v -3.37500 11.42000 0.00000 +v -2.53500 10.37000 0.00000 +v -2.64500 10.26000 0.00000 +v -3.55500 10.60000 0.00000 +v -3.60500 10.56000 0.00000 +v -2.17500 8.70000 0.00000 +v -0.17500 8.27000 0.00000 +v 0.42500 8.20000 0.00000 +v 0.46500 8.24000 0.00000 +v -0.63500 10.08000 0.00000 +v -0.60500 10.11000 0.00000 +v -0.64500 10.15000 0.00000 +v -3.35500 11.49000 0.00000 +v -3.39500 11.45000 0.49000 +v -3.37500 11.42000 0.49000 +v -2.53500 10.37000 0.49000 +v -2.64500 10.26000 0.49000 +v -3.55500 10.60000 0.49000 +v -3.60500 10.56000 0.49000 +v -2.17500 8.70000 0.49000 +v -0.17500 8.27000 0.49000 +v 0.42500 8.20000 0.49000 +v 0.46500 8.24000 0.49000 +v -0.63500 10.08000 0.49000 +v -0.60500 10.11000 0.49000 +v -0.64500 10.15000 0.49000 +v -3.35500 11.49000 0.49000 +v -3.38728 11.44924 0.49000 +v -3.37015 11.42354 0.49000 +v -2.52696 10.36955 0.49000 +v -2.57597 10.32055 0.49000 +v -2.35190 10.14519 0.49000 +v -1.32217 9.69531 0.49000 +v -1.21325 9.62600 0.49000 +v -1.18682 9.62600 0.49000 +v -1.12859 9.66481 0.49000 +v -0.64931 10.06422 0.49000 +v -0.61304 10.10955 0.49000 +v -0.64854 10.14506 0.49000 +v -3.35380 11.48271 0.49000 +v -3.38728 11.44924 0.50000 +v -3.37015 11.42354 0.50000 +v -2.52696 10.36955 0.50000 +v -2.57597 10.32055 0.50000 +v -2.35190 10.14519 0.50000 +v -1.32217 9.69531 0.50000 +v -1.21325 9.62600 0.50000 +v -1.18682 9.62600 0.50000 +v -1.12859 9.66481 0.50000 +v -0.64931 10.06422 0.50000 +v -0.61304 10.10955 0.50000 +v -0.64854 10.14506 0.50000 +v -3.35380 11.48271 0.50000 +v -3.34868 11.44541 0.53000 +v -3.34591 11.44127 0.53000 +v -2.48676 10.36733 0.53000 +v -2.53080 10.32329 0.53000 +v -2.33641 10.17116 0.53000 +v -1.30803 9.72187 0.53000 +v -1.20452 9.65600 0.53000 +v -1.19590 9.65600 0.53000 +v -1.14657 9.68889 0.53000 +v -0.67084 10.08533 0.53000 +v -0.65324 10.10733 0.53000 +v -0.66627 10.12036 0.53000 +v -3.34781 11.44628 0.53000 +v -3.59728 10.55924 0.49000 +v -3.58010 10.53347 0.49000 +v -2.29046 8.82395 0.49000 +v -2.22467 8.75816 0.49000 +v -1.23247 9.60152 0.49000 +v -1.24947 9.63553 0.49000 +v -1.32754 9.67456 0.49000 +v -2.35810 10.12481 0.49000 +v -2.58488 10.30229 0.49000 +v -2.64382 10.25317 0.49000 +v -3.56347 10.59304 0.49000 +v -3.59728 10.55924 0.50000 +v -3.58010 10.53347 0.50000 +v -2.29046 8.82395 0.50000 +v -2.22467 8.75816 0.50000 +v -1.23247 9.60152 0.50000 +v -1.24947 9.63553 0.50000 +v -1.32754 9.67456 0.50000 +v -2.35810 10.12481 0.50000 +v -2.58488 10.30229 0.50000 +v -2.64382 10.25317 0.50000 +v -3.56347 10.59304 0.50000 +v -3.55868 10.55541 0.53000 +v -3.55563 10.55084 0.53000 +v -2.26777 8.84368 0.53000 +v -2.22302 8.79893 0.53000 +v -1.26982 9.60915 0.53000 +v -1.27183 9.61317 0.53000 +v -1.34027 9.64738 0.53000 +v -2.37359 10.09884 0.53000 +v -2.58429 10.26373 0.53000 +v -2.63795 10.21901 0.53000 +v -3.55585 10.55824 0.53000 +v -1.12140 9.64519 0.49000 +v -1.18318 9.60400 0.49000 +v -1.21251 9.60400 0.49000 +v -1.33095 9.48556 0.49000 +v -2.16074 8.79574 0.49000 +v -2.20688 8.74037 0.49000 +v -2.17201 8.70549 0.49000 +v -0.22400 8.28592 0.49000 +v 0.42283 8.20631 0.49000 +v 0.45770 8.24118 0.49000 +v 0.43972 8.27714 0.49000 +v -0.54996 9.96659 0.49000 +v -0.62585 10.06145 0.49000 +v -1.12140 9.64519 0.50000 +v -1.18318 9.60400 0.50000 +v -1.21251 9.60400 0.50000 +v -1.33095 9.48556 0.50000 +v -2.16074 8.79574 0.50000 +v -2.20688 8.74037 0.50000 +v -2.17201 8.70549 0.50000 +v -0.22400 8.28592 0.50000 +v 0.42283 8.20631 0.50000 +v 0.45770 8.24118 0.50000 +v 0.43972 8.27714 0.50000 +v -0.54996 9.96659 0.50000 +v -0.62585 10.06145 0.50000 +v -1.10337 9.62115 0.53000 +v -1.17410 9.57400 0.53000 +v -1.20009 9.57400 0.53000 +v -1.31071 9.46337 0.53000 +v -2.13945 8.77442 0.53000 +v -2.16630 8.74221 0.53000 +v -2.15705 8.73296 0.53000 +v -0.21900 8.31553 0.53000 +v 0.41196 8.23788 0.53000 +v 0.42120 8.24711 0.53000 +v 0.41333 8.26283 0.53000 +v -0.57474 9.94955 0.53000 +v -0.63008 10.01872 0.53000 +vn 0.0000 -0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +vn -0.8370 -0.5472 0.0000 +vn -0.7071 0.7071 0.0000 +vn 0.3830 0.9237 0.0000 +vn -0.6854 0.7282 0.0000 +vn -0.7037 -0.7105 0.0000 +vn 0.0000 -1.0000 0.0000 +vn -0.7071 -0.7071 0.0000 +vn -0.9960 0.0894 0.0000 +vn 0.7071 0.7071 0.0000 +vn 0.8000 0.6000 0.0000 +vn 0.7071 -0.7071 0.0000 +vn 0.4432 0.8964 0.0000 +vn -0.7809 -0.6247 0.0000 +vn 0.3560 0.9345 0.0000 +vn -0.7993 -0.6010 0.0000 +vn -0.6936 0.7203 0.0000 +vn -0.6097 0.7926 0.0000 +vn 0.6000 0.8000 0.0000 +vn -0.9957 0.0929 0.0000 +vn -0.8321 0.5547 0.0000 +vn -0.7044 -0.7098 0.0000 +vn -0.4981 0.8671 0.0000 +vn -0.5834 0.8122 0.0000 +vn -0.6709 0.7415 0.0000 +vn -0.7593 0.6508 0.0000 +vn -1.0000 0.0000 0.0000 +vn -0.5440 -0.8391 0.0000 +vn -0.5448 0.8386 0.0000 +vn -0.9637 -0.2670 0.0000 +vn -0.7282 -0.6854 0.0000 +vn 0.4741 -0.8805 0.0000 +vn 0.6402 -0.7682 0.0000 +vn 0.6064 -0.7951 0.0000 +vn 0.6757 -0.7372 0.0000 +vn -0.9759 0.2180 0.0000 +vn 0.3618 -0.9323 0.0000 +vn 0.6585 -0.7526 0.0000 +vn -0.9829 0.1843 0.0000 +vn 0.3417 -0.9398 0.0000 +vn 0.7526 -0.6585 0.0000 +vn 0.9766 -0.2149 0.0000 +vn 0.6887 -0.7250 0.0000 +vn -0.9285 -0.3714 0.0000 +vn 0.4084 -0.9128 0.0000 +vn 0.9719 -0.2356 0.0000 +vn -0.7406 -0.6720 0.0000 +vn -0.5070 -0.8619 0.0000 +vn -0.1713 -0.9852 0.0000 +vn -0.9596 0.2813 0.0000 +vn 0.3610 -0.9326 0.0000 +vn 0.9600 -0.2800 0.0000 +vn -0.6247 -0.7809 0.0000 +vn 0.2950 -0.9555 0.0000 +vn 0.2562 -0.9666 0.0000 +vn 0.7241 -0.6897 0.0000 +vn 0.9403 -0.3404 0.0000 +vn 1.0000 0.0000 0.0000 +vn -0.2460 0.9693 0.0000 +vn 0.6709 0.7415 0.0000 +vn 0.9648 -0.2631 0.0000 +vn 0.2585 -0.9660 0.0000 +vn 0.6854 -0.7282 0.0000 +vn 0.9413 -0.3375 0.0000 +vn 0.6823 0.7311 0.0000 +vn -0.2261 0.9741 0.0000 +vn 0.2623 -0.9650 0.0000 +vn 0.9274 -0.3742 0.0000 +vn -0.2147 0.9767 0.0000 +vn -0.2649 0.9643 0.0000 +vn -0.3096 0.9509 0.0000 +vn 0.5340 0.8455 0.0000 +vn 0.2316 0.9728 0.0000 +vn -0.0688 0.9976 0.0000 +vn -0.3032 0.9529 0.0000 +vn -0.4930 0.8700 0.0000 +vn -0.6839 0.7295 0.0000 +vn 0.9531 -0.3026 0.0000 +vn 0.6306 0.7761 0.0000 +vn -0.3321 0.9432 0.0000 +vn -0.9712 0.2384 0.0000 +vn 0.9373 0.3485 0.0000 +vn 0.7295 0.6839 0.0000 +vn -0.3958 0.9183 0.0000 +vn 0.9574 -0.2887 0.0000 +vn -0.0320 -0.9995 0.0000 +vn 0.7567 0.6538 0.0000 +vn -0.5547 0.8321 0.0000 +vn -0.1843 0.9829 0.0000 +vn 0.7226 -0.6912 0.0000 +vn -0.5940 -0.8045 0.0000 +vn -0.0549 -0.9985 0.0000 +vn -0.6332 -0.7740 0.0000 +vn -0.4842 -0.8750 0.0000 +vn -0.0546 -0.9985 0.0000 +vn 0.7386 0.6741 0.0000 +vn -0.0483 0.9988 0.0000 +vn 0.9468 -0.3217 0.0000 +vn 0.9829 0.1843 0.0000 +vn 0.9191 -0.3939 0.0000 +vn 0.7669 0.6418 0.0000 +vn 0.7250 -0.6887 0.0000 +vn 0.1927 -0.9812 0.0000 +vn -0.6373 -0.7706 0.0000 +vn 0.8944 -0.4472 0.0000 +vn 0.7474 0.6644 0.0000 +vn 0.9487 0.3162 0.0000 +vn 0.8164 0.5775 0.0000 +vn 0.1390 0.9903 0.0000 +vn 0.5586 0.8294 0.0000 +vn 0.0222 0.9998 0.0000 +vn 0.6508 0.7593 0.0000 +vn 0.5261 0.8504 0.0000 +vn -0.5501 0.8351 0.0000 +vn -0.6332 0.7740 0.0000 +vn -0.7747 0.6324 0.0000 +vn -0.9839 0.1789 0.0000 +vn -0.6757 -0.7372 0.0000 +vn -0.5444 -0.8388 0.0000 +vn -0.6178 -0.7863 0.0000 +vn -0.5865 0.8099 0.0000 +vn -0.5487 0.8360 0.0000 +vn -0.9864 0.1644 0.0000 +vn -0.9582 -0.2860 0.0000 +vn -0.9114 -0.4116 0.0000 +vn 0.2425 -0.9701 0.0000 +vn 0.6380 -0.7700 0.0000 +vn 0.6094 -0.7929 0.0000 +vn 0.6720 -0.7406 0.0000 +vn 0.7562 -0.6544 0.0000 +vn 0.9939 0.1104 0.0000 +vn 0.6139 0.7894 0.0000 +vn 0.5221 0.8529 0.0000 +vn 0.6663 -0.7457 0.0000 +vn 0.5570 -0.8305 0.0000 +vn 0.4650 -0.8853 0.0000 +vn 0.9588 0.2841 0.0000 +vn 0.9255 0.3786 0.0000 +vn 0.3511 0.9363 0.0000 +vn -0.4390 0.8985 0.0000 +vn -0.3890 0.5905 0.7071 +vn -0.4478 0.5473 0.7071 +vn -0.5000 0.5000 0.7071 +vn -0.5478 0.4472 0.7071 +vn -0.6957 0.1265 0.7071 +vn -0.4778 -0.5212 0.7071 +vn -0.3850 -0.5931 0.7071 +vn -0.4369 -0.5560 0.7071 +vn -0.4147 0.5727 0.7071 +vn -0.3880 0.5912 0.7071 +vn -0.6975 0.1163 0.7071 +vn -0.6776 -0.2023 0.7071 +vn -0.6445 -0.2910 0.7071 +vn 0.1715 -0.6860 0.7071 +vn 0.4511 -0.5445 0.7071 +vn 0.4309 -0.5606 0.7071 +vn 0.4752 -0.5237 0.7071 +vn 0.5347 -0.4627 0.7071 +vn 0.7028 0.0781 0.7071 +vn 0.5000 0.5000 0.7071 +vn 0.4341 0.5582 0.7071 +vn 0.3692 0.6031 0.7071 +vn 0.4712 -0.5273 0.7071 +vn 0.3938 -0.5873 0.7071 +vn 0.3288 -0.6260 0.7071 +vn 0.6780 0.2009 0.7071 +vn 0.6544 0.2677 0.7071 +vn 0.2483 0.6621 0.7071 +vn -0.3104 0.6353 0.7071 +vn -0.6402 0.7682 0.0000 +vn -0.9753 0.2209 0.0000 +vn -0.9138 -0.4061 0.0000 +vn 0.3557 -0.9346 0.0000 +vn 0.4692 -0.8831 0.0000 +vn 0.9739 -0.2271 0.0000 +vn 0.9684 -0.2492 0.0000 +vn 0.1644 0.9864 0.0000 +vn -0.3497 0.9369 0.0000 +vn -0.4527 0.5432 0.7071 +vn -0.6897 0.1562 0.7071 +vn -0.6950 0.1303 0.7071 +vn -0.6462 -0.2872 0.7071 +vn 0.2515 -0.6609 0.7071 +vn 0.3317 -0.6245 0.7071 +vn 0.6886 -0.1606 0.7071 +vn 0.6848 -0.1762 0.7071 +vn 0.1158 0.6976 0.7070 +vn -0.2473 0.6625 0.7071 +vn -0.7682 0.6402 0.0000 +vn -0.9733 0.2294 0.0000 +vn -0.3511 -0.9363 0.0000 +vn 0.3304 -0.9439 0.0000 +vn 0.3779 -0.9258 0.0000 +vn 0.9439 -0.3304 0.0000 +vn 0.3162 0.9487 0.0000 +vn -0.3534 0.9355 0.0000 +vn -0.5433 0.4526 0.7071 +vn -0.6883 0.1622 0.7071 +vn -0.2481 -0.6622 0.7071 +vn 0.2336 -0.6674 0.7071 +vn 0.2672 -0.6547 0.7071 +vn 0.6674 -0.2336 0.7071 +vn 0.7071 0.0000 0.7071 +vn 0.2240 0.6706 0.7071 +vn -0.2499 0.6615 0.7071 +vn -0.9716 0.2365 0.0000 +vn 0.9317 0.3633 0.0000 +vn 0.0767 0.9971 0.0000 +vn -0.4158 0.9095 0.0000 +vn -0.9736 0.2283 0.0000 +vn -0.9759 0.2181 0.0000 +vn 0.3766 -0.9264 0.0000 +vn 0.9728 -0.2315 0.0000 +vn -0.9248 -0.3805 0.0000 +vn -0.2169 -0.9762 0.0000 +vn 0.4668 -0.8844 0.0000 +vn 0.9684 -0.2495 0.0000 +vn 0.9572 -0.2893 0.0000 +vn 0.9950 -0.0995 0.0000 +vn 0.3939 0.9191 0.0000 +vn -0.3532 0.9356 0.0000 +vn -0.7105 0.1742 0.6818 +vn 0.6809 0.2669 0.6819 +vn 0.0542 0.7050 0.7071 +vn -0.2940 0.6431 0.7071 +vn -0.6885 0.1614 0.7071 +vn -0.6901 0.1542 0.7071 +vn -0.7071 0.0000 0.7071 +vn 0.0000 -0.7071 0.7071 +vn 0.2663 -0.6551 0.7071 +vn 0.7083 -0.1697 0.6852 +vn -0.6731 -0.2781 0.6852 +vn -0.1533 -0.6903 0.7071 +vn 0.3300 -0.6253 0.7071 +vn 0.6847 -0.1764 0.7071 +vn 0.6769 -0.2046 0.7071 +vn 0.7036 -0.0704 0.7071 +vn 0.2785 0.6499 0.7071 +vn -0.2497 0.6615 0.7071 +vn -0.4399 0.8981 0.0000 +vn -0.5882 0.8087 0.0000 +vn -0.7139 0.7002 0.0000 +vn -0.8104 0.5859 0.0000 +vn -0.8817 0.4719 0.0000 +vn -0.9363 0.3511 0.0000 +vn -0.9710 0.2390 0.0000 +vn -0.9953 0.0971 0.0000 +vn -0.9978 -0.0665 0.0000 +vn -0.9628 -0.2703 0.0000 +vn -0.8638 -0.5039 0.0000 +vn -0.6364 -0.7714 0.0000 +vn -0.3096 -0.9509 0.0000 +vn 0.0651 -0.9979 0.0000 +vn 0.3311 -0.9436 0.0000 +vn -0.9594 0.2822 0.0000 +vn -0.9398 -0.3417 0.0000 +vn 0.9514 -0.3080 0.0000 +vn -0.1807 0.9835 0.0000 +vn -0.2484 0.9687 0.0000 +vn -0.9450 0.3271 0.0000 +vn -0.3846 -0.9231 0.0000 +vn 0.2382 -0.9712 0.0000 +vn -0.8575 -0.5145 0.0000 +vn -0.9310 0.3651 0.0000 +vn -0.7572 0.6532 0.0000 +vn -0.5665 0.8240 0.0000 +vn -0.3714 0.9285 0.0000 +vn 0.0278 0.9996 0.0000 +vn 0.6292 0.7772 0.0000 +vn 0.8638 0.5039 0.0000 +vn 0.9882 0.1533 0.0000 +vn 0.9867 -0.1624 0.0000 +vn 0.9390 -0.3439 0.0000 +vn 0.8584 -0.5130 0.0000 +vn 0.7203 -0.6936 0.0000 +vn 0.5623 -0.8269 0.0000 +vn 0.3824 -0.9240 0.0000 +vn 0.1047 -0.9945 0.0000 +vn -0.3011 -0.9536 0.0000 +vn -0.6585 -0.7526 0.0000 +vn -0.8480 -0.5300 0.0000 +vn 0.7782 -0.6280 0.0000 +vn 0.8376 0.5463 0.0000 +vn 0.6738 0.7390 0.0000 +vn 0.3796 0.9252 0.0000 +vn 0.0565 0.9984 0.0000 +vn -0.2309 0.9730 0.0000 +vn -0.3110 0.6350 0.7071 +vn -0.4159 0.5718 0.7071 +vn -0.5048 0.4951 0.7071 +vn -0.5730 0.4143 0.7071 +vn -0.6234 0.3337 0.7071 +vn -0.6621 0.2483 0.7071 +vn -0.6866 0.1690 0.7071 +vn -0.7038 0.0687 0.7071 +vn -0.7056 -0.0470 0.7071 +vn -0.6808 -0.1911 0.7071 +vn -0.6108 -0.3563 0.7071 +vn -0.4500 -0.5454 0.7071 +vn -0.2189 -0.6724 0.7071 +vn 0.0460 -0.7056 0.7071 +vn 0.2341 -0.6672 0.7071 +vn -0.6784 0.1995 0.7071 +vn -0.6645 -0.2417 0.7071 +vn 0.2553 -0.6594 0.7071 +vn 0.6727 -0.2178 0.7071 +vn 0.4243 0.5657 0.7071 +vn -0.1277 0.6955 0.7071 +vn -0.1756 0.6849 0.7071 +vn -0.6682 0.2313 0.7071 +vn -0.2719 -0.6527 0.7071 +vn 0.1684 -0.6867 0.7071 +vn -0.6063 -0.3638 0.7071 +vn -0.6583 0.2582 0.7071 +vn -0.5354 0.4619 0.7071 +vn -0.4006 0.5827 0.7071 +vn -0.2626 0.6565 0.7071 +vn 0.0196 0.7068 0.7071 +vn 0.4449 0.5496 0.7071 +vn 0.6108 0.3563 0.7071 +vn 0.6988 0.1084 0.7071 +vn 0.6977 -0.1148 0.7071 +vn 0.6640 -0.2432 0.7071 +vn 0.6070 -0.3627 0.7071 +vn 0.5093 -0.4905 0.7071 +vn 0.3976 -0.5847 0.7071 +vn 0.2703 -0.6534 0.7071 +vn 0.0740 -0.7032 0.7071 +vn -0.2129 -0.6743 0.7071 +vn -0.4657 -0.5322 0.7071 +vn -0.5996 -0.3748 0.7071 +vn 0.5503 -0.4441 0.7071 +vn 0.7028 0.0780 0.7071 +vn 0.5923 0.3863 0.7071 +vn 0.4764 0.5225 0.7071 +vn 0.2684 0.6542 0.7071 +vn 0.0400 0.7060 0.7071 +vn -0.1633 0.6880 0.7071 +vn -0.3125 0.9499 0.0000 +vn -0.9568 0.2909 0.0000 +vn -0.4061 -0.9138 0.0000 +vn 0.2998 -0.9540 0.0000 +vn -0.8000 -0.6000 0.0000 +vn -0.9636 0.2672 0.0000 +vn -0.9719 0.2353 0.0000 +vn -0.8944 -0.4472 0.0000 +vn 0.2778 -0.9607 0.0000 +vn 0.4640 -0.8858 0.0000 +vn 0.9372 -0.3487 0.0000 +vn 0.9138 0.4061 0.0000 +vn -0.2359 0.9718 0.0000 +vn -0.2747 0.9615 0.0000 +vn 0.7682 0.6402 0.0000 +vn 0.9625 -0.2713 0.0000 +vn 0.2728 -0.9621 0.0000 +vn 0.9210 -0.3896 0.0000 +vn 0.9578 0.2873 0.0000 +vn 0.0000 1.0000 0.0000 +vn -0.2305 0.9731 0.0000 +vn 0.9607 -0.2777 0.0000 +vn 0.2787 -0.9604 0.0000 +vn 0.9247 -0.3807 0.0000 +vn 0.9231 0.3846 0.0000 +vn -0.2402 0.9707 0.0000 +vn -0.2210 0.6717 0.7071 +vn -0.6765 0.2057 0.7071 +vn -0.2871 -0.6462 0.7071 +vn 0.2120 -0.6746 0.7071 +vn -0.5657 -0.4242 0.7071 +vn -0.6814 0.1889 0.7071 +vn -0.6873 0.1664 0.7071 +vn -0.6327 -0.3159 0.7071 +vn 0.1964 -0.6793 0.7071 +vn 0.3281 -0.6264 0.7071 +vn 0.6627 -0.2466 0.7071 +vn 0.6461 0.2873 0.7071 +vn -0.1668 0.6872 0.7071 +vn -0.1943 0.6799 0.7071 +vn 0.5432 0.4527 0.7071 +vn 0.6806 -0.1918 0.7071 +vn 0.1929 -0.6803 0.7071 +vn 0.6512 -0.2755 0.7071 +vn 0.6773 0.2031 0.7071 +vn 0.0000 0.7071 0.7071 +vn -0.1630 0.6881 0.7071 +vn 0.6793 -0.1964 0.7071 +vn 0.1971 -0.6791 0.7071 +vn 0.6539 -0.2692 0.7071 +vn 0.6527 0.2720 0.7071 +vn -0.1699 0.6864 0.7071 +vn -0.4619 -0.8869 0.0000 +vn -0.5480 -0.8365 0.0000 +vn -0.7181 0.6960 0.0000 +vn 0.1998 -0.9798 0.0000 +vn 0.2216 0.9751 0.0000 +vn 0.2075 -0.9782 0.0000 +vn -0.7612 -0.6485 0.0000 +vn 0.8990 -0.4380 0.0000 +vn 0.8080 0.5892 0.0000 +vn -0.9398 0.3417 0.0000 +vn 0.1961 0.9806 0.0000 +vn 0.0423 0.9991 0.0000 +vn 0.1241 -0.9923 0.0000 +vn -0.2748 -0.9615 0.0000 +vn 0.0808 -0.9967 0.0000 +vn -0.0278 -0.9996 0.0000 +vn 0.0500 -0.9988 0.0000 +vn 0.7068 -0.7074 0.0000 +vn 0.6097 0.7926 0.0000 +vn 0.5322 0.8466 0.0000 +vn -0.5002 -0.5002 0.7069 +vn -0.3268 -0.6274 0.7068 +vn 0.0878 -0.7019 0.7069 +vn -0.1943 -0.6801 0.7069 +vn 0.0572 -0.7049 0.7070 +vn -0.0196 -0.7067 0.7073 +vn 0.0353 -0.7063 0.7071 +vn 0.4999 -0.5002 0.7070 +vn 0.4311 0.5604 0.7072 +vn 0.3763 0.5986 0.7071 +vn 0.0299 0.7065 0.7071 +vn -0.5002 0.5002 0.7069 +vn 0.2747 0.9615 0.0000 +vn -0.1414 0.9900 0.0000 +vn -0.4008 -0.9162 0.0000 +vn -0.7073 -0.7069 0.0000 +vn 0.8137 -0.5812 0.0000 +vn -0.6247 0.7809 0.0000 +vn 0.1155 0.9933 0.0000 +vn -0.3713 0.9285 0.0000 +vn 0.0948 0.9955 0.0000 +vn 0.9487 0.3160 0.0000 +vn -0.0808 0.9967 0.0000 +vn 0.1943 0.6799 0.7071 +vn -0.1000 0.7000 0.7071 +vn -0.5000 -0.5000 0.7071 +vn -0.3875 -0.5915 0.7071 +vn -0.2834 -0.6478 0.7071 +vn 0.5000 -0.5000 0.7071 +vn 0.1467 -0.6917 0.7071 +vn -0.5001 -0.4999 0.7071 +vn 0.5754 -0.4110 0.7071 +vn 0.5285 0.4698 0.7071 +vn -0.4417 0.5521 0.7072 +vn 0.0817 0.7025 0.7070 +vn -0.2626 0.6566 0.7070 +vn 0.0670 0.7038 0.7072 +vn 0.6708 0.2236 0.7072 +vn -0.0571 0.7048 0.7071 +vn -0.7433 -0.6690 0.0000 +vn -0.5145 0.8575 0.0000 +vn -0.9284 0.3715 0.0000 +vn -0.8742 -0.4856 0.0000 +vn 0.7070 -0.7072 0.0000 +vn 0.8222 0.5692 0.0000 +vn -0.7072 0.7070 0.0000 +vn -0.5256 -0.4730 0.7071 +vn -0.3638 0.6064 0.7071 +vn -0.6565 0.2627 0.7071 +vn -0.6182 -0.3434 0.7071 +vn 0.4999 -0.5001 0.7071 +vn 0.6357 -0.3097 0.7071 +vn 0.5814 0.4025 0.7071 +vn -0.5001 0.5000 0.7071 +vn -0.6313 -0.7756 0.0000 +vn -0.6300 -0.7766 0.0000 +vn 0.7071 0.7072 0.0000 +vn -0.3715 0.9284 0.0000 +vn 0.4258 0.9048 0.0000 +vn -0.4464 -0.5484 0.7071 +vn -0.4456 -0.5492 0.7070 +vn 0.4999 -0.5000 0.7071 +vn 0.6325 -0.3162 0.7071 +vn -0.2627 0.6565 0.7071 +vn 0.1568 0.6897 0.7070 +vn 0.5001 0.5001 0.7070 +vn 0.3010 0.6397 0.7072 +vn -0.4999 0.4999 0.7072 +vn -0.0948 -0.9955 0.0000 +vn 0.1961 -0.9806 0.0000 +vn -0.0671 -0.7041 0.7069 +vn 0.1388 -0.6938 0.7066 +vn 0.5002 -0.5002 0.7069 +vn 0.5002 0.5002 0.7069 +vn -0.7072 -0.7070 0.0000 +vn 0.2036 -0.9791 0.0000 +vn 0.7072 0.7070 0.0000 +vn 0.6225 0.7826 0.0000 +vn -0.7131 0.7010 0.0000 +vn -0.5000 -0.4999 0.7071 +vn 0.1439 -0.6923 0.7071 +vn 0.5000 0.4999 0.7071 +vn 0.4402 0.5534 0.7071 +vn -0.5043 0.4958 0.7070 +vn -0.9950 0.1003 0.0000 +vn 0.7612 -0.6485 0.0000 +vn -0.9110 -0.4125 0.0000 +vn -0.8028 -0.5962 0.0000 +vn 0.9994 -0.0345 0.0000 +vn -0.7944 -0.6075 0.0000 +vn 0.8662 -0.4997 0.0000 +vn 0.7619 -0.6476 0.0000 +vn 0.7648 0.6443 0.0000 +vn 0.7181 -0.6960 0.0000 +vn 0.5726 0.8198 0.0000 +vn -0.4441 0.8960 0.0000 +vn -0.4472 0.8944 0.0000 +vn -0.9960 0.0891 0.0000 +vn -0.9487 0.3162 0.0000 +vn -0.7070 -0.7072 0.0000 +vn 0.8575 -0.5145 0.0000 +vn 0.9864 -0.1644 0.0000 +vn 0.8320 -0.5547 0.0000 +vn 0.6508 -0.7593 0.0000 +vn 0.8137 -0.5813 0.0000 +vn 0.8366 -0.5478 0.0000 +vn 0.7072 -0.7070 0.0000 +vn -0.4292 0.9032 0.0000 +vn -0.3160 0.6321 0.7075 +vn -0.4998 0.4998 0.7074 +vn -0.7041 0.0630 0.7073 +vn -0.6705 0.2235 0.7074 +vn -0.4999 -0.4999 0.7072 +vn 0.4527 -0.5432 0.7071 +vn 0.6062 -0.3637 0.7073 +vn -0.4994 -0.5001 0.7074 +vn 0.6976 -0.1162 0.7070 +vn 0.5884 -0.3922 0.7071 +vn 0.4602 -0.5369 0.7071 +vn 0.5755 -0.4111 0.7069 +vn 0.5916 -0.3874 0.7070 +vn 0.5001 -0.5000 0.7071 +vn -0.3035 0.6386 0.7072 +vn -0.9695 -0.2449 0.0000 +vn -0.9806 0.1960 0.0000 +vn -0.9091 -0.4167 0.0000 +vn -0.8403 -0.5421 0.0000 +vn -0.9731 -0.2305 0.0000 +vn -0.8823 -0.4706 0.0000 +vn -0.7526 -0.6585 0.0000 +vn 0.8829 -0.4696 0.0000 +vn 0.7296 -0.6839 0.0000 +vn 0.8944 -0.4473 0.0000 +vn 0.7690 0.6392 0.0000 +vn -0.6855 -0.1732 0.7072 +vn -0.6934 0.1387 0.7071 +vn -0.6428 -0.2946 0.7071 +vn -0.5942 -0.3834 0.7071 +vn -0.6882 -0.1630 0.7070 +vn -0.6238 -0.3327 0.7073 +vn -0.5321 -0.4656 0.7071 +vn 0.6243 -0.3321 0.7071 +vn 0.5160 -0.4837 0.7070 +vn 0.6327 -0.3163 0.7069 +vn 0.5437 0.4519 0.7072 +vn 0.4998 0.4998 0.7074 +vn -0.3161 0.6326 0.7070 +vn -0.8334 0.5526 0.0000 +vn -0.9487 -0.3162 0.0000 +vn -0.7893 -0.6140 0.0000 +vn 0.3379 -0.9412 0.0000 +vn -0.3955 -0.9185 0.0000 +vn 0.8898 0.4563 0.0000 +vn 0.5547 0.8321 0.0000 +vn -0.5895 0.3909 0.7069 +vn -0.6712 -0.2238 0.7067 +vn -0.5584 -0.4343 0.7068 +vn 0.2390 -0.6657 0.7069 +vn -0.2829 -0.6551 0.7006 +vn 0.6364 0.3335 0.6956 +vn 0.5079 -0.4922 0.7070 +vn 0.4999 0.4999 0.7072 +vn 0.3922 0.5884 0.7071 +vn 0.4343 0.5584 0.7068 +vn -0.5546 0.8321 0.0000 +vn -0.8209 -0.5711 0.0000 +vn -0.6247 -0.7808 0.0000 +vn 0.9702 -0.2423 0.0000 +vn 0.9695 0.2449 0.0000 +vn 0.5146 0.8575 0.0000 +vn -0.3162 0.9487 0.0000 +vn -0.3922 0.5884 0.7071 +vn -0.5805 -0.4038 0.7071 +vn -0.4417 -0.5521 0.7071 +vn 0.6860 -0.1715 0.7071 +vn 0.6857 0.1732 0.7070 +vn 0.3638 0.6063 0.7071 +vn -0.2236 0.6708 0.7071 +vn -0.8320 0.5547 0.0000 +vn -0.9884 0.1521 0.0000 +vn -0.8062 -0.5916 0.0000 +vn -0.7719 -0.6357 0.0000 +vn 0.9998 -0.0189 0.0000 +vn 0.9806 -0.1961 0.0000 +vn 0.7071 -0.7072 0.0000 +vn 0.9744 0.2249 0.0000 +vn 0.8403 0.5421 0.0000 +vn 0.9171 0.3987 0.0000 +vn 0.3940 0.9191 0.0000 +vn 0.7592 0.6508 0.0000 +vn 0.8183 0.5748 0.0000 +vn 0.9615 0.2748 0.0000 +vn 0.7894 0.6139 0.0000 +vn 0.9285 0.3714 0.0000 +vn -0.5884 0.3923 0.7071 +vn -0.6989 0.1075 0.7071 +vn -0.6442 -0.2917 0.7071 +vn -0.5701 -0.4183 0.7071 +vn -0.5459 -0.4495 0.7071 +vn 0.7070 -0.0133 0.7071 +vn 0.6934 -0.1387 0.7070 +vn 0.6890 0.1590 0.7071 +vn 0.5942 0.3834 0.7071 +vn 0.6485 0.2819 0.7071 +vn 0.2786 0.6499 0.7071 +vn 0.5369 0.4602 0.7071 +vn 0.5786 0.4064 0.7071 +vn 0.6799 0.1943 0.7071 +vn 0.5581 0.4341 0.7071 +vn 0.6566 0.2626 0.7071 +vn -0.4416 0.5522 0.7072 +vn 0.0891 0.9960 0.0000 +vn -0.8910 -0.4539 0.0000 +vn -0.5955 -0.8034 0.0000 +vn -0.0605 -0.9982 0.0000 +vn 0.9426 -0.3338 0.0000 +vn 0.9837 0.1797 0.0000 +vn -0.8854 0.4648 0.0000 +vn -0.7311 0.6823 0.0000 +vn 0.8575 0.5145 0.0000 +vn -0.9999 -0.0132 0.0000 +vn 0.7986 0.6019 0.0000 +vn -0.9088 0.4172 0.0000 +vn -0.9247 0.3808 0.0000 +vn -0.7526 0.6585 0.0000 +vn -0.3714 -0.9285 0.0000 +vn -0.7071 -0.7072 0.0000 +vn -0.7512 -0.6601 0.0000 +vn -0.9487 -0.3161 0.0000 +vn -0.7515 -0.6597 0.0000 +vn -0.6508 -0.7592 0.0000 +vn 0.9975 -0.0713 0.0000 +vn 0.8132 0.5819 0.0000 +vn 0.7634 0.6459 0.0000 +vn -0.8193 0.5734 0.0000 +vn -0.6540 0.2693 0.7070 +vn -0.5322 0.4657 0.7071 +vn -0.2625 -0.6564 0.7073 +vn -0.5312 -0.4668 0.7071 +vn -0.6708 -0.2236 0.7072 +vn -0.5314 -0.4665 0.7071 +vn -0.4602 -0.5369 0.7071 +vn 0.7054 -0.0504 0.7070 +vn 0.6707 0.2236 0.7072 +vn 0.5751 0.4115 0.7071 +vn 0.5398 0.4568 0.7071 +vn -0.5794 0.4056 0.7069 +vn 0.5657 -0.8246 0.0000 +vn 0.5052 -0.8630 0.0000 +vn 0.5781 -0.8160 0.0000 +vn -0.7738 0.6334 0.0000 +vn -0.7070 0.7072 0.0000 +vn -0.6300 -0.3209 0.7072 +vn 0.4000 -0.5830 0.7072 +vn 0.3572 -0.6103 0.7071 +vn 0.4108 -0.6706 0.6177 +vn -0.6646 0.4108 0.6242 +vn 0.5000 0.4999 0.7072 +vn 0.0630 0.7043 0.7071 +vn -0.4999 0.5001 0.7071 +vn -0.5010 0.8654 0.0000 +vn -0.5647 0.8253 0.0000 +vn -0.7809 0.6247 0.0000 +vn -0.6402 -0.7682 0.0000 +vn -0.5951 -0.8036 0.0000 +vn 0.9999 -0.0138 0.0000 +vn 0.9982 -0.0605 0.0000 +vn 0.8192 0.5735 0.0000 +vn 0.7072 0.7071 0.0000 +vn 0.3161 0.9487 0.0000 +vn -0.3543 0.6119 0.7071 +vn -0.3993 0.5836 0.7071 +vn -0.5522 0.4418 0.7071 +vn -0.4526 -0.5432 0.7072 +vn -0.4208 -0.5682 0.7071 +vn 0.7071 -0.0098 0.7070 +vn 0.7059 -0.0428 0.7070 +vn 0.5794 0.4055 0.7070 +vn 0.5285 0.4698 0.7070 +vn 0.2236 0.6708 0.7071 +vn -0.5522 0.4417 0.7071 +vn -0.7328 0.6804 0.0000 +vn -0.8522 -0.5233 0.0000 +vn 0.6644 -0.7474 0.0000 +vn 0.5547 -0.8320 0.0000 +vn 0.9845 0.1755 0.0000 +vn 0.8137 0.5812 0.0000 +vn -0.6261 0.3287 0.7071 +vn -0.5182 0.4812 0.7071 +vn -0.6026 -0.3700 0.7071 +vn 0.4698 -0.5285 0.7071 +vn 0.3922 -0.5883 0.7072 +vn 0.6961 0.1241 0.7071 +vn 0.5754 0.4110 0.7071 +vn -0.9975 0.0713 0.0000 +vn -0.1643 -0.9864 0.0000 +vn 0.6499 -0.7600 0.0000 +vn 0.8522 0.5233 0.0000 +vn 0.7070 0.7072 0.0000 +vn -0.7070 -0.0093 0.7072 +vn -0.6646 -0.2417 0.7071 +vn -0.7053 0.0504 0.7071 +vn -0.1162 -0.6975 0.7071 +vn 0.4596 -0.5374 0.7071 +vn 0.6026 0.3700 0.7071 +vn 0.6063 0.3638 0.7072 +vn -0.9999 0.0139 0.0000 +vn 0.7372 0.6757 0.0000 +vn 0.8321 0.5547 0.0000 +vn 0.6585 0.7526 0.0000 +vn 0.7515 0.6597 0.0000 +vn -0.9978 0.0665 0.0000 +vn -0.7070 0.0098 0.7071 +vn -0.0428 -0.7058 0.7071 +vn 0.4999 -0.4999 0.7072 +vn 0.5212 0.4778 0.7072 +vn 0.5884 0.3923 0.7071 +vn 0.4657 0.5322 0.7071 +vn 0.5314 0.4665 0.7071 +vn -0.7055 0.0470 0.7071 +vn 0.1645 0.9864 0.0000 +vn -0.8322 -0.5545 0.0000 +vn -0.7448 -0.6673 0.0000 +vn -0.6509 -0.7592 0.0000 +vn 0.7808 -0.6247 0.0000 +vn 0.9487 -0.3162 0.0000 +vn 0.9445 -0.3285 0.0000 +vn 0.4471 0.8945 0.0000 +vn -0.6467 0.7628 0.0000 +vn 0.1163 0.6975 0.7071 +vn -0.5883 -0.3921 0.7072 +vn -0.5267 -0.4718 0.7071 +vn -0.4603 -0.5369 0.7070 +vn 0.5521 -0.4417 0.7072 +vn 0.6707 -0.2236 0.7072 +vn 0.6679 -0.2323 0.7071 +vn 0.3162 0.6326 0.7070 +vn -0.4573 0.5394 0.7071 +vn -0.6983 -0.7159 0.0000 +vn -0.0176 -0.9998 0.0000 +vn 0.7617 0.6479 0.0000 +vn -0.1745 0.9847 0.0000 +vn 0.5613 0.8276 0.0000 +vn 0.9758 0.2187 0.0000 +vn 0.7450 -0.6671 0.0000 +vn 0.9771 -0.2128 0.0000 +vn 0.6247 0.7809 0.0000 +vn -0.6118 0.7910 0.0000 +vn -0.5882 0.8088 0.0000 +vn -0.1961 -0.9806 0.0000 +vn -0.6000 -0.8000 0.0000 +vn -0.0294 -0.9996 0.0000 +vn -0.6174 0.7866 0.0000 +vn -0.4159 0.5719 0.7071 +vn -0.1387 -0.6934 0.7071 +vn -0.4243 -0.5657 0.7071 +vn -0.0208 -0.7067 0.7073 +vn 0.5657 0.4243 0.7071 +vn 0.5385 0.4581 0.7072 +vn -0.4366 0.5562 0.7071 +vn -0.8320 0.5548 0.0000 +vn -0.7944 0.6075 0.0000 +vn -0.7029 -0.7113 0.0000 +vn -0.6941 -0.7199 0.0000 +vn -0.3162 -0.9487 0.0000 +vn 0.9697 0.2445 0.0000 +vn 0.9899 -0.1414 0.0000 +vn 0.9762 0.2169 0.0000 +vn 0.6246 0.7809 0.0000 +vn 0.1962 0.9806 0.0000 +vn -0.9923 0.1240 0.0000 +vn -0.5883 0.3922 0.7072 +vn -0.5616 0.4295 0.7072 +vn -0.4970 -0.5029 0.7072 +vn -0.4908 -0.5090 0.7072 +vn -0.2238 -0.6708 0.7070 +vn 0.6934 -0.1387 0.7071 +vn 0.6858 0.1729 0.7070 +vn 0.7001 -0.1000 0.7070 +vn 0.6903 0.1534 0.7071 +vn 0.4417 0.5521 0.7072 +vn 0.1388 0.6935 0.7070 +vn -0.7016 0.0877 0.7071 +vn -0.9968 0.0795 0.0000 +vn 0.7944 -0.6074 0.0000 +vn 0.9192 -0.3939 0.0000 +vn 0.9900 0.1414 0.0000 +vn 0.8598 0.5105 0.0000 +vn 0.8849 0.4657 0.0000 +vn -0.4474 0.8943 0.0000 +vn -0.6974 0.1162 0.7072 +vn -0.7048 0.0562 0.7071 +vn -0.6707 0.2235 0.7073 +vn -0.4999 -0.5000 0.7071 +vn 0.5617 -0.4295 0.7071 +vn 0.6500 -0.2786 0.7071 +vn 0.7000 0.1000 0.7071 +vn 0.6080 0.3610 0.7072 +vn 0.6258 0.3293 0.7071 +vn -0.3162 0.6325 0.7071 +vn 0.4999 0.5000 0.7072 +vn -0.4999 0.5000 0.7071 +vn -0.6024 -0.7982 0.0000 +vn -0.5734 -0.8193 0.0000 +vn -0.1962 -0.9806 0.0000 +vn 0.6145 -0.7889 0.0000 +vn -0.5369 0.4602 0.7071 +vn -0.4260 -0.5644 0.7071 +vn -0.4055 -0.5793 0.7071 +vn 0.4345 -0.5578 0.7071 +vn 0.5001 0.4999 0.7071 +vn -0.1234 0.6962 0.7071 +vn -0.8898 -0.4563 0.0000 +vn -0.2425 -0.9702 0.0000 +vn 0.7498 -0.6616 0.0000 +vn 0.9798 -0.2000 0.0000 +vn 0.8321 -0.5547 0.0000 +vn -0.6100 0.7924 0.0000 +vn -0.6292 -0.3227 0.7071 +vn -0.6064 -0.3638 0.7071 +vn -0.1715 -0.6860 0.7071 +vn 0.5302 -0.4678 0.7071 +vn 0.6928 -0.1414 0.7071 +vn 0.5884 -0.3921 0.7071 +vn 0.4417 0.5522 0.7071 +vn -0.4314 0.5604 0.7070 +vn -0.9697 -0.2445 0.0000 +vn -0.9487 0.3163 0.0000 +vn -0.0301 -0.9995 0.0000 +vn 0.8946 0.4470 0.0000 +vn 0.9864 0.1644 0.0000 +vn 0.5798 0.8148 0.0000 +vn 0.6459 0.7634 0.0000 +vn 0.4472 0.8944 0.0000 +vn 0.5631 0.8264 0.0000 +vn 0.6644 0.7474 0.0000 +vn -0.9701 0.2425 0.0000 +vn -0.6860 -0.1729 0.7068 +vn -0.6708 0.2236 0.7072 +vn -0.0213 -0.7070 0.7068 +vn 0.6326 0.3160 0.7071 +vn 0.6975 0.1162 0.7071 +vn 0.4099 0.5761 0.7072 +vn 0.4567 0.5397 0.7072 +vn 0.3162 0.6325 0.7071 +vn 0.3981 0.5843 0.7072 +vn 0.4698 0.5285 0.7071 +vn -0.6861 0.1715 0.7070 +vn -0.0550 0.9985 0.0000 +vn -0.8682 -0.4961 0.0000 +vn -0.4961 -0.8682 0.0000 +vn 0.7809 0.6247 0.0000 +vn 0.7407 0.6718 0.0000 +vn -0.8682 -0.4962 0.0000 +vn -0.4472 -0.8944 0.0000 +vn -0.6508 -0.7593 0.0000 +vn -0.8400 -0.5425 0.0000 +vn 0.7838 -0.6210 0.0000 +vn 0.9806 0.1960 0.0000 +vn -0.0653 0.9979 0.0000 +vn -0.6140 -0.3508 0.7071 +vn -0.5521 0.4417 0.7072 +vn -0.3162 -0.6324 0.7072 +vn -0.4602 -0.5369 0.7070 +vn -0.5940 -0.3837 0.7070 +vn 0.5543 -0.4391 0.7071 +vn 0.6934 0.1386 0.7071 +vn -0.0462 0.7055 0.7072 +vn -0.6140 -0.7893 0.0000 +vn 0.4706 0.8824 0.0000 +vn 0.8400 0.5425 0.0000 +vn 0.5433 0.8396 0.0000 +vn -0.4341 -0.5581 0.7072 +vn -0.3424 -0.6187 0.7071 +vn 0.3327 0.6239 0.7071 +vn 0.5940 0.3836 0.7071 +vn 0.3842 0.5937 0.7071 +vn -0.8000 0.6000 0.0000 +vn -0.7788 0.6273 0.0000 +vn -0.5547 -0.8321 0.0000 +vn -0.0578 -0.9983 0.0000 +vn -0.5657 0.4243 0.7071 +vn -0.5507 0.4436 0.7071 +vn -0.3922 -0.5883 0.7071 +vn -0.0408 -0.7060 0.7071 +vn 0.5521 0.4417 0.7072 +vn 0.5238 0.4750 0.7071 +vn -0.9659 0.2587 0.0000 +vn -0.6097 -0.7926 0.0000 +vn 0.7204 -0.6935 0.0000 +vn 0.8861 0.4634 0.0000 +vn 0.4741 0.8805 0.0000 +vn -0.6834 0.7300 0.0000 +vn -0.9654 0.2607 0.0000 +vn -0.9744 0.2249 0.0000 +vn -0.5591 -0.8291 0.0000 +vn 0.2425 -0.9702 0.0000 +vn 0.9710 -0.2390 0.0000 +vn 0.9762 -0.2170 0.0000 +vn -0.6827 0.1843 0.7071 +vn -0.6890 0.1590 0.7071 +vn -0.3953 -0.5862 0.7071 +vn -0.4527 -0.5432 0.7071 +vn 0.1714 -0.6860 0.7071 +vn 0.6709 -0.2236 0.7071 +vn 0.6866 -0.1690 0.7071 +vn 0.6822 -0.1861 0.7071 +vn 0.6903 -0.1534 0.7071 +vn 0.4998 0.5001 0.7072 +vn -0.4833 0.5162 0.7071 +vn -0.9674 0.2533 0.0000 +vn -0.9285 0.3714 0.0000 +vn -0.4471 0.8945 0.0000 +vn 0.6823 -0.7311 0.0000 +vn 0.8862 0.4634 0.0000 +vn -0.6841 0.1791 0.7070 +vn -0.6566 0.2626 0.7070 +vn -0.3162 0.6326 0.7070 +vn 0.4824 -0.5169 0.7071 +vn 0.5127 -0.4870 0.7071 +vn 0.6266 0.3277 0.7071 +vn 0.4999 0.5000 0.7071 +vn -0.7032 -0.7110 0.0000 +vn -0.0365 -0.9993 0.0000 +vn 0.6247 -0.7809 0.0000 +vn 0.3852 0.9228 0.0000 +vn 0.6246 -0.7809 0.0000 +vn 0.6403 0.7682 0.0000 +vn 0.3764 0.9265 0.0000 +vn -0.4972 -0.5027 0.7071 +vn -0.0258 -0.7066 0.7071 +vn 0.4417 -0.5522 0.7070 +vn 0.4527 0.5432 0.7071 +vn 0.2661 0.6551 0.7071 +vn -0.8214 -0.5704 0.0000 +vn -0.0917 -0.9958 0.0000 +vn 0.5347 0.8450 0.0000 +vn -0.8944 0.4472 0.0000 +vn -0.8945 -0.4470 0.0000 +vn -0.8301 -0.5576 0.0000 +vn -0.6326 -0.3161 0.7071 +vn -0.5871 -0.3943 0.7070 +vn -0.5001 -0.5001 0.7070 +vn -0.0649 -0.7041 0.7071 +vn 0.4416 -0.5522 0.7072 +vn 0.3720 0.6013 0.7071 +vn -0.8321 -0.5547 0.0000 +vn 0.3500 0.9368 0.0000 +vn -0.7928 -0.6095 0.0000 +vn -0.2102 -0.9777 0.0000 +vn -0.1159 -0.9933 0.0000 +vn 0.8583 0.5131 0.0000 +vn -0.8321 -0.5546 0.0000 +vn -0.6163 -0.7875 0.0000 +vn -0.4004 -0.9164 0.0000 +vn -0.5369 -0.8437 0.0000 +vn 0.5546 -0.8321 0.0000 +vn 0.7808 -0.6248 0.0000 +vn -0.5883 -0.3923 0.7071 +vn -0.5521 -0.4417 0.7072 +vn -0.4358 -0.5569 0.7071 +vn -0.2831 -0.6480 0.7071 +vn -0.3796 -0.5966 0.7071 +vn 0.4526 -0.5432 0.7072 +vn 0.5521 -0.4417 0.7071 +vn 0.3134 0.6338 0.7071 +vn -0.7983 -0.6022 0.0000 +vn 0.6476 -0.7619 0.0000 +vn 0.8945 0.4471 0.0000 +vn 0.4472 0.8945 0.0000 +vn 0.4004 0.9164 0.0000 +vn 0.6163 0.7875 0.0000 +vn 0.3466 0.9380 0.0000 +vn -0.5884 -0.3923 0.7070 +vn -0.5645 -0.4259 0.7071 +vn 0.4580 -0.5388 0.7071 +vn 0.6325 0.3162 0.7071 +vn 0.2831 0.6479 0.7071 +vn 0.4358 0.5568 0.7071 +vn 0.2451 0.6632 0.7072 +vn -0.5547 0.8320 0.0000 +vn -0.6393 0.7690 0.0000 +vn -0.2106 -0.9776 0.0000 +vn -0.1222 -0.9925 0.0000 +vn 0.8944 0.4472 0.0000 +vn 0.8629 0.5055 0.0000 +vn -0.6432 0.7657 0.0000 +vn -0.3922 0.5883 0.7071 +vn -0.4520 0.5437 0.7071 +vn -0.5432 0.4527 0.7071 +vn -0.1489 -0.6912 0.7071 +vn -0.0864 -0.7018 0.7071 +vn 0.4999 -0.5000 0.7072 +vn 0.6324 0.3163 0.7071 +vn 0.6101 0.3574 0.7071 +vn 0.5522 0.4418 0.7071 +vn -0.4548 0.5414 0.7071 +o plate +usemtl plate +f 1//1 170//1 169//1 +f 339//2 340//2 171//2 +f 1//1 169//1 168//1 +f 338//2 339//2 171//2 +f 168//1 167//1 166//1 +f 336//2 337//2 338//2 +f 165//1 164//1 163//1 +f 333//2 334//2 335//2 +f 165//1 163//1 162//1 +f 332//2 333//2 335//2 +f 166//1 165//1 162//1 +f 332//2 335//2 336//2 +f 166//1 162//1 161//1 +f 331//2 332//2 336//2 +f 168//1 166//1 161//1 +f 331//2 336//2 338//2 +f 168//1 161//1 160//1 +f 330//2 331//2 338//2 +f 1//1 168//1 160//1 +f 330//2 338//2 171//2 +f 1//1 160//1 159//1 +f 329//2 330//2 171//2 +f 1//1 159//1 158//1 +f 328//2 329//2 171//2 +f 1//1 158//1 157//1 +f 327//2 328//2 171//2 +f 1//1 157//1 156//1 +f 326//2 327//2 171//2 +f 1//1 156//1 155//1 +f 325//2 326//2 171//2 +f 1//1 155//1 154//1 +f 324//2 325//2 171//2 +f 1//1 154//1 153//1 +f 323//2 324//2 171//2 +f 153//1 152//1 151//1 +f 321//2 322//2 323//2 +f 153//1 151//1 150//1 +f 320//2 321//2 323//2 +f 153//1 150//1 149//1 +f 319//2 320//2 323//2 +f 147//1 146//1 145//1 +f 315//2 316//2 317//2 +f 148//1 147//1 145//1 +f 315//2 317//2 318//2 +f 148//1 145//1 144//1 +f 314//2 315//2 318//2 +f 148//1 144//1 143//1 +f 313//2 314//2 318//2 +f 149//1 148//1 143//1 +f 313//2 318//2 319//2 +f 153//1 149//1 143//1 +f 313//2 319//2 323//2 +f 153//1 143//1 142//1 +f 312//2 313//2 323//2 +f 153//1 142//1 141//1 +f 311//2 312//2 323//2 +f 153//1 141//1 140//1 +f 310//2 311//2 323//2 +f 153//1 140//1 139//1 +f 309//2 310//2 323//2 +f 153//1 139//1 138//1 +f 308//2 309//2 323//2 +f 153//1 138//1 137//1 +f 307//2 308//2 323//2 +f 153//1 137//1 136//1 +f 306//2 307//2 323//2 +f 153//1 136//1 135//1 +f 305//2 306//2 323//2 +f 134//1 133//1 132//1 +f 302//2 303//2 304//2 +f 132//1 131//1 130//1 +f 300//2 301//2 302//2 +f 134//1 132//1 130//1 +f 300//2 302//2 304//2 +f 134//1 130//1 129//1 +f 299//2 300//2 304//2 +f 134//1 129//1 128//1 +f 298//2 299//2 304//2 +f 135//1 134//1 128//1 +f 298//2 304//2 305//2 +f 135//1 128//1 127//1 +f 297//2 298//2 305//2 +f 135//1 127//1 126//1 +f 296//2 297//2 305//2 +f 125//1 124//1 123//1 +f 293//2 294//2 295//2 +f 125//1 123//1 122//1 +f 292//2 293//2 295//2 +f 125//1 122//1 121//1 +f 291//2 292//2 295//2 +f 120//1 119//1 118//1 +f 288//2 289//2 290//2 +f 120//1 118//1 117//1 +f 287//2 288//2 290//2 +f 120//1 117//1 116//1 +f 286//2 287//2 290//2 +f 120//1 116//1 115//1 +f 285//2 286//2 290//2 +f 120//1 115//1 114//1 +f 284//2 285//2 290//2 +f 121//1 120//1 114//1 +f 284//2 290//2 291//2 +f 121//1 114//1 113//1 +f 283//2 284//2 291//2 +f 113//1 112//1 111//1 +f 281//2 282//2 283//2 +f 113//1 111//1 110//1 +f 280//2 281//2 283//2 +f 113//1 110//1 109//1 +f 279//2 280//2 283//2 +f 113//1 109//1 108//1 +f 278//2 279//2 283//2 +f 121//1 113//1 108//1 +f 278//2 283//2 291//2 +f 121//1 108//1 107//1 +f 277//2 278//2 291//2 +f 121//1 107//1 106//1 +f 276//2 277//2 291//2 +f 106//1 105//1 104//1 +f 274//2 275//2 276//2 +f 106//1 104//1 103//1 +f 273//2 274//2 276//2 +f 106//1 103//1 102//1 +f 272//2 273//2 276//2 +f 106//1 102//1 101//1 +f 271//2 272//2 276//2 +f 121//1 106//1 101//1 +f 271//2 276//2 291//2 +f 121//1 101//1 100//1 +f 270//2 271//2 291//2 +f 121//1 100//1 99//1 +f 269//2 270//2 291//2 +f 121//1 99//1 98//1 +f 268//2 269//2 291//2 +f 121//1 98//1 97//1 +f 267//2 268//2 291//2 +f 121//1 97//1 96//1 +f 266//2 267//2 291//2 +f 121//1 96//1 95//1 +f 265//2 266//2 291//2 +f 95//1 94//1 93//1 +f 263//2 264//2 265//2 +f 121//1 95//1 93//1 +f 263//2 265//2 291//2 +f 121//1 93//1 92//1 +f 262//2 263//2 291//2 +f 121//1 92//1 91//1 +f 261//2 262//2 291//2 +f 121//1 91//1 90//1 +f 260//2 261//2 291//2 +f 121//1 90//1 89//1 +f 259//2 260//2 291//2 +f 121//1 89//1 88//1 +f 258//2 259//2 291//2 +f 121//1 88//1 87//1 +f 257//2 258//2 291//2 +f 87//1 86//1 85//1 +f 255//2 256//2 257//2 +f 87//1 85//1 84//1 +f 254//2 255//2 257//2 +f 87//1 84//1 83//1 +f 253//2 254//2 257//2 +f 121//1 87//1 83//1 +f 253//2 257//2 291//2 +f 121//1 83//1 82//1 +f 252//2 253//2 291//2 +f 121//1 82//1 81//1 +f 251//2 252//2 291//2 +f 121//1 81//1 80//1 +f 250//2 251//2 291//2 +f 125//1 121//1 80//1 +f 250//2 291//2 295//2 +f 125//1 80//1 79//1 +f 249//2 250//2 295//2 +f 125//1 79//1 78//1 +f 248//2 249//2 295//2 +f 125//1 78//1 77//1 +f 247//2 248//2 295//2 +f 126//1 125//1 77//1 +f 247//2 295//2 296//2 +f 126//1 77//1 76//1 +f 246//2 247//2 296//2 +f 76//1 75//1 74//1 +f 244//2 245//2 246//2 +f 76//1 74//1 73//1 +f 243//2 244//2 246//2 +f 76//1 73//1 72//1 +f 242//2 243//2 246//2 +f 126//1 76//1 72//1 +f 242//2 246//2 296//2 +f 126//1 72//1 71//1 +f 241//2 242//2 296//2 +f 126//1 71//1 70//1 +f 240//2 241//2 296//2 +f 126//1 70//1 69//1 +f 239//2 240//2 296//2 +f 126//1 69//1 68//1 +f 238//2 239//2 296//2 +f 126//1 68//1 67//1 +f 237//2 238//2 296//2 +f 126//1 67//1 66//1 +f 236//2 237//2 296//2 +f 126//1 66//1 65//1 +f 235//2 236//2 296//2 +f 126//1 65//1 64//1 +f 234//2 235//2 296//2 +f 126//1 64//1 63//1 +f 233//2 234//2 296//2 +f 126//1 63//1 62//1 +f 232//2 233//2 296//2 +f 126//1 62//1 61//1 +f 231//2 232//2 296//2 +f 126//1 61//1 60//1 +f 230//2 231//2 296//2 +f 126//1 60//1 59//1 +f 229//2 230//2 296//2 +f 135//1 126//1 59//1 +f 229//2 296//2 305//2 +f 135//1 59//1 58//1 +f 228//2 229//2 305//2 +f 135//1 58//1 57//1 +f 227//2 228//2 305//2 +f 57//1 56//1 55//1 +f 225//2 226//2 227//2 +f 135//1 57//1 55//1 +f 225//2 227//2 305//2 +f 135//1 55//1 54//1 +f 224//2 225//2 305//2 +f 135//1 54//1 53//1 +f 223//2 224//2 305//2 +f 135//1 53//1 52//1 +f 222//2 223//2 305//2 +f 135//1 52//1 51//1 +f 221//2 222//2 305//2 +f 135//1 51//1 50//1 +f 220//2 221//2 305//2 +f 135//1 50//1 49//1 +f 219//2 220//2 305//2 +f 135//1 49//1 48//1 +f 218//2 219//2 305//2 +f 135//1 48//1 47//1 +f 217//2 218//2 305//2 +f 135//1 47//1 46//1 +f 216//2 217//2 305//2 +f 135//1 46//1 45//1 +f 215//2 216//2 305//2 +f 135//1 45//1 44//1 +f 214//2 215//2 305//2 +f 44//1 43//1 42//1 +f 212//2 213//2 214//2 +f 44//1 42//1 41//1 +f 211//2 212//2 214//2 +f 44//1 41//1 40//1 +f 210//2 211//2 214//2 +f 44//1 40//1 39//1 +f 209//2 210//2 214//2 +f 135//1 44//1 39//1 +f 209//2 214//2 305//2 +f 135//1 39//1 38//1 +f 208//2 209//2 305//2 +f 135//1 38//1 37//1 +f 207//2 208//2 305//2 +f 153//1 135//1 37//1 +f 207//2 305//2 323//2 +f 37//1 36//1 35//1 +f 205//2 206//2 207//2 +f 37//1 35//1 34//1 +f 204//2 205//2 207//2 +f 37//1 34//1 33//1 +f 203//2 204//2 207//2 +f 37//1 33//1 32//1 +f 202//2 203//2 207//2 +f 37//1 32//1 31//1 +f 201//2 202//2 207//2 +f 153//1 37//1 31//1 +f 201//2 207//2 323//2 +f 153//1 31//1 30//1 +f 200//2 201//2 323//2 +f 153//1 30//1 29//1 +f 199//2 200//2 323//2 +f 153//1 29//1 28//1 +f 198//2 199//2 323//2 +f 153//1 28//1 27//1 +f 197//2 198//2 323//2 +f 153//1 27//1 26//1 +f 196//2 197//2 323//2 +f 26//1 25//1 24//1 +f 194//2 195//2 196//2 +f 153//1 26//1 24//1 +f 194//2 196//2 323//2 +f 153//1 24//1 23//1 +f 193//2 194//2 323//2 +f 153//1 23//1 22//1 +f 192//2 193//2 323//2 +f 22//1 21//1 20//1 +f 190//2 191//2 192//2 +f 22//1 20//1 19//1 +f 189//2 190//2 192//2 +f 22//1 19//1 18//1 +f 188//2 189//2 192//2 +f 18//1 17//1 16//1 +f 186//2 187//2 188//2 +f 18//1 16//1 15//1 +f 185//2 186//2 188//2 +f 22//1 18//1 15//1 +f 185//2 188//2 192//2 +f 22//1 15//1 14//1 +f 184//2 185//2 192//2 +f 22//1 14//1 13//1 +f 183//2 184//2 192//2 +f 22//1 13//1 12//1 +f 182//2 183//2 192//2 +f 22//1 12//1 11//1 +f 181//2 182//2 192//2 +f 153//1 22//1 11//1 +f 181//2 192//2 323//2 +f 153//1 11//1 10//1 +f 180//2 181//2 323//2 +f 153//1 10//1 9//1 +f 179//2 180//2 323//2 +f 153//1 9//1 8//1 +f 178//2 179//2 323//2 +f 153//1 8//1 7//1 +f 177//2 178//2 323//2 +f 7//1 6//1 5//1 +f 175//2 176//2 177//2 +f 153//1 7//1 5//1 +f 175//2 177//2 323//2 +f 153//1 5//1 4//1 +f 174//2 175//2 323//2 +f 153//1 4//1 3//1 +f 173//2 174//2 323//2 +f 153//1 3//1 2//1 +f 172//2 173//2 323//2 +f 153//1 2//1 1//1 +f 171//2 172//2 323//2 +f 1//3 2//3 172//3 171//3 +f 2//4 3//4 173//4 172//4 +f 3//5 4//5 174//5 173//5 +f 4//6 5//6 175//6 174//6 +f 5//7 6//7 176//7 175//7 +f 6//8 7//8 177//8 176//8 +f 7//9 8//9 178//9 177//9 +f 8//4 9//4 179//4 178//4 +f 9//10 10//10 180//10 179//10 +f 10//4 11//4 181//4 180//4 +f 11//11 12//11 182//11 181//11 +f 12//12 13//12 183//12 182//12 +f 13//13 14//13 184//13 183//13 +f 14//11 15//11 185//11 184//11 +f 15//14 16//14 186//14 185//14 +f 16//4 17//4 187//4 186//4 +f 17//15 18//15 188//15 187//15 +f 18//4 19//4 189//4 188//4 +f 19//16 20//16 190//16 189//16 +f 20//4 21//4 191//4 190//4 +f 21//17 22//17 192//17 191//17 +f 22//18 23//18 193//18 192//18 +f 23//19 24//19 194//19 193//19 +f 24//20 25//20 195//20 194//20 +f 25//4 26//4 196//4 195//4 +f 26//21 27//21 197//21 196//21 +f 27//9 28//9 198//9 197//9 +f 28//22 29//22 199//22 198//22 +f 29//23 30//23 200//23 199//23 +f 30//4 31//4 201//4 200//4 +f 31//24 32//24 202//24 201//24 +f 32//25 33//25 203//25 202//25 +f 33//26 34//26 204//26 203//26 +f 34//27 35//27 205//27 204//27 +f 35//28 36//28 206//28 205//28 +f 36//9 37//9 207//9 206//9 +f 37//29 38//29 208//29 207//29 +f 38//9 39//9 209//9 208//9 +f 39//4 40//4 210//4 209//4 +f 40//30 41//30 211//30 210//30 +f 41//4 42//4 212//4 211//4 +f 42//31 43//31 213//31 212//31 +f 43//32 44//32 214//32 213//32 +f 44//33 45//33 215//33 214//33 +f 45//34 46//34 216//34 215//34 +f 46//35 47//35 217//35 216//35 +f 47//36 48//36 218//36 217//36 +f 48//9 49//9 219//9 218//9 +f 49//37 50//37 220//37 219//37 +f 50//28 51//28 221//28 220//28 +f 51//9 52//9 222//9 221//9 +f 52//38 53//38 223//38 222//38 +f 53//39 54//39 224//39 223//39 +f 54//9 55//9 225//9 224//9 +f 55//40 56//40 226//40 225//40 +f 56//9 57//9 227//9 226//9 +f 57//41 58//41 228//41 227//41 +f 58//42 59//42 229//42 228//42 +f 59//43 60//43 230//43 229//43 +f 60//44 61//44 231//44 230//44 +f 61//9 62//9 232//9 231//9 +f 62//45 63//45 233//45 232//45 +f 63//9 64//9 234//9 233//9 +f 64//46 65//46 235//46 234//46 +f 65//13 66//13 236//13 235//13 +f 66//47 67//47 237//47 236//47 +f 67//13 68//13 238//13 237//13 +f 68//48 69//48 239//48 238//48 +f 69//49 70//49 240//49 239//49 +f 70//50 71//50 241//50 240//50 +f 71//9 72//9 242//9 241//9 +f 72//51 73//51 243//51 242//51 +f 73//28 74//28 244//28 243//28 +f 74//9 75//9 245//9 244//9 +f 75//52 76//52 246//52 245//52 +f 76//13 77//13 247//13 246//13 +f 77//53 78//53 248//53 247//53 +f 78//13 79//13 249//13 248//13 +f 79//54 80//54 250//54 249//54 +f 80//55 81//55 251//55 250//55 +f 81//56 82//56 252//56 251//56 +f 82//57 83//57 253//57 252//57 +f 83//58 84//58 254//58 253//58 +f 84//59 85//59 255//59 254//59 +f 85//11 86//11 256//11 255//11 +f 86//60 87//60 257//60 256//60 +f 87//61 88//61 258//61 257//61 +f 88//62 89//62 259//62 258//62 +f 89//13 90//13 260//13 259//13 +f 90//63 91//63 261//63 260//63 +f 91//64 92//64 262//64 261//64 +f 92//65 93//65 263//65 262//65 +f 93//66 94//66 264//66 263//66 +f 94//67 95//67 265//67 264//67 +f 95//11 96//11 266//11 265//11 +f 96//13 97//13 267//13 266//13 +f 97//68 98//68 268//68 267//68 +f 98//13 99//13 269//13 268//13 +f 99//69 100//69 270//69 269//69 +f 100//59 101//59 271//59 270//59 +f 101//11 102//11 272//11 271//11 +f 102//70 103//70 273//70 272//70 +f 103//71 104//71 274//71 273//71 +f 104//72 105//72 275//72 274//72 +f 105//4 106//4 276//4 275//4 +f 106//11 107//11 277//11 276//11 +f 107//73 108//73 278//73 277//73 +f 108//74 109//74 279//74 278//74 +f 109//75 110//75 280//75 279//75 +f 110//76 111//76 281//76 280//76 +f 111//77 112//77 282//77 281//77 +f 112//78 113//78 283//78 282//78 +f 113//11 114//11 284//11 283//11 +f 114//79 115//79 285//79 284//79 +f 115//59 116//59 286//59 285//59 +f 116//80 117//80 287//80 286//80 +f 117//81 118//81 288//81 287//81 +f 118//78 119//78 289//78 288//78 +f 119//82 120//82 290//82 289//82 +f 120//4 121//4 291//4 290//4 +f 121//11 122//11 292//11 291//11 +f 122//83 123//83 293//83 292//83 +f 123//84 124//84 294//84 293//84 +f 124//85 125//85 295//85 294//85 +f 125//11 126//11 296//11 295//11 +f 126//86 127//86 297//86 296//86 +f 127//13 128//13 298//13 297//13 +f 128//87 129//87 299//87 298//87 +f 129//13 130//13 300//13 299//13 +f 130//88 131//88 301//88 300//88 +f 131//89 132//89 302//89 301//89 +f 132//11 133//11 303//11 302//11 +f 133//90 134//90 304//90 303//90 +f 134//11 135//11 305//11 304//11 +f 135//91 136//91 306//91 305//91 +f 136//92 137//92 307//92 306//92 +f 137//13 138//13 308//13 307//13 +f 138//9 139//9 309//9 308//9 +f 139//93 140//93 310//93 309//93 +f 140//94 141//94 311//94 310//94 +f 141//95 142//95 312//95 311//95 +f 142//13 143//13 313//13 312//13 +f 143//96 144//96 314//96 313//96 +f 144//13 145//13 315//13 314//13 +f 145//97 146//97 316//97 315//97 +f 146//4 147//4 317//4 316//4 +f 147//98 148//98 318//98 317//98 +f 148//11 149//11 319//11 318//11 +f 149//99 150//99 320//99 319//99 +f 150//100 151//100 321//100 320//100 +f 151//101 152//101 322//101 321//101 +f 152//102 153//102 323//102 322//102 +f 153//103 154//103 324//103 323//103 +f 154//104 155//104 325//104 324//104 +f 155//105 156//105 326//105 325//105 +f 156//13 157//13 327//13 326//13 +f 157//106 158//106 328//106 327//106 +f 158//107 159//107 329//107 328//107 +f 159//13 160//13 330//13 329//13 +f 160//106 161//106 331//106 330//106 +f 161//108 162//108 332//108 331//108 +f 162//109 163//109 333//109 332//109 +f 163//11 164//11 334//11 333//11 +f 164//4 165//4 335//4 334//4 +f 165//110 166//110 336//110 335//110 +f 166//111 167//111 337//111 336//111 +f 167//112 168//112 338//112 337//112 +f 168//113 169//113 339//113 338//113 +f 169//114 170//114 340//114 339//114 +f 170//4 1//4 171//4 340//4 +o letter01Walls +usemtl frame +f 341//1 369//1 368//1 +f 397//2 398//2 370//2 +f 341//1 368//1 367//1 +f 396//2 397//2 370//2 +f 341//1 367//1 366//1 +f 395//2 396//2 370//2 +f 341//1 366//1 365//1 +f 394//2 395//2 370//2 +f 341//1 365//1 364//1 +f 393//2 394//2 370//2 +f 341//1 364//1 363//1 +f 392//2 393//2 370//2 +f 363//1 362//1 361//1 +f 390//2 391//2 392//2 +f 363//1 361//1 360//1 +f 389//2 390//2 392//2 +f 363//1 360//1 359//1 +f 388//2 389//2 392//2 +f 363//1 359//1 358//1 +f 387//2 388//2 392//2 +f 363//1 358//1 357//1 +f 386//2 387//2 392//2 +f 356//1 355//1 354//1 +f 383//2 384//2 385//2 +f 356//1 354//1 353//1 +f 382//2 383//2 385//2 +f 357//1 356//1 353//1 +f 382//2 385//2 386//2 +f 357//1 353//1 352//1 +f 381//2 382//2 386//2 +f 357//1 352//1 351//1 +f 380//2 381//2 386//2 +f 357//1 351//1 350//1 +f 379//2 380//2 386//2 +f 357//1 350//1 349//1 +f 378//2 379//2 386//2 +f 363//1 357//1 349//1 +f 378//2 386//2 392//2 +f 363//1 349//1 348//1 +f 377//2 378//2 392//2 +f 363//1 348//1 347//1 +f 376//2 377//2 392//2 +f 341//1 363//1 347//1 +f 376//2 392//2 370//2 +f 341//1 347//1 346//1 +f 375//2 376//2 370//2 +f 341//1 346//1 345//1 +f 374//2 375//2 370//2 +f 341//1 345//1 344//1 +f 373//2 374//2 370//2 +f 341//1 344//1 343//1 +f 372//2 373//2 370//2 +f 343//1 342//1 341//1 +f 370//2 371//2 372//2 +f 341//115 342//115 371//115 370//115 +f 342//116 343//116 372//116 371//116 +f 343//4 344//4 373//4 372//4 +f 344//117 345//117 374//117 373//117 +f 345//118 346//118 375//118 374//118 +f 346//119 347//119 376//119 375//119 +f 347//120 348//120 377//120 376//120 +f 348//121 349//121 378//121 377//121 +f 349//122 350//122 379//122 378//122 +f 350//123 351//123 380//123 379//123 +f 351//124 352//124 381//124 380//124 +f 352//125 353//125 382//125 381//125 +f 353//126 354//126 383//126 382//126 +f 354//127 355//127 384//127 383//127 +f 355//128 356//128 385//128 384//128 +f 356//129 357//129 386//129 385//129 +f 357//130 358//130 387//130 386//130 +f 358//131 359//131 388//131 387//131 +f 359//132 360//132 389//132 388//132 +f 360//11 361//11 390//11 389//11 +f 361//133 362//133 391//133 390//133 +f 362//134 363//134 392//134 391//134 +f 363//135 364//135 393//135 392//135 +f 364//136 365//136 394//136 393//136 +f 365//137 366//137 395//137 394//137 +f 366//138 367//138 396//138 395//138 +f 367//139 368//139 397//139 396//139 +f 368//140 369//140 398//140 397//140 +f 369//141 341//141 370//141 398//141 +o letter01Face +usemtl dark +f 399//1 427//1 426//1 +f 484//1 485//1 457//1 +f 399//1 426//1 425//1 +f 483//2 484//2 457//2 +f 399//1 425//1 424//1 +f 482//2 483//2 457//2 +f 399//1 424//1 423//1 +f 481//2 482//2 457//2 +f 399//1 423//1 422//1 +f 480//2 481//2 457//2 +f 399//1 422//1 421//1 +f 479//2 480//2 457//2 +f 421//1 420//1 419//1 +f 477//2 478//2 479//2 +f 421//1 419//1 418//1 +f 476//2 477//2 479//2 +f 421//1 418//1 417//1 +f 475//2 476//2 479//2 +f 421//1 417//1 416//1 +f 474//2 475//2 479//2 +f 421//1 416//1 415//1 +f 473//2 474//2 479//2 +f 414//1 413//1 412//1 +f 470//2 471//2 472//2 +f 414//1 412//1 411//1 +f 469//2 470//2 472//2 +f 415//1 414//1 411//1 +f 469//2 472//2 473//2 +f 415//1 411//1 410//1 +f 468//2 469//2 473//2 +f 415//1 410//1 409//1 +f 467//2 468//2 473//2 +f 415//1 409//1 408//1 +f 466//2 467//2 473//2 +f 415//1 408//1 407//1 +f 465//2 466//2 473//2 +f 421//1 415//1 407//1 +f 465//2 473//2 479//2 +f 421//1 407//1 406//1 +f 464//2 465//2 479//2 +f 421//1 406//1 405//1 +f 463//2 464//2 479//2 +f 399//1 421//1 405//1 +f 463//2 479//2 457//2 +f 399//1 405//1 404//1 +f 462//2 463//2 457//2 +f 399//1 404//1 403//1 +f 461//2 462//2 457//2 +f 399//1 403//1 402//1 +f 460//2 461//2 457//2 +f 399//1 402//1 401//1 +f 459//2 460//2 457//2 +f 401//1 400//1 399//1 +f 457//2 458//2 459//2 +f 399//115 400//115 429//115 428//115 +f 400//116 401//116 430//116 429//116 +f 401//4 402//4 431//4 430//4 +f 402//117 403//117 432//117 431//117 +f 403//118 404//118 433//118 432//118 +f 404//119 405//119 434//119 433//119 +f 405//120 406//120 435//120 434//120 +f 406//121 407//121 436//121 435//121 +f 407//122 408//122 437//122 436//122 +f 408//123 409//123 438//123 437//123 +f 409//124 410//124 439//124 438//124 +f 410//125 411//125 440//125 439//125 +f 411//126 412//126 441//126 440//126 +f 412//127 413//127 442//127 441//127 +f 413//128 414//128 443//128 442//128 +f 414//129 415//129 444//129 443//129 +f 415//130 416//130 445//130 444//130 +f 416//131 417//131 446//131 445//131 +f 417//132 418//132 447//132 446//132 +f 418//11 419//11 448//11 447//11 +f 419//133 420//133 449//133 448//133 +f 420//134 421//134 450//134 449//134 +f 421//135 422//135 451//135 450//135 +f 422//136 423//136 452//136 451//136 +f 423//137 424//137 453//137 452//137 +f 424//138 425//138 454//138 453//138 +f 425//139 426//139 455//139 454//139 +f 426//140 427//140 456//140 455//140 +f 427//141 399//141 428//141 456//141 +f 428//142 429//142 458//142 457//142 +f 429//143 430//143 459//143 458//143 +f 430//144 431//144 460//144 459//144 +f 431//145 432//145 461//145 460//145 +f 432//146 433//146 462//146 461//146 +f 433//147 434//147 463//147 462//147 +f 434//148 435//148 464//148 463//148 +f 435//149 436//149 465//149 464//149 +f 436//150 437//150 466//150 465//150 +f 437//151 438//151 467//151 466//151 +f 438//152 439//152 468//152 467//152 +f 439//153 440//153 469//153 468//153 +f 440//154 441//154 470//154 469//154 +f 441//155 442//155 471//155 470//155 +f 442//156 443//156 472//156 471//156 +f 443//157 444//157 473//157 472//157 +f 444//158 445//158 474//158 473//158 +f 445//159 446//159 475//159 474//159 +f 446//160 447//160 476//160 475//160 +f 447//161 448//161 477//161 476//161 +f 448//162 449//162 478//162 477//162 +f 449//163 450//163 479//163 478//163 +f 450//164 451//164 480//164 479//164 +f 451//165 452//165 481//165 480//165 +f 452//166 453//166 482//166 481//166 +f 453//167 454//167 483//167 482//167 +f 454//168 455//168 484//168 483//168 +f 455//169 456//169 485//169 484//169 +f 456//170 428//170 457//170 485//170 +o letter02Walls +usemtl frame +f 486//1 496//1 495//1 +f 506//2 507//2 497//2 +f 486//1 495//1 494//1 +f 505//2 506//2 497//2 +f 486//1 494//1 493//1 +f 504//2 505//2 497//2 +f 486//1 493//1 492//1 +f 503//2 504//2 497//2 +f 486//1 492//1 491//1 +f 502//2 503//2 497//2 +f 486//1 491//1 490//1 +f 501//2 502//2 497//2 +f 486//1 490//1 489//1 +f 500//2 501//2 497//2 +f 486//1 489//1 488//1 +f 499//2 500//2 497//2 +f 488//1 487//1 486//1 +f 497//2 498//2 499//2 +f 486//171 487//171 498//171 497//171 +f 487//172 488//172 499//172 498//172 +f 488//40 489//40 500//40 499//40 +f 489//173 490//173 501//173 500//173 +f 490//174 491//174 502//174 501//174 +f 491//175 492//175 503//175 502//175 +f 492//176 493//176 504//176 503//176 +f 493//177 494//177 505//177 504//177 +f 494//132 495//132 506//132 505//132 +f 495//178 496//178 507//178 506//178 +f 496//179 486//179 497//179 507//179 +o letter02Face +usemtl dark +f 508//1 518//1 517//1 +f 539//1 540//1 530//1 +f 508//1 517//1 516//1 +f 538//1 539//1 530//1 +f 508//1 516//1 515//1 +f 537//2 538//2 530//2 +f 508//1 515//1 514//1 +f 536//2 537//2 530//2 +f 508//1 514//1 513//1 +f 535//2 536//2 530//2 +f 508//1 513//1 512//1 +f 534//2 535//2 530//2 +f 508//1 512//1 511//1 +f 533//1 534//1 530//1 +f 508//1 511//1 510//1 +f 532//2 533//2 530//2 +f 510//1 509//1 508//1 +f 530//2 531//2 532//2 +f 508//171 509//171 520//171 519//171 +f 509//172 510//172 521//172 520//172 +f 510//40 511//40 522//40 521//40 +f 511//173 512//173 523//173 522//173 +f 512//174 513//174 524//174 523//174 +f 513//175 514//175 525//175 524//175 +f 514//176 515//176 526//176 525//176 +f 515//177 516//177 527//177 526//177 +f 516//132 517//132 528//132 527//132 +f 517//178 518//178 529//178 528//178 +f 518//179 508//179 519//179 529//179 +f 519//180 520//180 531//180 530//180 +f 520//181 521//181 532//181 531//181 +f 521//182 522//182 533//182 532//182 +f 522//183 523//183 534//183 533//183 +f 523//184 524//184 535//184 534//184 +f 524//185 525//185 536//185 535//185 +f 525//186 526//186 537//186 536//186 +f 526//187 527//187 538//187 537//187 +f 527//160 528//160 539//160 538//160 +f 528//188 529//188 540//188 539//188 +f 529//189 519//189 530//189 540//189 +o letter03Walls +usemtl frame +f 541//1 549//1 548//1 +f 557//2 558//2 550//2 +f 541//1 548//1 547//1 +f 556//2 557//2 550//2 +f 541//1 547//1 546//1 +f 555//2 556//2 550//2 +f 541//1 546//1 545//1 +f 554//2 555//2 550//2 +f 541//1 545//1 544//1 +f 553//2 554//2 550//2 +f 541//1 544//1 543//1 +f 552//2 553//2 550//2 +f 543//1 542//1 541//1 +f 550//2 551//2 552//2 +f 541//190 542//190 551//190 550//190 +f 542//191 543//191 552//191 551//191 +f 543//192 544//192 553//192 552//192 +f 544//193 545//193 554//193 553//193 +f 545//194 546//194 555//194 554//194 +f 546//195 547//195 556//195 555//195 +f 547//59 548//59 557//59 556//59 +f 548//196 549//196 558//196 557//196 +f 549//197 541//197 550//197 558//197 +o letter03Face +usemtl dark +f 559//1 567//1 566//1 +f 584//1 585//1 577//1 +f 559//1 566//1 565//1 +f 583//1 584//1 577//1 +f 559//1 565//1 564//1 +f 582//2 583//2 577//2 +f 559//1 564//1 563//1 +f 581//2 582//2 577//2 +f 559//1 563//1 562//1 +f 580//2 581//2 577//2 +f 559//1 562//1 561//1 +f 579//1 580//1 577//1 +f 561//1 560//1 559//1 +f 577//2 578//2 579//2 +f 559//190 560//190 569//190 568//190 +f 560//191 561//191 570//191 569//191 +f 561//192 562//192 571//192 570//192 +f 562//193 563//193 572//193 571//193 +f 563//194 564//194 573//194 572//194 +f 564//195 565//195 574//195 573//195 +f 565//59 566//59 575//59 574//59 +f 566//196 567//196 576//196 575//196 +f 567//197 559//197 568//197 576//197 +f 568//198 569//198 578//198 577//198 +f 569//199 570//199 579//199 578//199 +f 570//200 571//200 580//200 579//200 +f 571//201 572//201 581//201 580//201 +f 572//202 573//202 582//202 581//202 +f 573//203 574//203 583//203 582//203 +f 574//204 575//204 584//204 583//204 +f 575//205 576//205 585//205 584//205 +f 576//206 568//206 577//206 585//206 +o letter04Walls +usemtl frame +f 586//1 603//1 602//1 +f 620//2 621//2 604//2 +f 586//1 602//1 601//1 +f 619//2 620//2 604//2 +f 586//1 601//1 600//1 +f 618//2 619//2 604//2 +f 586//1 600//1 599//1 +f 617//2 618//2 604//2 +f 586//1 599//1 598//1 +f 616//2 617//2 604//2 +f 586//1 598//1 597//1 +f 615//2 616//2 604//2 +f 596//1 595//1 594//1 +f 612//2 613//2 614//2 +f 596//1 594//1 593//1 +f 611//2 612//2 614//2 +f 596//1 593//1 592//1 +f 610//2 611//2 614//2 +f 596//1 592//1 591//1 +f 609//2 610//2 614//2 +f 596//1 591//1 590//1 +f 608//2 609//2 614//2 +f 597//1 596//1 590//1 +f 608//2 614//2 615//2 +f 597//1 590//1 589//1 +f 607//2 608//2 615//2 +f 597//1 589//1 588//1 +f 606//2 607//2 615//2 +f 597//1 588//1 587//1 +f 605//2 606//2 615//2 +f 597//1 587//1 586//1 +f 604//2 605//2 615//2 +f 586//207 587//207 605//207 604//207 +f 587//208 588//208 606//208 605//208 +f 588//209 589//209 607//209 606//209 +f 589//210 590//210 608//210 607//210 +f 590//211 591//211 609//211 608//211 +f 591//212 592//212 610//212 609//212 +f 592//28 593//28 611//28 610//28 +f 593//8 594//8 612//8 611//8 +f 594//213 595//213 613//213 612//213 +f 595//214 596//214 614//214 613//214 +f 596//215 597//215 615//215 614//215 +f 597//216 598//216 616//216 615//216 +f 598//217 599//217 617//217 616//217 +f 599//218 600//218 618//218 617//218 +f 600//219 601//219 619//219 618//219 +f 601//220 602//220 620//220 619//220 +f 602//221 603//221 621//221 620//221 +f 603//222 586//222 604//222 621//222 +o letter04Face +usemtl dark +f 622//1 639//1 638//1 +f 674//1 675//1 658//1 +f 622//1 638//1 637//1 +f 673//2 674//2 658//2 +f 622//1 637//1 636//1 +f 672//2 673//2 658//2 +f 622//1 636//1 635//1 +f 671//2 672//2 658//2 +f 622//1 635//1 634//1 +f 670//2 671//2 658//2 +f 622//1 634//1 633//1 +f 669//2 670//2 658//2 +f 632//1 631//1 630//1 +f 666//2 667//2 668//2 +f 632//1 630//1 629//1 +f 665//1 666//1 668//1 +f 632//1 629//1 628//1 +f 664//2 665//2 668//2 +f 632//1 628//1 627//1 +f 663//2 664//2 668//2 +f 632//1 627//1 626//1 +f 662//2 663//2 668//2 +f 633//1 632//1 626//1 +f 662//2 668//2 669//2 +f 633//1 626//1 625//1 +f 661//2 662//2 669//2 +f 633//1 625//1 624//1 +f 660//2 661//2 669//2 +f 633//1 624//1 623//1 +f 659//2 660//2 669//2 +f 633//1 623//1 622//1 +f 658//2 659//2 669//2 +f 622//207 623//207 641//207 640//207 +f 623//208 624//208 642//208 641//208 +f 624//209 625//209 643//209 642//209 +f 625//210 626//210 644//210 643//210 +f 626//211 627//211 645//211 644//211 +f 627//212 628//212 646//212 645//212 +f 628//28 629//28 647//28 646//28 +f 629//8 630//8 648//8 647//8 +f 630//213 631//213 649//213 648//213 +f 631//214 632//214 650//214 649//214 +f 632//215 633//215 651//215 650//215 +f 633//216 634//216 652//216 651//216 +f 634//217 635//217 653//217 652//217 +f 635//218 636//218 654//218 653//218 +f 636//219 637//219 655//219 654//219 +f 637//220 638//220 656//220 655//220 +f 638//221 639//221 657//221 656//221 +f 639//222 622//222 640//222 657//222 +f 640//223 641//223 659//223 658//223 +f 641//224 642//224 660//224 659//224 +f 642//225 643//225 661//225 660//225 +f 643//226 644//226 662//226 661//226 +f 644//227 645//227 663//227 662//227 +f 645//228 646//228 664//228 663//228 +f 646//229 647//229 665//229 664//229 +f 647//230 648//230 666//230 665//230 +f 648//231 649//231 667//231 666//231 +f 649//232 650//232 668//232 667//232 +f 650//233 651//233 669//233 668//233 +f 651//234 652//234 670//234 669//234 +f 652//235 653//235 671//235 670//235 +f 653//236 654//236 672//236 671//236 +f 654//237 655//237 673//237 672//237 +f 655//238 656//238 674//238 673//238 +f 656//239 657//239 675//239 674//239 +f 657//240 640//240 658//240 675//240 +o letter05Walls +usemtl frame +f 676//1 727//1 726//1 +f 778//2 779//2 728//2 +f 676//1 726//1 725//1 +f 777//2 778//2 728//2 +f 676//1 725//1 724//1 +f 776//2 777//2 728//2 +f 676//1 724//1 723//1 +f 775//2 776//2 728//2 +f 676//1 723//1 722//1 +f 774//2 775//2 728//2 +f 676//1 722//1 721//1 +f 773//2 774//2 728//2 +f 676//1 721//1 720//1 +f 772//2 773//2 728//2 +f 676//1 720//1 719//1 +f 771//2 772//2 728//2 +f 676//1 719//1 718//1 +f 770//2 771//2 728//2 +f 676//1 718//1 717//1 +f 769//2 770//2 728//2 +f 676//1 717//1 716//1 +f 768//2 769//2 728//2 +f 676//1 716//1 715//1 +f 767//2 768//2 728//2 +f 676//1 715//1 714//1 +f 766//2 767//2 728//2 +f 676//1 714//1 713//1 +f 765//2 766//2 728//2 +f 676//1 713//1 712//1 +f 764//2 765//2 728//2 +f 701//1 700//1 699//1 +f 751//2 752//2 753//2 +f 701//1 699//1 698//1 +f 750//2 751//2 753//2 +f 701//1 698//1 697//1 +f 749//2 750//2 753//2 +f 702//1 701//1 697//1 +f 749//2 753//2 754//2 +f 703//1 702//1 697//1 +f 749//2 754//2 755//2 +f 703//1 697//1 696//1 +f 748//2 749//2 755//2 +f 704//1 703//1 696//1 +f 748//2 755//2 756//2 +f 704//1 696//1 695//1 +f 747//2 748//2 756//2 +f 704//1 695//1 694//1 +f 746//2 747//2 756//2 +f 705//1 704//1 694//1 +f 746//2 756//2 757//2 +f 706//1 705//1 694//1 +f 746//2 757//2 758//2 +f 694//1 693//1 692//1 +f 744//2 745//2 746//2 +f 694//1 692//1 691//1 +f 743//2 744//2 746//2 +f 706//1 694//1 691//1 +f 743//2 746//2 758//2 +f 707//1 706//1 691//1 +f 743//2 758//2 759//2 +f 708//1 707//1 691//1 +f 743//2 759//2 760//2 +f 708//1 691//1 690//1 +f 742//2 743//2 760//2 +f 709//1 708//1 690//1 +f 742//2 760//2 761//2 +f 709//1 690//1 689//1 +f 741//2 742//2 761//2 +f 710//1 709//1 689//1 +f 741//2 761//2 762//2 +f 710//1 689//1 688//1 +f 740//2 741//2 762//2 +f 710//1 688//1 687//1 +f 739//2 740//2 762//2 +f 711//1 710//1 687//1 +f 739//2 762//2 763//2 +f 711//1 687//1 686//1 +f 738//2 739//2 763//2 +f 712//1 711//1 686//1 +f 738//2 763//2 764//2 +f 676//1 712//1 686//1 +f 738//2 764//2 728//2 +f 676//1 686//1 685//1 +f 737//2 738//2 728//2 +f 676//1 685//1 684//1 +f 736//2 737//2 728//2 +f 676//1 684//1 683//1 +f 735//2 736//2 728//2 +f 676//1 683//1 682//1 +f 734//2 735//2 728//2 +f 676//1 682//1 681//1 +f 733//2 734//2 728//2 +f 676//1 681//1 680//1 +f 732//2 733//2 728//2 +f 676//1 680//1 679//1 +f 731//2 732//2 728//2 +f 676//1 679//1 678//1 +f 730//2 731//2 728//2 +f 678//1 677//1 676//1 +f 728//2 729//2 730//2 +f 676//241 677//241 729//241 728//241 +f 677//242 678//242 730//242 729//242 +f 678//243 679//243 731//243 730//243 +f 679//244 680//244 732//244 731//244 +f 680//245 681//245 733//245 732//245 +f 681//246 682//246 734//246 733//246 +f 682//247 683//247 735//247 734//247 +f 683//248 684//248 736//248 735//248 +f 684//249 685//249 737//249 736//249 +f 685//250 686//250 738//250 737//250 +f 686//251 687//251 739//251 738//251 +f 687//252 688//252 740//252 739//252 +f 688//253 689//253 741//253 740//253 +f 689//254 690//254 742//254 741//254 +f 690//255 691//255 743//255 742//255 +f 691//256 692//256 744//256 743//256 +f 692//257 693//257 745//257 744//257 +f 693//52 694//52 746//52 745//52 +f 694//258 695//258 747//258 746//258 +f 695//20 696//20 748//20 747//20 +f 696//259 697//259 749//259 748//259 +f 697//260 698//260 750//260 749//260 +f 698//261 699//261 751//261 750//261 +f 699//262 700//262 752//262 751//262 +f 700//263 701//263 753//263 752//263 +f 701//264 702//264 754//264 753//264 +f 702//265 703//265 755//265 754//265 +f 703//266 704//266 756//266 755//266 +f 704//267 705//267 757//267 756//267 +f 705//268 706//268 758//268 757//268 +f 706//269 707//269 759//269 758//269 +f 707//270 708//270 760//270 759//270 +f 708//271 709//271 761//271 760//271 +f 709//272 710//272 762//272 761//272 +f 710//273 711//273 763//273 762//273 +f 711//274 712//274 764//274 763//274 +f 712//275 713//275 765//275 764//275 +f 713//276 714//276 766//276 765//276 +f 714//277 715//277 767//277 766//277 +f 715//278 716//278 768//278 767//278 +f 716//279 717//279 769//279 768//279 +f 717//280 718//280 770//280 769//280 +f 718//281 719//281 771//281 770//281 +f 719//282 720//282 772//282 771//282 +f 720//8 721//8 773//8 772//8 +f 721//283 722//283 774//283 773//283 +f 722//132 723//132 775//132 774//132 +f 723//284 724//284 776//284 775//284 +f 724//285 725//285 777//285 776//285 +f 725//286 726//286 778//286 777//286 +f 726//287 727//287 779//287 778//287 +f 727//288 676//288 728//288 779//288 +o letter05Face +usemtl dark +f 780//1 831//1 830//1 +f 934//2 935//2 884//2 +f 780//1 830//1 829//1 +f 933//2 934//2 884//2 +f 780//1 829//1 828//1 +f 932//2 933//2 884//2 +f 780//1 828//1 827//1 +f 931//2 932//2 884//2 +f 780//1 827//1 826//1 +f 930//2 931//2 884//2 +f 780//1 826//1 825//1 +f 929//2 930//2 884//2 +f 780//1 825//1 824//1 +f 928//1 929//1 884//1 +f 780//1 824//1 823//1 +f 927//1 928//1 884//1 +f 780//1 823//1 822//1 +f 926//2 927//2 884//2 +f 780//1 822//1 821//1 +f 925//2 926//2 884//2 +f 780//1 821//1 820//1 +f 924//2 925//2 884//2 +f 780//1 820//1 819//1 +f 923//2 924//2 884//2 +f 780//1 819//1 818//1 +f 922//2 923//2 884//2 +f 780//1 818//1 817//1 +f 921//2 922//2 884//2 +f 780//1 817//1 816//1 +f 920//2 921//2 884//2 +f 805//1 804//1 803//1 +f 907//2 908//2 909//2 +f 805//1 803//1 802//1 +f 906//2 907//2 909//2 +f 805//1 802//1 801//1 +f 905//2 906//2 909//2 +f 806//1 805//1 801//1 +f 905//2 909//2 910//2 +f 807//1 806//1 801//1 +f 905//2 910//2 911//2 +f 807//1 801//1 800//1 +f 904//2 905//2 911//2 +f 808//1 807//1 800//1 +f 904//2 911//2 912//2 +f 808//1 800//1 799//1 +f 903//1 904//1 912//1 +f 808//1 799//1 798//1 +f 902//2 903//2 912//2 +f 809//1 808//1 798//1 +f 902//2 912//2 913//2 +f 810//1 809//1 798//1 +f 902//2 913//2 914//2 +f 798//1 797//1 796//1 +f 900//1 901//1 902//1 +f 798//1 796//1 795//1 +f 899//2 900//2 902//2 +f 810//1 798//1 795//1 +f 899//1 902//1 914//1 +f 811//1 810//1 795//1 +f 899//2 914//2 915//2 +f 812//1 811//1 795//1 +f 899//2 915//2 916//2 +f 812//1 795//1 794//1 +f 898//2 899//2 916//2 +f 813//1 812//1 794//1 +f 898//2 916//2 917//2 +f 813//1 794//1 793//1 +f 897//2 898//2 917//2 +f 814//1 813//1 793//1 +f 897//2 917//2 918//2 +f 814//1 793//1 792//1 +f 896//2 897//2 918//2 +f 814//1 792//1 791//1 +f 895//2 896//2 918//2 +f 815//1 814//1 791//1 +f 895//2 918//2 919//2 +f 815//1 791//1 790//1 +f 894//2 895//2 919//2 +f 816//1 815//1 790//1 +f 894//2 919//2 920//2 +f 780//1 816//1 790//1 +f 894//1 920//1 884//1 +f 780//1 790//1 789//1 +f 893//2 894//2 884//2 +f 780//1 789//1 788//1 +f 892//2 893//2 884//2 +f 780//1 788//1 787//1 +f 891//2 892//2 884//2 +f 780//1 787//1 786//1 +f 890//2 891//2 884//2 +f 780//1 786//1 785//1 +f 889//2 890//2 884//2 +f 780//1 785//1 784//1 +f 888//2 889//2 884//2 +f 780//1 784//1 783//1 +f 887//2 888//2 884//2 +f 780//1 783//1 782//1 +f 886//2 887//2 884//2 +f 782//1 781//1 780//1 +f 884//2 885//2 886//2 +f 780//241 781//241 833//241 832//241 +f 781//242 782//242 834//242 833//242 +f 782//243 783//243 835//243 834//243 +f 783//244 784//244 836//244 835//244 +f 784//245 785//245 837//245 836//245 +f 785//246 786//246 838//246 837//246 +f 786//247 787//247 839//247 838//247 +f 787//248 788//248 840//248 839//248 +f 788//249 789//249 841//249 840//249 +f 789//250 790//250 842//250 841//250 +f 790//251 791//251 843//251 842//251 +f 791//252 792//252 844//252 843//252 +f 792//253 793//253 845//253 844//253 +f 793//254 794//254 846//254 845//254 +f 794//255 795//255 847//255 846//255 +f 795//256 796//256 848//256 847//256 +f 796//257 797//257 849//257 848//257 +f 797//52 798//52 850//52 849//52 +f 798//258 799//258 851//258 850//258 +f 799//20 800//20 852//20 851//20 +f 800//259 801//259 853//259 852//259 +f 801//260 802//260 854//260 853//260 +f 802//261 803//261 855//261 854//261 +f 803//262 804//262 856//262 855//262 +f 804//263 805//263 857//263 856//263 +f 805//264 806//264 858//264 857//264 +f 806//265 807//265 859//265 858//265 +f 807//266 808//266 860//266 859//266 +f 808//267 809//267 861//267 860//267 +f 809//268 810//268 862//268 861//268 +f 810//269 811//269 863//269 862//269 +f 811//270 812//270 864//270 863//270 +f 812//271 813//271 865//271 864//271 +f 813//272 814//272 866//272 865//272 +f 814//273 815//273 867//273 866//273 +f 815//274 816//274 868//274 867//274 +f 816//275 817//275 869//275 868//275 +f 817//276 818//276 870//276 869//276 +f 818//277 819//277 871//277 870//277 +f 819//278 820//278 872//278 871//278 +f 820//279 821//279 873//279 872//279 +f 821//280 822//280 874//280 873//280 +f 822//281 823//281 875//281 874//281 +f 823//282 824//282 876//282 875//282 +f 824//8 825//8 877//8 876//8 +f 825//283 826//283 878//283 877//283 +f 826//132 827//132 879//132 878//132 +f 827//284 828//284 880//284 879//284 +f 828//285 829//285 881//285 880//285 +f 829//286 830//286 882//286 881//286 +f 830//287 831//287 883//287 882//287 +f 831//288 780//288 832//288 883//288 +f 832//289 833//289 885//289 884//289 +f 833//290 834//290 886//290 885//290 +f 834//291 835//291 887//291 886//291 +f 835//292 836//292 888//292 887//292 +f 836//293 837//293 889//293 888//293 +f 837//294 838//294 890//294 889//294 +f 838//295 839//295 891//295 890//295 +f 839//296 840//296 892//296 891//296 +f 840//297 841//297 893//297 892//297 +f 841//298 842//298 894//298 893//298 +f 842//299 843//299 895//299 894//299 +f 843//300 844//300 896//300 895//300 +f 844//301 845//301 897//301 896//301 +f 845//302 846//302 898//302 897//302 +f 846//303 847//303 899//303 898//303 +f 847//304 848//304 900//304 899//304 +f 848//305 849//305 901//305 900//305 +f 849//306 850//306 902//306 901//306 +f 850//307 851//307 903//307 902//307 +f 851//308 852//308 904//308 903//308 +f 852//309 853//309 905//309 904//309 +f 853//310 854//310 906//310 905//310 +f 854//311 855//311 907//311 906//311 +f 855//312 856//312 908//312 907//312 +f 856//313 857//313 909//313 908//313 +f 857//314 858//314 910//314 909//314 +f 858//315 859//315 911//315 910//315 +f 859//316 860//316 912//316 911//316 +f 860//317 861//317 913//317 912//317 +f 861//318 862//318 914//318 913//318 +f 862//319 863//319 915//319 914//319 +f 863//320 864//320 916//320 915//320 +f 864//321 865//321 917//321 916//321 +f 865//322 866//322 918//322 917//322 +f 866//323 867//323 919//323 918//323 +f 867//324 868//324 920//324 919//324 +f 868//325 869//325 921//325 920//325 +f 869//326 870//326 922//326 921//326 +f 870//327 871//327 923//327 922//327 +f 871//328 872//328 924//328 923//328 +f 872//329 873//329 925//329 924//329 +f 873//330 874//330 926//330 925//330 +f 874//331 875//331 927//331 926//331 +f 875//332 876//332 928//332 927//332 +f 876//230 877//230 929//230 928//230 +f 877//333 878//333 930//333 929//333 +f 878//334 879//334 931//334 930//334 +f 879//335 880//335 932//335 931//335 +f 880//336 881//336 933//336 932//336 +f 881//337 882//337 934//337 933//337 +f 882//338 883//338 935//338 934//338 +f 883//339 832//339 884//339 935//339 +o letter06Walls +usemtl frame +f 936//1 962//1 961//1 +f 988//2 989//2 963//2 +f 936//1 961//1 960//1 +f 987//2 988//2 963//2 +f 936//1 960//1 959//1 +f 986//2 987//2 963//2 +f 936//1 959//1 958//1 +f 985//2 986//2 963//2 +f 936//1 958//1 957//1 +f 984//2 985//2 963//2 +f 957//1 956//1 955//1 +f 982//2 983//2 984//2 +f 957//1 955//1 954//1 +f 981//2 982//2 984//2 +f 957//1 954//1 953//1 +f 980//2 981//2 984//2 +f 957//1 953//1 952//1 +f 979//2 980//2 984//2 +f 936//1 957//1 952//1 +f 979//2 984//2 963//2 +f 936//1 952//1 951//1 +f 978//2 979//2 963//2 +f 950//1 949//1 948//1 +f 975//2 976//2 977//2 +f 950//1 948//1 947//1 +f 974//2 975//2 977//2 +f 950//1 947//1 946//1 +f 973//2 974//2 977//2 +f 950//1 946//1 945//1 +f 972//2 973//2 977//2 +f 950//1 945//1 944//1 +f 971//2 972//2 977//2 +f 951//1 950//1 944//1 +f 971//2 977//2 978//2 +f 936//1 951//1 944//1 +f 971//2 978//2 963//2 +f 936//1 944//1 943//1 +f 970//2 971//2 963//2 +f 936//1 943//1 942//1 +f 969//2 970//2 963//2 +f 936//1 942//1 941//1 +f 968//2 969//2 963//2 +f 936//1 941//1 940//1 +f 967//2 968//2 963//2 +f 936//1 940//1 939//1 +f 966//2 967//2 963//2 +f 936//1 939//1 938//1 +f 965//2 966//2 963//2 +f 938//1 937//1 936//1 +f 963//2 964//2 965//2 +f 936//340 937//340 964//340 963//340 +f 937//341 938//341 965//341 964//341 +f 938//342 939//342 966//342 965//342 +f 939//343 940//343 967//343 966//343 +f 940//344 941//344 968//344 967//344 +f 941//345 942//345 969//345 968//345 +f 942//346 943//346 970//346 969//346 +f 943//347 944//347 971//347 970//347 +f 944//348 945//348 972//348 971//348 +f 945//349 946//349 973//349 972//349 +f 946//350 947//350 974//350 973//350 +f 947//351 948//351 975//351 974//351 +f 948//352 949//352 976//352 975//352 +f 949//353 950//353 977//353 976//353 +f 950//354 951//354 978//354 977//354 +f 951//355 952//355 979//355 978//355 +f 952//356 953//356 980//356 979//356 +f 953//357 954//357 981//357 980//357 +f 954//358 955//358 982//358 981//358 +f 955//359 956//359 983//359 982//359 +f 956//360 957//360 984//360 983//360 +f 957//361 958//361 985//361 984//361 +f 958//362 959//362 986//362 985//362 +f 959//363 960//363 987//363 986//363 +f 960//364 961//364 988//364 987//364 +f 961//359 962//359 989//359 988//359 +f 962//365 936//365 963//365 989//365 +o letter06Face +usemtl dark +f 990//1 1016//1 1015//1 +f 1069//1 1070//1 1044//1 +f 990//1 1015//1 1014//1 +f 1068//2 1069//2 1044//2 +f 990//1 1014//1 1013//1 +f 1067//2 1068//2 1044//2 +f 990//1 1013//1 1012//1 +f 1066//2 1067//2 1044//2 +f 990//1 1012//1 1011//1 +f 1065//2 1066//2 1044//2 +f 1011//1 1010//1 1009//1 +f 1063//2 1064//2 1065//2 +f 1011//1 1009//1 1008//1 +f 1062//1 1063//1 1065//1 +f 1011//1 1008//1 1007//1 +f 1061//2 1062//2 1065//2 +f 1011//1 1007//1 1006//1 +f 1060//2 1061//2 1065//2 +f 990//1 1011//1 1006//1 +f 1060//2 1065//2 1044//2 +f 990//1 1006//1 1005//1 +f 1059//2 1060//2 1044//2 +f 1004//1 1003//1 1002//1 +f 1056//2 1057//2 1058//2 +f 1004//1 1002//1 1001//1 +f 1055//1 1056//1 1058//1 +f 1004//1 1001//1 1000//1 +f 1054//2 1055//2 1058//2 +f 1004//1 1000//1 999//1 +f 1053//2 1054//2 1058//2 +f 1004//1 999//1 998//1 +f 1052//2 1053//2 1058//2 +f 1005//1 1004//1 998//1 +f 1052//2 1058//2 1059//2 +f 990//1 1005//1 998//1 +f 1052//2 1059//2 1044//2 +f 990//1 998//1 997//1 +f 1051//1 1052//1 1044//1 +f 990//1 997//1 996//1 +f 1050//2 1051//2 1044//2 +f 990//1 996//1 995//1 +f 1049//2 1050//2 1044//2 +f 990//1 995//1 994//1 +f 1048//2 1049//2 1044//2 +f 990//1 994//1 993//1 +f 1047//2 1048//2 1044//2 +f 990//1 993//1 992//1 +f 1046//1 1047//1 1044//1 +f 992//1 991//1 990//1 +f 1044//2 1045//2 1046//2 +f 990//340 991//340 1018//340 1017//340 +f 991//341 992//341 1019//341 1018//341 +f 992//342 993//342 1020//342 1019//342 +f 993//343 994//343 1021//343 1020//343 +f 994//344 995//344 1022//344 1021//344 +f 995//345 996//345 1023//345 1022//345 +f 996//346 997//346 1024//346 1023//346 +f 997//347 998//347 1025//347 1024//347 +f 998//348 999//348 1026//348 1025//348 +f 999//349 1000//349 1027//349 1026//349 +f 1000//350 1001//350 1028//350 1027//350 +f 1001//351 1002//351 1029//351 1028//351 +f 1002//352 1003//352 1030//352 1029//352 +f 1003//353 1004//353 1031//353 1030//353 +f 1004//354 1005//354 1032//354 1031//354 +f 1005//355 1006//355 1033//355 1032//355 +f 1006//356 1007//356 1034//356 1033//356 +f 1007//357 1008//357 1035//357 1034//357 +f 1008//358 1009//358 1036//358 1035//358 +f 1009//359 1010//359 1037//359 1036//359 +f 1010//360 1011//360 1038//360 1037//360 +f 1011//361 1012//361 1039//361 1038//361 +f 1012//362 1013//362 1040//362 1039//362 +f 1013//363 1014//363 1041//363 1040//363 +f 1014//364 1015//364 1042//364 1041//364 +f 1015//359 1016//359 1043//359 1042//359 +f 1016//365 990//365 1017//365 1043//365 +f 1017//366 1018//366 1045//366 1044//366 +f 1018//367 1019//367 1046//367 1045//367 +f 1019//368 1020//368 1047//368 1046//368 +f 1020//369 1021//369 1048//369 1047//369 +f 1021//370 1022//370 1049//370 1048//370 +f 1022//371 1023//371 1050//371 1049//371 +f 1023//372 1024//372 1051//372 1050//372 +f 1024//373 1025//373 1052//373 1051//373 +f 1025//374 1026//374 1053//374 1052//374 +f 1026//375 1027//375 1054//375 1053//375 +f 1027//376 1028//376 1055//376 1054//376 +f 1028//377 1029//377 1056//377 1055//377 +f 1029//378 1030//378 1057//378 1056//378 +f 1030//379 1031//379 1058//379 1057//379 +f 1031//380 1032//380 1059//380 1058//380 +f 1032//381 1033//381 1060//381 1059//381 +f 1033//382 1034//382 1061//382 1060//382 +f 1034//383 1035//383 1062//383 1061//383 +f 1035//384 1036//384 1063//384 1062//384 +f 1036//385 1037//385 1064//385 1063//385 +f 1037//386 1038//386 1065//386 1064//386 +f 1038//387 1039//387 1066//387 1065//387 +f 1039//388 1040//388 1067//388 1066//388 +f 1040//389 1041//389 1068//389 1067//389 +f 1041//390 1042//390 1069//390 1068//390 +f 1042//385 1043//385 1070//385 1069//385 +f 1043//391 1017//391 1044//391 1070//391 +o head +usemtl frame +f 1071//9 1072//9 1100//9 1099//9 +f 1072//392 1073//392 1101//392 1100//392 +f 1073//9 1074//9 1102//9 1101//9 +f 1074//393 1075//393 1103//393 1102//393 +f 1075//9 1076//9 1104//9 1103//9 +f 1076//394 1077//394 1105//394 1104//394 +f 1077//9 1078//9 1106//9 1105//9 +f 1078//395 1079//395 1107//395 1106//395 +f 1079//105 1080//105 1108//105 1107//105 +f 1080//13 1081//13 1109//13 1108//13 +f 1081//106 1082//106 1110//106 1109//106 +f 1082//11 1083//11 1111//11 1110//11 +f 1083//268 1084//268 1112//268 1111//268 +f 1084//396 1085//396 1113//396 1112//396 +f 1085//11 1086//11 1114//11 1113//11 +f 1086//13 1087//13 1115//13 1114//13 +f 1087//397 1088//397 1116//397 1115//397 +f 1088//398 1089//398 1117//398 1116//398 +f 1089//13 1090//13 1118//13 1117//13 +f 1090//399 1091//399 1119//399 1118//399 +f 1091//400 1092//400 1120//400 1119//400 +f 1092//401 1093//401 1121//401 1120//401 +f 1093//4 1094//4 1122//4 1121//4 +f 1094//209 1095//209 1123//209 1122//209 +f 1095//402 1096//402 1124//402 1123//402 +f 1096//111 1097//111 1125//111 1124//111 +f 1097//403 1098//403 1126//403 1125//403 +f 1098//4 1071//4 1099//4 1126//4 +f 1071//1 1098//1 1097//1 +f 1125//2 1126//2 1099//2 +f 1071//1 1097//1 1096//1 +f 1124//2 1125//2 1099//2 +f 1071//1 1096//1 1095//1 +f 1123//2 1124//2 1099//2 +f 1093//1 1092//1 1091//1 +f 1119//2 1120//2 1121//2 +f 1094//1 1093//1 1091//1 +f 1119//2 1121//2 1122//2 +f 1095//1 1094//1 1091//1 +f 1119//2 1122//2 1123//2 +f 1071//1 1095//1 1091//1 +f 1119//2 1123//2 1099//2 +f 1091//1 1090//1 1089//1 +f 1117//2 1118//2 1119//2 +f 1091//1 1089//1 1088//1 +f 1116//2 1117//2 1119//2 +f 1071//1 1091//1 1088//1 +f 1116//2 1119//2 1099//2 +f 1071//1 1088//1 1087//1 +f 1115//2 1116//2 1099//2 +f 1071//1 1087//1 1086//1 +f 1114//2 1115//2 1099//2 +f 1071//1 1086//1 1085//1 +f 1113//2 1114//2 1099//2 +f 1084//1 1083//1 1082//1 +f 1110//2 1111//2 1112//2 +f 1085//1 1084//1 1082//1 +f 1110//2 1112//2 1113//2 +f 1085//1 1082//1 1081//1 +f 1109//2 1110//2 1113//2 +f 1085//1 1081//1 1080//1 +f 1108//2 1109//2 1113//2 +f 1085//1 1080//1 1079//1 +f 1107//2 1108//2 1113//2 +f 1085//1 1079//1 1078//1 +f 1106//2 1107//2 1113//2 +f 1085//1 1078//1 1077//1 +f 1105//2 1106//2 1113//2 +f 1085//1 1077//1 1076//1 +f 1104//2 1105//2 1113//2 +f 1085//1 1076//1 1075//1 +f 1103//2 1104//2 1113//2 +f 1085//1 1075//1 1074//1 +f 1102//2 1103//2 1113//2 +f 1085//1 1074//1 1073//1 +f 1101//2 1102//2 1113//2 +f 1071//1 1085//1 1073//1 +f 1101//2 1113//2 1099//2 +f 1073//1 1072//1 1071//1 +f 1099//2 1100//2 1101//2 +usemtl glass +f 1127//1 1139//1 1138//1 +f 1164//2 1165//2 1153//2 +f 1127//1 1138//1 1137//1 +f 1163//2 1164//2 1153//2 +f 1127//1 1137//1 1136//1 +f 1162//2 1163//2 1153//2 +f 1127//1 1136//1 1135//1 +f 1161//2 1162//2 1153//2 +f 1127//1 1135//1 1134//1 +f 1160//2 1161//2 1153//2 +f 1127//1 1134//1 1133//1 +f 1159//2 1160//2 1153//2 +f 1127//1 1133//1 1132//1 +f 1158//2 1159//2 1153//2 +f 1127//1 1132//1 1131//1 +f 1157//2 1158//2 1153//2 +f 1131//1 1130//1 1129//1 +f 1155//2 1156//2 1157//2 +f 1127//1 1131//1 1129//1 +f 1155//2 1157//2 1153//2 +f 1129//1 1128//1 1127//1 +f 1153//2 1154//2 1155//2 +f 1127//9 1128//9 1141//9 1140//9 +f 1128//392 1129//392 1142//392 1141//392 +f 1129//9 1130//9 1143//9 1142//9 +f 1130//404 1131//404 1144//404 1143//404 +f 1131//405 1132//405 1145//405 1144//405 +f 1132//406 1133//406 1146//406 1145//406 +f 1133//407 1134//407 1147//407 1146//407 +f 1134//408 1135//408 1148//408 1147//408 +f 1135//409 1136//409 1149//409 1148//409 +f 1136//410 1137//410 1150//410 1149//410 +f 1137//411 1138//411 1151//411 1150//411 +f 1138//403 1139//403 1152//403 1151//403 +f 1139//4 1127//4 1140//4 1152//4 +f 1140//412 1141//412 1154//412 1153//412 +f 1141//413 1142//413 1155//413 1154//413 +f 1142//412 1143//412 1156//412 1155//412 +f 1143//414 1144//414 1157//414 1156//414 +f 1144//415 1145//415 1158//415 1157//415 +f 1145//416 1146//416 1159//416 1158//416 +f 1146//417 1147//417 1160//417 1159//417 +f 1147//418 1148//418 1161//418 1160//418 +f 1148//419 1149//419 1162//419 1161//419 +f 1149//420 1150//420 1163//420 1162//420 +f 1150//421 1151//421 1164//421 1163//421 +f 1151//422 1152//422 1165//422 1164//422 +f 1152//423 1140//423 1153//423 1165//423 +usemtl champagne +f 1166//1 1187//1 1186//1 +f 1230//2 1231//2 1210//2 +f 1166//1 1186//1 1185//1 +f 1229//2 1230//2 1210//2 +f 1166//1 1185//1 1184//1 +f 1228//2 1229//2 1210//2 +f 1184//1 1183//1 1182//1 +f 1226//2 1227//2 1228//2 +f 1181//1 1180//1 1179//1 +f 1223//2 1224//2 1225//2 +f 1182//1 1181//1 1179//1 +f 1223//2 1225//2 1226//2 +f 1184//1 1182//1 1179//1 +f 1223//2 1226//2 1228//2 +f 1166//1 1184//1 1179//1 +f 1223//2 1228//2 1210//2 +f 1166//1 1179//1 1178//1 +f 1222//2 1223//2 1210//2 +f 1166//1 1178//1 1177//1 +f 1221//2 1222//2 1210//2 +f 1177//1 1176//1 1175//1 +f 1219//2 1220//2 1221//2 +f 1166//1 1177//1 1175//1 +f 1219//2 1221//2 1210//2 +f 1166//1 1175//1 1174//1 +f 1218//2 1219//2 1210//2 +f 1166//1 1174//1 1173//1 +f 1217//2 1218//2 1210//2 +f 1166//1 1173//1 1172//1 +f 1216//2 1217//2 1210//2 +f 1172//1 1171//1 1170//1 +f 1214//2 1215//2 1216//2 +f 1166//1 1172//1 1170//1 +f 1214//2 1216//2 1210//2 +f 1166//1 1170//1 1169//1 +f 1213//2 1214//2 1210//2 +f 1166//1 1169//1 1168//1 +f 1212//2 1213//2 1210//2 +f 1168//1 1167//1 1166//1 +f 1210//2 1211//2 1212//2 +f 1166//424 1167//424 1189//424 1188//424 +f 1167//425 1168//425 1190//425 1189//425 +f 1168//9 1169//9 1191//9 1190//9 +f 1169//393 1170//393 1192//393 1191//393 +f 1170//9 1171//9 1193//9 1192//9 +f 1171//426 1172//426 1194//426 1193//426 +f 1172//8 1173//8 1195//8 1194//8 +f 1173//13 1174//13 1196//13 1195//13 +f 1174//397 1175//397 1197//397 1196//397 +f 1175//427 1176//427 1198//427 1197//427 +f 1176//428 1177//428 1199//428 1198//428 +f 1177//8 1178//8 1200//8 1199//8 +f 1178//59 1179//59 1201//59 1200//59 +f 1179//107 1180//107 1202//107 1201//107 +f 1180//429 1181//429 1203//429 1202//429 +f 1181//359 1182//359 1204//359 1203//359 +f 1182//430 1183//430 1205//430 1204//430 +f 1183//431 1184//431 1206//431 1205//431 +f 1184//432 1185//432 1207//432 1206//432 +f 1185//433 1186//433 1208//433 1207//433 +f 1186//359 1187//359 1209//359 1208//359 +f 1187//434 1166//434 1188//434 1209//434 +f 1188//435 1189//435 1211//435 1210//435 +f 1189//436 1190//436 1212//436 1211//436 +f 1190//437 1191//437 1213//437 1212//437 +f 1191//438 1192//438 1214//438 1213//438 +f 1192//437 1193//437 1215//437 1214//437 +f 1193//439 1194//439 1216//439 1215//439 +f 1194//230 1195//230 1217//230 1216//230 +f 1195//440 1196//440 1218//440 1217//440 +f 1196//441 1197//441 1219//441 1218//441 +f 1197//442 1198//442 1220//442 1219//442 +f 1198//443 1199//443 1221//443 1220//443 +f 1199//230 1200//230 1222//230 1221//230 +f 1200//204 1201//204 1223//204 1222//204 +f 1201//444 1202//444 1224//444 1223//444 +f 1202//445 1203//445 1225//445 1224//445 +f 1203//385 1204//385 1226//385 1225//385 +f 1204//446 1205//446 1227//446 1226//446 +f 1205//447 1206//447 1228//447 1227//447 +f 1206//448 1207//448 1229//448 1228//448 +f 1207//449 1208//449 1230//449 1229//449 +f 1208//385 1209//385 1231//385 1230//385 +f 1209//450 1188//450 1210//450 1231//450 +f 1232//1 1245//1 1244//1 +f 1272//2 1273//2 1260//2 +f 1232//1 1244//1 1243//1 +f 1271//2 1272//2 1260//2 +f 1232//1 1243//1 1242//1 +f 1270//2 1271//2 1260//2 +f 1232//1 1242//1 1241//1 +f 1269//2 1270//2 1260//2 +f 1232//1 1241//1 1240//1 +f 1268//2 1269//2 1260//2 +f 1239//1 1238//1 1237//1 +f 1265//2 1266//2 1267//2 +f 1239//1 1237//1 1236//1 +f 1264//2 1265//2 1267//2 +f 1240//1 1239//1 1236//1 +f 1264//2 1267//2 1268//2 +f 1240//1 1236//1 1235//1 +f 1263//2 1264//2 1268//2 +f 1232//1 1240//1 1235//1 +f 1263//1 1268//1 1260//1 +f 1235//1 1234//1 1233//1 +f 1261//2 1262//2 1263//2 +f 1235//1 1233//1 1232//1 +f 1260//2 1261//2 1263//2 +f 1232//28 1233//28 1247//28 1246//28 +f 1233//4 1234//4 1248//4 1247//4 +f 1234//451 1235//451 1249//451 1248//451 +f 1235//28 1236//28 1250//28 1249//28 +f 1236//452 1237//452 1251//452 1250//452 +f 1237//453 1238//453 1252//453 1251//453 +f 1238//9 1239//9 1253//9 1252//9 +f 1239//454 1240//454 1254//454 1253//454 +f 1240//9 1241//9 1255//9 1254//9 +f 1241//455 1242//455 1256//455 1255//455 +f 1242//399 1243//399 1257//399 1256//399 +f 1243//456 1244//456 1258//456 1257//456 +f 1244//107 1245//107 1259//107 1258//107 +f 1245//457 1232//457 1246//457 1259//457 +f 1246//229 1247//229 1261//229 1260//229 +f 1247//144 1248//144 1262//144 1261//144 +f 1248//458 1249//458 1263//458 1262//458 +f 1249//229 1250//229 1264//229 1263//229 +f 1250//459 1251//459 1265//459 1264//459 +f 1251//460 1252//460 1266//460 1265//460 +f 1252//437 1253//437 1267//437 1266//437 +f 1253//461 1254//461 1268//461 1267//461 +f 1254//437 1255//437 1269//437 1268//437 +f 1255//462 1256//462 1270//462 1269//462 +f 1256//463 1257//463 1271//463 1270//463 +f 1257//464 1258//464 1272//464 1271//464 +f 1258//444 1259//444 1273//444 1272//444 +f 1259//465 1246//465 1260//465 1273//465 +f 1316//1 1328//1 1327//1 +f 1353//2 1354//2 1342//2 +f 1327//1 1326//1 1325//1 +f 1351//2 1352//2 1353//2 +f 1327//1 1325//1 1324//1 +f 1350//2 1351//2 1353//2 +f 1324//1 1323//1 1322//1 +f 1348//2 1349//2 1350//2 +f 1327//1 1324//1 1322//1 +f 1348//2 1350//2 1353//2 +f 1316//1 1327//1 1322//1 +f 1348//1 1353//1 1342//1 +f 1316//1 1322//1 1321//1 +f 1347//2 1348//2 1342//2 +f 1321//1 1320//1 1319//1 +f 1345//2 1346//2 1347//2 +f 1321//1 1319//1 1318//1 +f 1344//2 1345//2 1347//2 +f 1321//1 1318//1 1317//1 +f 1343//2 1344//2 1347//2 +f 1321//1 1317//1 1316//1 +f 1342//1 1343//1 1347//1 +f 1316//466 1317//466 1330//466 1329//466 +f 1317//9 1318//9 1331//9 1330//9 +f 1318//28 1319//28 1332//28 1331//28 +f 1319//467 1320//467 1333//467 1332//467 +f 1320//455 1321//455 1334//455 1333//455 +f 1321//106 1322//106 1335//106 1334//106 +f 1322//468 1323//468 1336//468 1335//468 +f 1323//469 1324//469 1337//469 1336//469 +f 1324//396 1325//396 1338//396 1337//396 +f 1325//11 1326//11 1339//11 1338//11 +f 1326//359 1327//359 1340//359 1339//359 +f 1327//470 1328//470 1341//470 1340//470 +f 1328//4 1316//4 1329//4 1341//4 +f 1329//471 1330//471 1343//471 1342//471 +f 1330//437 1331//437 1344//437 1343//437 +f 1331//229 1332//229 1345//229 1344//229 +f 1332//472 1333//472 1346//472 1345//472 +f 1333//473 1334//473 1347//473 1346//473 +f 1334//474 1335//474 1348//474 1347//474 +f 1335//161 1336//161 1349//161 1348//161 +f 1336//475 1337//475 1350//475 1349//475 +f 1337//476 1338//476 1351//476 1350//476 +f 1338//477 1339//477 1352//477 1351//477 +f 1339//385 1340//385 1353//385 1352//385 +f 1340//478 1341//478 1354//478 1353//478 +f 1341//479 1329//479 1342//479 1354//479 +usemtl dark +f 1274//1 1281//1 1280//1 +f 1296//2 1297//2 1290//2 +f 1274//1 1280//1 1279//1 +f 1295//2 1296//2 1290//2 +f 1274//1 1279//1 1278//1 +f 1294//2 1295//2 1290//2 +f 1274//1 1278//1 1277//1 +f 1293//2 1294//2 1290//2 +f 1274//1 1277//1 1276//1 +f 1292//2 1293//2 1290//2 +f 1276//1 1275//1 1274//1 +f 1290//2 1291//2 1292//2 +f 1274//359 1275//359 1283//359 1282//359 +f 1275//28 1276//28 1284//28 1283//28 +f 1276//480 1277//480 1285//480 1284//480 +f 1277//481 1278//481 1286//481 1285//481 +f 1278//13 1279//13 1287//13 1286//13 +f 1279//11 1280//11 1288//11 1287//11 +f 1280//359 1281//359 1289//359 1288//359 +f 1281//4 1274//4 1282//4 1289//4 +f 1282//385 1283//385 1291//385 1290//385 +f 1283//229 1284//229 1292//229 1291//229 +f 1284//482 1285//482 1293//482 1292//482 +f 1285//483 1286//483 1294//483 1293//483 +f 1286//484 1287//484 1295//484 1294//484 +f 1287//485 1288//485 1296//485 1295//485 +f 1288//385 1289//385 1297//385 1296//385 +f 1289//423 1282//423 1290//423 1297//423 +f 1298//1 1303//1 1302//1 +f 1314//2 1315//2 1310//2 +f 1298//1 1302//1 1301//1 +f 1313//2 1314//2 1310//2 +f 1298//1 1301//1 1300//1 +f 1312//2 1313//2 1310//2 +f 1300//1 1299//1 1298//1 +f 1310//2 1311//2 1312//2 +f 1298//486 1299//486 1305//486 1304//486 +f 1299//487 1300//487 1306//487 1305//487 +f 1300//59 1301//59 1307//59 1306//59 +f 1301//488 1302//488 1308//488 1307//488 +f 1302//489 1303//489 1309//489 1308//489 +f 1303//490 1298//490 1304//490 1309//490 +f 1304//491 1305//491 1311//491 1310//491 +f 1305//492 1306//492 1312//492 1311//492 +f 1306//204 1307//204 1313//204 1312//204 +f 1307//493 1308//493 1314//493 1313//493 +f 1308//494 1309//494 1315//494 1314//494 +f 1309//495 1304//495 1310//495 1315//495 +o neck +usemtl frame +f 1355//496 1356//496 1373//496 1372//496 +f 1356//54 1357//54 1374//54 1373//54 +f 1357//497 1358//497 1375//497 1374//497 +f 1358//9 1359//9 1376//9 1375//9 +f 1359//498 1360//498 1377//498 1376//498 +f 1360//499 1361//499 1378//499 1377//499 +f 1361//13 1362//13 1379//13 1378//13 +f 1362//500 1363//500 1380//500 1379//500 +f 1363//13 1364//13 1381//13 1380//13 +f 1364//501 1365//501 1382//501 1381//501 +f 1365//502 1366//502 1383//502 1382//502 +f 1366//503 1367//503 1384//503 1383//503 +f 1367//504 1368//504 1385//504 1384//504 +f 1368//505 1369//505 1386//505 1385//505 +f 1369//506 1370//506 1387//506 1386//506 +f 1370//11 1371//11 1388//11 1387//11 +f 1371//507 1355//507 1372//507 1388//507 +f 1355//1 1371//1 1370//1 +f 1387//2 1388//2 1372//2 +f 1355//1 1370//1 1369//1 +f 1386//2 1387//2 1372//2 +f 1355//1 1369//1 1368//1 +f 1385//2 1386//2 1372//2 +f 1355//1 1368//1 1367//1 +f 1384//2 1385//2 1372//2 +f 1355//1 1367//1 1366//1 +f 1383//2 1384//2 1372//2 +f 1355//1 1366//1 1365//1 +f 1382//2 1383//2 1372//2 +f 1355//1 1365//1 1364//1 +f 1381//2 1382//2 1372//2 +f 1355//1 1364//1 1363//1 +f 1380//2 1381//2 1372//2 +f 1355//1 1363//1 1362//1 +f 1379//2 1380//2 1372//2 +f 1355//1 1362//1 1361//1 +f 1378//2 1379//2 1372//2 +f 1355//1 1361//1 1360//1 +f 1377//2 1378//2 1372//2 +f 1355//1 1360//1 1359//1 +f 1376//2 1377//2 1372//2 +f 1355//1 1359//1 1358//1 +f 1375//2 1376//2 1372//2 +f 1355//1 1358//1 1357//1 +f 1374//2 1375//2 1372//2 +f 1357//1 1356//1 1355//1 +f 1372//2 1373//2 1374//2 +usemtl champagne +f 1389//1 1405//1 1404//1 +f 1438//2 1439//2 1423//2 +f 1389//1 1404//1 1403//1 +f 1437//2 1438//2 1423//2 +f 1389//1 1403//1 1402//1 +f 1436//2 1437//2 1423//2 +f 1389//1 1402//1 1401//1 +f 1435//2 1436//2 1423//2 +f 1389//1 1401//1 1400//1 +f 1434//2 1435//2 1423//2 +f 1389//1 1400//1 1399//1 +f 1433//2 1434//2 1423//2 +f 1389//1 1399//1 1398//1 +f 1432//2 1433//2 1423//2 +f 1389//1 1398//1 1397//1 +f 1431//2 1432//2 1423//2 +f 1389//1 1397//1 1396//1 +f 1430//2 1431//2 1423//2 +f 1389//1 1396//1 1395//1 +f 1429//2 1430//2 1423//2 +f 1389//1 1395//1 1394//1 +f 1428//2 1429//2 1423//2 +f 1394//1 1393//1 1392//1 +f 1426//2 1427//2 1428//2 +f 1389//1 1394//1 1392//1 +f 1426//2 1428//2 1423//2 +f 1389//1 1392//1 1391//1 +f 1425//2 1426//2 1423//2 +f 1391//1 1390//1 1389//1 +f 1423//2 1424//2 1425//2 +f 1389//508 1390//508 1407//508 1406//508 +f 1390//4 1391//4 1408//4 1407//4 +f 1391//509 1392//509 1409//509 1408//509 +f 1392//510 1393//510 1410//510 1409//510 +f 1393//511 1394//511 1411//511 1410//511 +f 1394//34 1395//34 1412//34 1411//34 +f 1395//512 1396//512 1413//512 1412//512 +f 1396//13 1397//13 1414//13 1413//13 +f 1397//9 1398//9 1415//9 1414//9 +f 1398//513 1399//513 1416//513 1415//513 +f 1399//514 1400//514 1417//514 1416//514 +f 1400//515 1401//515 1418//515 1417//515 +f 1401//516 1402//516 1419//516 1418//516 +f 1402//517 1403//517 1420//517 1419//517 +f 1403//518 1404//518 1421//518 1420//518 +f 1404//11 1405//11 1422//11 1421//11 +f 1405//519 1389//519 1406//519 1422//519 +f 1406//520 1407//520 1424//520 1423//520 +f 1407//521 1408//521 1425//521 1424//521 +f 1408//522 1409//522 1426//522 1425//522 +f 1409//523 1410//523 1427//523 1426//523 +f 1410//524 1411//524 1428//524 1427//524 +f 1411//525 1412//525 1429//525 1428//525 +f 1412//526 1413//526 1430//526 1429//526 +f 1413//440 1414//440 1431//440 1430//440 +f 1414//527 1415//527 1432//527 1431//527 +f 1415//528 1416//528 1433//528 1432//528 +f 1416//529 1417//529 1434//529 1433//529 +f 1417//530 1418//530 1435//530 1434//530 +f 1418//531 1419//531 1436//531 1435//531 +f 1419//532 1420//532 1437//532 1436//532 +f 1420//533 1421//533 1438//533 1437//533 +f 1421//485 1422//485 1439//485 1438//485 +f 1422//534 1406//534 1423//534 1439//534 +f 1569//1 1585//1 1584//1 +f 1618//2 1619//2 1603//2 +f 1569//1 1584//1 1583//1 +f 1617//2 1618//2 1603//2 +f 1569//1 1583//1 1582//1 +f 1616//2 1617//2 1603//2 +f 1569//1 1582//1 1581//1 +f 1615//2 1616//2 1603//2 +f 1569//1 1581//1 1580//1 +f 1614//2 1615//2 1603//2 +f 1569//1 1580//1 1579//1 +f 1613//2 1614//2 1603//2 +f 1569//1 1579//1 1578//1 +f 1612//2 1613//2 1603//2 +f 1569//1 1578//1 1577//1 +f 1611//2 1612//2 1603//2 +f 1569//1 1577//1 1576//1 +f 1610//2 1611//2 1603//2 +f 1576//1 1575//1 1574//1 +f 1608//2 1609//2 1610//2 +f 1569//1 1576//1 1574//1 +f 1608//1 1610//1 1603//1 +f 1569//1 1574//1 1573//1 +f 1607//2 1608//2 1603//2 +f 1573//1 1572//1 1571//1 +f 1605//2 1606//2 1607//2 +f 1569//1 1573//1 1571//1 +f 1605//2 1607//2 1603//2 +f 1571//1 1570//1 1569//1 +f 1603//2 1604//2 1605//2 +f 1569//28 1570//28 1587//28 1586//28 +f 1570//535 1571//535 1588//535 1587//535 +f 1571//536 1572//536 1589//536 1588//536 +f 1572//537 1573//537 1590//537 1589//537 +f 1573//538 1574//538 1591//538 1590//538 +f 1574//539 1575//539 1592//539 1591//539 +f 1575//13 1576//13 1593//13 1592//13 +f 1576//9 1577//9 1594//9 1593//9 +f 1577//540 1578//540 1595//540 1594//540 +f 1578//541 1579//541 1596//541 1595//541 +f 1579//13 1580//13 1597//13 1596//13 +f 1580//542 1581//542 1598//542 1597//542 +f 1581//543 1582//543 1599//543 1598//543 +f 1582//544 1583//544 1600//544 1599//544 +f 1583//545 1584//545 1601//545 1600//545 +f 1584//11 1585//11 1602//11 1601//11 +f 1585//508 1569//508 1586//508 1602//508 +f 1586//229 1587//229 1604//229 1603//229 +f 1587//546 1588//546 1605//546 1604//546 +f 1588//547 1589//547 1606//547 1605//547 +f 1589//548 1590//548 1607//548 1606//548 +f 1590//549 1591//549 1608//549 1607//549 +f 1591//550 1592//550 1609//550 1608//550 +f 1592//484 1593//484 1610//484 1609//484 +f 1593//524 1594//524 1611//524 1610//524 +f 1594//551 1595//551 1612//551 1611//551 +f 1595//552 1596//552 1613//552 1612//552 +f 1596//484 1597//484 1614//484 1613//484 +f 1597//553 1598//553 1615//553 1614//553 +f 1598//554 1599//554 1616//554 1615//554 +f 1599//555 1600//555 1617//555 1616//555 +f 1600//556 1601//556 1618//556 1617//556 +f 1601//557 1602//557 1619//557 1618//557 +f 1602//558 1586//558 1603//558 1619//558 +usemtl glass +f 1440//1 1451//1 1450//1 +f 1474//2 1475//2 1464//2 +f 1440//1 1450//1 1449//1 +f 1473//2 1474//2 1464//2 +f 1440//1 1449//1 1448//1 +f 1472//2 1473//2 1464//2 +f 1440//1 1448//1 1447//1 +f 1471//2 1472//2 1464//2 +f 1447//1 1446//1 1445//1 +f 1469//2 1470//2 1471//2 +f 1440//1 1447//1 1445//1 +f 1469//2 1471//2 1464//2 +f 1440//1 1445//1 1444//1 +f 1468//2 1469//2 1464//2 +f 1440//1 1444//1 1443//1 +f 1467//2 1468//2 1464//2 +f 1440//1 1443//1 1442//1 +f 1466//2 1467//2 1464//2 +f 1442//1 1441//1 1440//1 +f 1464//2 1465//2 1466//2 +f 1440//559 1441//559 1453//559 1452//559 +f 1441//560 1442//560 1454//560 1453//560 +f 1442//561 1443//561 1455//561 1454//561 +f 1443//562 1444//562 1456//562 1455//562 +f 1444//8 1445//8 1457//8 1456//8 +f 1445//563 1446//563 1458//563 1457//563 +f 1446//564 1447//564 1459//564 1458//564 +f 1447//505 1448//505 1460//505 1459//505 +f 1448//488 1449//488 1461//488 1460//488 +f 1449//565 1450//565 1462//565 1461//565 +f 1450//133 1451//133 1463//133 1462//133 +f 1451//4 1440//4 1452//4 1463//4 +f 1452//566 1453//566 1465//566 1464//566 +f 1453//567 1454//567 1466//567 1465//567 +f 1454//568 1455//568 1467//568 1466//568 +f 1455//569 1456//569 1468//569 1467//569 +f 1456//230 1457//230 1469//230 1468//230 +f 1457//570 1458//570 1470//570 1469//570 +f 1458//571 1459//571 1471//571 1470//571 +f 1459//572 1460//572 1472//572 1471//572 +f 1460//573 1461//573 1473//573 1472//573 +f 1461//574 1462//574 1474//574 1473//574 +f 1462//575 1463//575 1475//575 1474//575 +f 1463//423 1452//423 1464//423 1475//423 +f 1476//1 1486//1 1485//1 +f 1507//2 1508//2 1498//2 +f 1476//1 1485//1 1484//1 +f 1506//2 1507//2 1498//2 +f 1476//1 1484//1 1483//1 +f 1505//2 1506//2 1498//2 +f 1476//1 1483//1 1482//1 +f 1504//2 1505//2 1498//2 +f 1476//1 1482//1 1481//1 +f 1503//2 1504//2 1498//2 +f 1476//1 1481//1 1480//1 +f 1502//2 1503//2 1498//2 +f 1476//1 1480//1 1479//1 +f 1501//2 1502//2 1498//2 +f 1476//1 1479//1 1478//1 +f 1500//2 1501//2 1498//2 +f 1478//1 1477//1 1476//1 +f 1498//2 1499//2 1500//2 +f 1476//576 1477//576 1488//576 1487//576 +f 1477//28 1478//28 1489//28 1488//28 +f 1478//577 1479//577 1490//577 1489//577 +f 1479//578 1480//578 1491//578 1490//578 +f 1480//8 1481//8 1492//8 1491//8 +f 1481//579 1482//579 1493//579 1492//579 +f 1482//580 1483//580 1494//580 1493//580 +f 1483//59 1484//59 1495//59 1494//59 +f 1484//581 1485//581 1496//581 1495//581 +f 1485//359 1486//359 1497//359 1496//359 +f 1486//582 1476//582 1487//582 1497//582 +f 1487//583 1488//583 1499//583 1498//583 +f 1488//229 1489//229 1500//229 1499//229 +f 1489//584 1490//584 1501//584 1500//584 +f 1490//585 1491//585 1502//585 1501//585 +f 1491//230 1492//230 1503//230 1502//230 +f 1492//586 1493//586 1504//586 1503//586 +f 1493//587 1494//587 1505//587 1504//587 +f 1494//204 1495//204 1506//204 1505//204 +f 1495//588 1496//588 1507//588 1506//588 +f 1496//385 1497//385 1508//385 1507//385 +f 1497//589 1487//589 1498//589 1508//589 +usemtl dark +f 1509//1 1528//1 1527//1 +f 1567//2 1568//2 1549//2 +f 1509//1 1527//1 1526//1 +f 1566//2 1567//2 1549//2 +f 1509//1 1526//1 1525//1 +f 1565//2 1566//2 1549//2 +f 1509//1 1525//1 1524//1 +f 1564//2 1565//2 1549//2 +f 1523//1 1522//1 1521//1 +f 1561//2 1562//2 1563//2 +f 1524//1 1523//1 1521//1 +f 1561//2 1563//2 1564//2 +f 1509//1 1524//1 1521//1 +f 1561//2 1564//2 1549//2 +f 1509//1 1521//1 1520//1 +f 1560//2 1561//2 1549//2 +f 1509//1 1520//1 1519//1 +f 1559//2 1560//2 1549//2 +f 1509//1 1519//1 1518//1 +f 1558//2 1559//2 1549//2 +f 1509//1 1518//1 1517//1 +f 1557//2 1558//2 1549//2 +f 1509//1 1517//1 1516//1 +f 1556//2 1557//2 1549//2 +f 1509//1 1516//1 1515//1 +f 1555//2 1556//2 1549//2 +f 1509//1 1515//1 1514//1 +f 1554//2 1555//2 1549//2 +f 1509//1 1514//1 1513//1 +f 1553//2 1554//2 1549//2 +f 1509//1 1513//1 1512//1 +f 1552//2 1553//2 1549//2 +f 1509//1 1512//1 1511//1 +f 1551//2 1552//2 1549//2 +f 1511//1 1510//1 1509//1 +f 1549//2 1550//2 1551//2 +f 1509//590 1510//590 1530//590 1529//590 +f 1510//591 1511//591 1531//591 1530//591 +f 1511//9 1512//9 1532//9 1531//9 +f 1512//498 1513//498 1533//498 1532//498 +f 1513//592 1514//592 1534//592 1533//592 +f 1514//593 1515//593 1535//593 1534//593 +f 1515//455 1516//455 1536//455 1535//455 +f 1516//594 1517//594 1537//594 1536//594 +f 1517//595 1518//595 1538//595 1537//595 +f 1518//596 1519//596 1539//596 1538//596 +f 1519//597 1520//597 1540//597 1539//597 +f 1520//598 1521//598 1541//598 1540//598 +f 1521//599 1522//599 1542//599 1541//599 +f 1522//600 1523//600 1543//600 1542//600 +f 1523//601 1524//601 1544//601 1543//601 +f 1524//602 1525//602 1545//602 1544//602 +f 1525//603 1526//603 1546//603 1545//603 +f 1526//604 1527//604 1547//604 1546//604 +f 1527//605 1528//605 1548//605 1547//605 +f 1528//429 1509//429 1529//429 1548//429 +f 1529//606 1530//606 1550//606 1549//606 +f 1530//607 1531//607 1551//607 1550//607 +f 1531//437 1532//437 1552//437 1551//437 +f 1532//608 1533//608 1553//608 1552//608 +f 1533//609 1534//609 1554//609 1553//609 +f 1534//610 1535//610 1555//610 1554//610 +f 1535//462 1536//462 1556//462 1555//462 +f 1536//611 1537//611 1557//611 1556//611 +f 1537//612 1538//612 1558//612 1557//612 +f 1538//440 1539//440 1559//440 1558//440 +f 1539//613 1540//613 1560//613 1559//613 +f 1540//614 1541//614 1561//614 1560//614 +f 1541//615 1542//615 1562//615 1561//615 +f 1542//616 1543//616 1563//616 1562//616 +f 1543//617 1544//617 1564//617 1563//617 +f 1544//618 1545//618 1565//618 1564//618 +f 1545//619 1546//619 1566//619 1565//619 +f 1546//620 1547//620 1567//620 1566//620 +f 1547//621 1548//621 1568//621 1567//621 +f 1548//622 1529//622 1549//622 1568//622 +o body +usemtl frame +f 1620//4 1621//4 1647//4 1646//4 +f 1621//11 1622//11 1648//11 1647//11 +f 1622//623 1623//623 1649//623 1648//623 +f 1623//4 1624//4 1650//4 1649//4 +f 1624//624 1625//624 1651//624 1650//624 +f 1625//9 1626//9 1652//9 1651//9 +f 1626//625 1627//625 1653//625 1652//625 +f 1627//13 1628//13 1654//13 1653//13 +f 1628//9 1629//9 1655//9 1654//9 +f 1629//626 1630//626 1656//626 1655//626 +f 1630//13 1631//13 1657//13 1656//13 +f 1631//354 1632//354 1658//354 1657//354 +f 1632//627 1633//627 1659//627 1658//627 +f 1633//628 1634//628 1660//628 1659//628 +f 1634//11 1635//11 1661//11 1660//11 +f 1635//4 1636//4 1662//4 1661//4 +f 1636//629 1637//629 1663//629 1662//629 +f 1637//630 1638//630 1664//630 1663//630 +f 1638//11 1639//11 1665//11 1664//11 +f 1639//631 1640//631 1666//631 1665//631 +f 1640//11 1641//11 1667//11 1666//11 +f 1641//4 1642//4 1668//4 1667//4 +f 1642//632 1643//632 1669//632 1668//632 +f 1643//4 1644//4 1670//4 1669//4 +f 1644//633 1645//633 1671//633 1670//633 +f 1645//634 1620//634 1646//634 1671//634 +f 1620//1 1645//1 1644//1 +f 1670//2 1671//2 1646//2 +f 1643//1 1642//1 1641//1 +f 1667//2 1668//2 1669//2 +f 1643//1 1641//1 1640//1 +f 1666//2 1667//2 1669//2 +f 1643//1 1640//1 1639//1 +f 1665//2 1666//2 1669//2 +f 1643//1 1639//1 1638//1 +f 1664//2 1665//2 1669//2 +f 1644//1 1643//1 1638//1 +f 1664//2 1669//2 1670//2 +f 1637//1 1636//1 1635//1 +f 1661//2 1662//2 1663//2 +f 1637//1 1635//1 1634//1 +f 1660//2 1661//2 1663//2 +f 1637//1 1634//1 1633//1 +f 1659//2 1660//2 1663//2 +f 1638//1 1637//1 1633//1 +f 1659//2 1663//2 1664//2 +f 1644//1 1638//1 1633//1 +f 1659//2 1664//2 1670//2 +f 1644//1 1633//1 1632//1 +f 1658//2 1659//2 1670//2 +f 1620//1 1644//1 1632//1 +f 1658//2 1670//2 1646//2 +f 1620//1 1632//1 1631//1 +f 1657//2 1658//2 1646//2 +f 1620//1 1631//1 1630//1 +f 1656//2 1657//2 1646//2 +f 1620//1 1630//1 1629//1 +f 1655//2 1656//2 1646//2 +f 1620//1 1629//1 1628//1 +f 1654//2 1655//2 1646//2 +f 1620//1 1628//1 1627//1 +f 1653//2 1654//2 1646//2 +f 1627//1 1626//1 1625//1 +f 1651//2 1652//2 1653//2 +f 1627//1 1625//1 1624//1 +f 1650//2 1651//2 1653//2 +f 1627//1 1624//1 1623//1 +f 1649//2 1650//2 1653//2 +f 1627//1 1623//1 1622//1 +f 1648//2 1649//2 1653//2 +f 1627//1 1622//1 1621//1 +f 1647//2 1648//2 1653//2 +f 1627//1 1621//1 1620//1 +f 1646//2 1647//2 1653//2 +usemtl glass +f 1672//1 1690//1 1689//1 +f 1727//2 1728//2 1710//2 +f 1672//1 1689//1 1688//1 +f 1726//2 1727//2 1710//2 +f 1687//1 1686//1 1685//1 +f 1723//2 1724//2 1725//2 +f 1688//1 1687//1 1685//1 +f 1723//2 1725//2 1726//2 +f 1672//1 1688//1 1685//1 +f 1723//2 1726//2 1710//2 +f 1672//1 1685//1 1684//1 +f 1722//2 1723//2 1710//2 +f 1672//1 1684//1 1683//1 +f 1721//2 1722//2 1710//2 +f 1672//1 1683//1 1682//1 +f 1720//2 1721//2 1710//2 +f 1672//1 1682//1 1681//1 +f 1719//2 1720//2 1710//2 +f 1672//1 1681//1 1680//1 +f 1718//2 1719//2 1710//2 +f 1672//1 1680//1 1679//1 +f 1717//2 1718//2 1710//2 +f 1672//1 1679//1 1678//1 +f 1716//2 1717//2 1710//2 +f 1672//1 1678//1 1677//1 +f 1715//2 1716//2 1710//2 +f 1672//1 1677//1 1676//1 +f 1714//2 1715//2 1710//2 +f 1672//1 1676//1 1675//1 +f 1713//2 1714//2 1710//2 +f 1675//1 1674//1 1673//1 +f 1711//2 1712//2 1713//2 +f 1675//1 1673//1 1672//1 +f 1710//2 1711//2 1713//2 +f 1672//635 1673//635 1692//635 1691//635 +f 1673//636 1674//636 1693//636 1692//636 +f 1674//637 1675//637 1694//637 1693//637 +f 1675//638 1676//638 1695//638 1694//638 +f 1676//639 1677//639 1696//639 1695//639 +f 1677//640 1678//640 1697//640 1696//640 +f 1678//8 1679//8 1698//8 1697//8 +f 1679//9 1680//9 1699//9 1698//9 +f 1680//641 1681//641 1700//641 1699//641 +f 1681//642 1682//642 1701//642 1700//642 +f 1682//8 1683//8 1702//8 1701//8 +f 1683//643 1684//643 1703//643 1702//643 +f 1684//59 1685//59 1704//59 1703//59 +f 1685//108 1686//108 1705//108 1704//108 +f 1686//4 1687//4 1706//4 1705//4 +f 1687//11 1688//11 1707//11 1706//11 +f 1688//644 1689//644 1708//644 1707//644 +f 1689//645 1690//645 1709//645 1708//645 +f 1690//646 1672//646 1691//646 1709//646 +f 1691//647 1692//647 1711//647 1710//647 +f 1692//648 1693//648 1712//648 1711//648 +f 1693//649 1694//649 1713//649 1712//649 +f 1694//437 1695//437 1714//437 1713//437 +f 1695//650 1696//650 1715//650 1714//650 +f 1696//651 1697//651 1716//651 1715//651 +f 1697//230 1698//230 1717//230 1716//230 +f 1698//437 1699//437 1718//437 1717//437 +f 1699//652 1700//652 1719//652 1718//652 +f 1700//653 1701//653 1720//653 1719//653 +f 1701//230 1702//230 1721//230 1720//230 +f 1702//654 1703//654 1722//654 1721//654 +f 1703//204 1704//204 1723//204 1722//204 +f 1704//655 1705//655 1724//655 1723//655 +f 1705//144 1706//144 1725//144 1724//144 +f 1706//161 1707//161 1726//161 1725//161 +f 1707//656 1708//656 1727//656 1726//656 +f 1708//657 1709//657 1728//657 1727//657 +f 1709//658 1691//658 1710//658 1728//658 +f 1729//1 1738//1 1737//1 +f 1757//2 1758//2 1749//2 +f 1729//1 1737//1 1736//1 +f 1756//2 1757//2 1749//2 +f 1735//1 1734//1 1733//1 +f 1753//1 1754//1 1755//1 +f 1732//1 1731//1 1730//1 +f 1750//2 1751//2 1752//2 +f 1733//1 1732//1 1730//1 +f 1750//2 1752//2 1753//2 +f 1735//1 1733//1 1730//1 +f 1750//1 1753//1 1755//1 +f 1735//1 1730//1 1729//1 +f 1749//2 1750//2 1755//2 +f 1736//2 1735//2 1729//2 +f 1749//1 1755//1 1756//1 +f 1729//624 1730//624 1740//624 1739//624 +f 1730//638 1731//638 1741//638 1740//638 +f 1731//455 1732//455 1742//455 1741//455 +f 1732//659 1733//659 1743//659 1742//659 +f 1733//660 1734//660 1744//660 1743//660 +f 1734//661 1735//661 1745//661 1744//661 +f 1735//662 1736//662 1746//662 1745//662 +f 1736//11 1737//11 1747//11 1746//11 +f 1737//623 1738//623 1748//623 1747//623 +f 1738//663 1729//663 1739//663 1748//663 +f 1739//664 1740//664 1750//664 1749//664 +f 1740//437 1741//437 1751//437 1750//437 +f 1741//440 1742//440 1752//440 1751//440 +f 1742//665 1743//665 1753//665 1752//665 +f 1743//666 1744//666 1754//666 1753//666 +f 1744//667 1745//667 1755//667 1754//667 +f 1745//668 1746//668 1756//668 1755//668 +f 1746//669 1747//669 1757//669 1756//669 +f 1747//670 1748//670 1758//670 1757//670 +f 1748//671 1739//671 1749//671 1758//671 +f 1759//1 1771//1 1770//1 +f 1796//2 1797//2 1785//2 +f 1759//1 1770//1 1769//1 +f 1795//2 1796//2 1785//2 +f 1759//1 1769//1 1768//1 +f 1794//2 1795//2 1785//2 +f 1759//1 1768//1 1767//1 +f 1793//2 1794//2 1785//2 +f 1759//1 1767//1 1766//1 +f 1792//2 1793//2 1785//2 +f 1759//1 1766//1 1765//1 +f 1791//2 1792//2 1785//2 +f 1759//1 1765//1 1764//1 +f 1790//2 1791//2 1785//2 +f 1759//1 1764//1 1763//1 +f 1789//2 1790//2 1785//2 +f 1759//1 1763//1 1762//1 +f 1788//2 1789//2 1785//2 +f 1759//1 1762//1 1761//1 +f 1787//2 1788//2 1785//2 +f 1761//1 1760//1 1759//1 +f 1785//2 1786//2 1787//2 +f 1759//672 1760//672 1773//672 1772//672 +f 1760//673 1761//673 1774//673 1773//673 +f 1761//674 1762//674 1775//674 1774//674 +f 1762//675 1763//675 1776//675 1775//675 +f 1763//676 1764//676 1777//676 1776//676 +f 1764//13 1765//13 1778//13 1777//13 +f 1765//677 1766//677 1779//677 1778//677 +f 1766//678 1767//678 1780//678 1779//678 +f 1767//679 1768//679 1781//679 1780//679 +f 1768//107 1769//107 1782//107 1781//107 +f 1769//680 1770//680 1783//680 1782//680 +f 1770//681 1771//681 1784//681 1783//681 +f 1771//674 1759//674 1772//674 1784//674 +f 1772//682 1773//682 1786//682 1785//682 +f 1773//683 1774//683 1787//683 1786//683 +f 1774//684 1775//684 1788//684 1787//684 +f 1775//685 1776//685 1789//685 1788//685 +f 1776//686 1777//686 1790//686 1789//686 +f 1777//440 1778//440 1791//440 1790//440 +f 1778//687 1779//687 1792//687 1791//687 +f 1779//688 1780//688 1793//688 1792//688 +f 1780//689 1781//689 1794//689 1793//689 +f 1781//690 1782//690 1795//690 1794//690 +f 1782//161 1783//161 1796//161 1795//161 +f 1783//691 1784//691 1797//691 1796//691 +f 1784//692 1772//692 1785//692 1797//692 +usemtl champagne +f 1798//1 1806//1 1805//1 +f 1823//2 1824//2 1816//2 +f 1798//1 1805//1 1804//1 +f 1822//2 1823//2 1816//2 +f 1798//1 1804//1 1803//1 +f 1821//2 1822//2 1816//2 +f 1798//1 1803//1 1802//1 +f 1820//2 1821//2 1816//2 +f 1798//1 1802//1 1801//1 +f 1819//2 1820//2 1816//2 +f 1801//1 1800//1 1799//1 +f 1817//2 1818//2 1819//2 +f 1801//1 1799//1 1798//1 +f 1816//2 1817//2 1819//2 +f 1798//629 1799//629 1808//629 1807//629 +f 1799//693 1800//693 1809//693 1808//693 +f 1800//28 1801//28 1810//28 1809//28 +f 1801//694 1802//694 1811//694 1810//694 +f 1802//695 1803//695 1812//695 1811//695 +f 1803//696 1804//696 1813//696 1812//696 +f 1804//697 1805//697 1814//697 1813//697 +f 1805//698 1806//698 1815//698 1814//698 +f 1806//457 1798//457 1807//457 1815//457 +f 1807//699 1808//699 1817//699 1816//699 +f 1808//700 1809//700 1818//700 1817//700 +f 1809//229 1810//229 1819//229 1818//229 +f 1810//701 1811//701 1820//701 1819//701 +f 1811//702 1812//702 1821//702 1820//702 +f 1812//703 1813//703 1822//703 1821//703 +f 1813//704 1814//704 1823//704 1822//704 +f 1814//705 1815//705 1824//705 1823//705 +f 1815//144 1807//144 1816//144 1824//144 +f 1825//1 1838//1 1837//1 +f 1865//2 1866//2 1853//2 +f 1825//1 1837//1 1836//1 +f 1864//2 1865//2 1853//2 +f 1836//1 1835//1 1834//1 +f 1862//2 1863//2 1864//2 +f 1825//1 1836//1 1834//1 +f 1862//2 1864//2 1853//2 +f 1825//1 1834//1 1833//1 +f 1861//1 1862//1 1853//1 +f 1825//1 1833//1 1832//1 +f 1860//2 1861//2 1853//2 +f 1832//1 1831//1 1830//1 +f 1858//2 1859//2 1860//2 +f 1832//1 1830//1 1829//1 +f 1857//1 1858//1 1860//1 +f 1825//1 1832//1 1829//1 +f 1857//1 1860//1 1853//1 +f 1829//1 1828//1 1827//1 +f 1855//2 1856//2 1857//2 +f 1829//1 1827//1 1826//1 +f 1854//2 1855//2 1857//2 +f 1829//1 1826//1 1825//1 +f 1853//2 1854//2 1857//2 +f 1825//632 1826//632 1840//632 1839//632 +f 1826//457 1827//457 1841//457 1840//457 +f 1827//28 1828//28 1842//28 1841//28 +f 1828//257 1829//257 1843//257 1842//257 +f 1829//28 1830//28 1844//28 1843//28 +f 1830//706 1831//706 1845//706 1844//706 +f 1831//707 1832//707 1846//707 1845//707 +f 1832//708 1833//708 1847//708 1846//708 +f 1833//709 1834//709 1848//709 1847//709 +f 1834//59 1835//59 1849//59 1848//59 +f 1835//11 1836//11 1850//11 1849//11 +f 1836//631 1837//631 1851//631 1850//631 +f 1837//710 1838//710 1852//710 1851//710 +f 1838//663 1825//663 1839//663 1852//663 +f 1839//711 1840//711 1854//711 1853//711 +f 1840//144 1841//144 1855//144 1854//144 +f 1841//229 1842//229 1856//229 1855//229 +f 1842//712 1843//712 1857//712 1856//712 +f 1843//229 1844//229 1858//229 1857//229 +f 1844//713 1845//713 1859//713 1858//713 +f 1845//714 1846//714 1860//714 1859//714 +f 1846//715 1847//715 1861//715 1860//715 +f 1847//716 1848//716 1862//716 1861//716 +f 1848//204 1849//204 1863//204 1862//204 +f 1849//161 1850//161 1864//161 1863//161 +f 1850//717 1851//717 1865//717 1864//717 +f 1851//161 1852//161 1866//161 1865//161 +f 1852//671 1839//671 1853//671 1866//671 +f 1867//1 1879//1 1878//1 +f 1904//2 1905//2 1893//2 +f 1867//1 1878//1 1877//1 +f 1903//2 1904//2 1893//2 +f 1867//1 1877//1 1876//1 +f 1902//2 1903//2 1893//2 +f 1867//1 1876//1 1875//1 +f 1901//2 1902//2 1893//2 +f 1875//1 1874//1 1873//1 +f 1899//2 1900//2 1901//2 +f 1867//1 1875//1 1873//1 +f 1899//2 1901//2 1893//2 +f 1867//1 1873//1 1872//1 +f 1898//2 1899//2 1893//2 +f 1867//1 1872//1 1871//1 +f 1897//2 1898//2 1893//2 +f 1867//1 1871//1 1870//1 +f 1896//2 1897//2 1893//2 +f 1867//1 1870//1 1869//1 +f 1895//2 1896//2 1893//2 +f 1869//1 1868//1 1867//1 +f 1893//2 1894//2 1895//2 +f 1867//718 1868//718 1881//718 1880//718 +f 1868//9 1869//9 1882//9 1881//9 +f 1869//626 1870//626 1883//626 1882//626 +f 1870//13 1871//13 1884//13 1883//13 +f 1871//719 1872//719 1885//719 1884//719 +f 1872//720 1873//720 1886//720 1885//720 +f 1873//59 1874//59 1887//59 1886//59 +f 1874//359 1875//359 1888//359 1887//359 +f 1875//721 1876//721 1889//721 1888//721 +f 1876//722 1877//722 1890//722 1889//722 +f 1877//468 1878//468 1891//468 1890//468 +f 1878//359 1879//359 1892//359 1891//359 +f 1879//723 1867//723 1880//723 1892//723 +f 1880//724 1881//724 1894//724 1893//724 +f 1881//524 1882//524 1895//524 1894//524 +f 1882//725 1883//725 1896//725 1895//725 +f 1883//726 1884//726 1897//726 1896//726 +f 1884//727 1885//727 1898//727 1897//727 +f 1885//728 1886//728 1899//728 1898//728 +f 1886//204 1887//204 1900//204 1899//204 +f 1887//385 1888//385 1901//385 1900//385 +f 1888//729 1889//729 1902//729 1901//729 +f 1889//730 1890//730 1903//730 1902//730 +f 1890//161 1891//161 1904//161 1903//161 +f 1891//385 1892//385 1905//385 1904//385 +f 1892//731 1880//731 1893//731 1905//731 +usemtl dark +f 1906//1 1916//1 1915//1 +f 1937//2 1938//2 1928//2 +f 1906//1 1915//1 1914//1 +f 1936//2 1937//2 1928//2 +f 1906//1 1914//1 1913//1 +f 1935//2 1936//2 1928//2 +f 1906//1 1913//1 1912//1 +f 1934//2 1935//2 1928//2 +f 1906//1 1912//1 1911//1 +f 1933//2 1934//2 1928//2 +f 1906//1 1911//1 1910//1 +f 1932//2 1933//2 1928//2 +f 1906//1 1910//1 1909//1 +f 1931//2 1932//2 1928//2 +f 1906//1 1909//1 1908//1 +f 1930//2 1931//2 1928//2 +f 1908//1 1907//1 1906//1 +f 1928//2 1929//2 1930//2 +f 1906//732 1907//732 1918//732 1917//732 +f 1907//28 1908//28 1919//28 1918//28 +f 1908//733 1909//733 1920//733 1919//733 +f 1909//734 1910//734 1921//734 1920//734 +f 1910//735 1911//735 1922//735 1921//735 +f 1911//736 1912//736 1923//736 1922//736 +f 1912//737 1913//737 1924//737 1923//737 +f 1913//738 1914//738 1925//738 1924//738 +f 1914//13 1915//13 1926//13 1925//13 +f 1915//739 1916//739 1927//739 1926//739 +f 1916//740 1906//740 1917//740 1927//740 +f 1917//741 1918//741 1929//741 1928//741 +f 1918//229 1919//229 1930//229 1929//229 +f 1919//742 1920//742 1931//742 1930//742 +f 1920//743 1921//743 1932//743 1931//743 +f 1921//744 1922//744 1933//744 1932//744 +f 1922//745 1923//745 1934//745 1933//745 +f 1923//746 1924//746 1935//746 1934//746 +f 1924//747 1925//747 1936//747 1935//747 +f 1925//440 1926//440 1937//440 1936//440 +f 1926//748 1927//748 1938//748 1937//748 +f 1927//749 1917//749 1928//749 1938//749 +o tail +usemtl frame +f 1939//20 1940//20 1961//20 1960//20 +f 1940//4 1941//4 1962//4 1961//4 +f 1941//21 1942//21 1963//21 1962//21 +f 1942//9 1943//9 1964//9 1963//9 +f 1943//22 1944//22 1965//22 1964//22 +f 1944//750 1945//750 1966//750 1965//750 +f 1945//751 1946//751 1967//751 1966//751 +f 1946//13 1947//13 1968//13 1967//13 +f 1947//12 1948//12 1969//12 1968//12 +f 1948//752 1949//752 1970//752 1969//752 +f 1949//89 1950//89 1971//89 1970//89 +f 1950//11 1951//11 1972//11 1971//11 +f 1951//753 1952//753 1973//753 1972//753 +f 1952//4 1953//4 1974//4 1973//4 +f 1953//754 1954//754 1975//754 1974//754 +f 1954//11 1955//11 1976//11 1975//11 +f 1955//755 1956//755 1977//755 1976//755 +f 1956//756 1957//756 1978//756 1977//756 +f 1957//757 1958//757 1979//757 1978//757 +f 1958//758 1959//758 1980//758 1979//758 +f 1959//759 1939//759 1960//759 1980//759 +f 1939//1 1959//1 1958//1 +f 1979//2 1980//2 1960//2 +f 1939//1 1958//1 1957//1 +f 1978//2 1979//2 1960//2 +f 1939//1 1957//1 1956//1 +f 1977//2 1978//2 1960//2 +f 1939//1 1956//1 1955//1 +f 1976//2 1977//2 1960//2 +f 1953//1 1952//1 1951//1 +f 1972//2 1973//2 1974//2 +f 1953//1 1951//1 1950//1 +f 1971//2 1972//2 1974//2 +f 1950//1 1949//1 1948//1 +f 1969//2 1970//2 1971//2 +f 1953//1 1950//1 1948//1 +f 1969//2 1971//2 1974//2 +f 1953//1 1948//1 1947//1 +f 1968//2 1969//2 1974//2 +f 1953//1 1947//1 1946//1 +f 1967//2 1968//2 1974//2 +f 1953//1 1946//1 1945//1 +f 1966//2 1967//2 1974//2 +f 1954//1 1953//1 1945//1 +f 1966//2 1974//2 1975//2 +f 1955//1 1954//1 1945//1 +f 1966//2 1975//2 1976//2 +f 1939//1 1955//1 1945//1 +f 1966//2 1976//2 1960//2 +f 1945//1 1944//1 1943//1 +f 1964//2 1965//2 1966//2 +f 1939//1 1945//1 1943//1 +f 1964//2 1966//2 1960//2 +f 1939//1 1943//1 1942//1 +f 1963//2 1964//2 1960//2 +f 1939//1 1942//1 1941//1 +f 1962//2 1963//2 1960//2 +f 1941//1 1940//1 1939//1 +f 1960//2 1961//2 1962//2 +usemtl champagne +f 1981//1 1990//1 1989//1 +f 2009//2 2010//2 2001//2 +f 1981//1 1989//1 1988//1 +f 2008//2 2009//2 2001//2 +f 1981//1 1988//1 1987//1 +f 2007//2 2008//2 2001//2 +f 1981//1 1987//1 1986//1 +f 2006//2 2007//2 2001//2 +f 1981//1 1986//1 1985//1 +f 2005//2 2006//2 2001//2 +f 1981//1 1985//1 1984//1 +f 2004//2 2005//2 2001//2 +f 1984//1 1983//1 1982//1 +f 2002//2 2003//2 2004//2 +f 1984//1 1982//1 1981//1 +f 2001//2 2002//2 2004//2 +f 1981//760 1982//760 1992//760 1991//760 +f 1982//359 1983//359 1993//359 1992//359 +f 1983//28 1984//28 1994//28 1993//28 +f 1984//761 1985//761 1995//761 1994//761 +f 1985//762 1986//762 1996//762 1995//762 +f 1986//763 1987//763 1997//763 1996//763 +f 1987//13 1988//13 1998//13 1997//13 +f 1988//12 1989//12 1999//12 1998//12 +f 1989//752 1990//752 2000//752 1999//752 +f 1990//764 1981//764 1991//764 2000//764 +f 1991//765 1992//765 2002//765 2001//765 +f 1992//385 1993//385 2003//385 2002//385 +f 1993//229 1994//229 2004//229 2003//229 +f 1994//766 1995//766 2005//766 2004//766 +f 1995//767 1996//767 2006//767 2005//767 +f 1996//768 1997//768 2007//768 2006//768 +f 1997//726 1998//726 2008//726 2007//726 +f 1998//769 1999//769 2009//769 2008//769 +f 1999//770 2000//770 2010//770 2009//770 +f 2000//771 1991//771 2001//771 2010//771 +f 2080//1 2094//1 2093//1 +f 2123//2 2124//2 2110//2 +f 2080//1 2093//1 2092//1 +f 2122//2 2123//2 2110//2 +f 2080//1 2092//1 2091//1 +f 2121//2 2122//2 2110//2 +f 2091//1 2090//1 2089//1 +f 2119//2 2120//2 2121//2 +f 2091//1 2089//1 2088//1 +f 2118//2 2119//2 2121//2 +f 2080//1 2091//1 2088//1 +f 2118//2 2121//2 2110//2 +f 2080//1 2088//1 2087//1 +f 2117//2 2118//2 2110//2 +f 2080//1 2087//1 2086//1 +f 2116//2 2117//2 2110//2 +f 2080//1 2086//1 2085//1 +f 2115//2 2116//2 2110//2 +f 2080//1 2085//1 2084//1 +f 2114//2 2115//2 2110//2 +f 2084//1 2083//1 2082//1 +f 2112//2 2113//2 2114//2 +f 2084//1 2082//1 2081//1 +f 2111//2 2112//2 2114//2 +f 2084//1 2081//1 2080//1 +f 2110//2 2111//2 2114//2 +f 2080//772 2081//772 2096//772 2095//772 +f 2081//773 2082//773 2097//773 2096//773 +f 2082//4 2083//4 2098//4 2097//4 +f 2083//774 2084//774 2099//774 2098//774 +f 2084//775 2085//775 2100//775 2099//775 +f 2085//776 2086//776 2101//776 2100//776 +f 2086//595 2087//595 2102//595 2101//595 +f 2087//777 2088//777 2103//777 2102//777 +f 2088//778 2089//778 2104//778 2103//778 +f 2089//13 2090//13 2105//13 2104//13 +f 2090//11 2091//11 2106//11 2105//11 +f 2091//779 2092//779 2107//779 2106//779 +f 2092//780 2093//780 2108//780 2107//780 +f 2093//781 2094//781 2109//781 2108//781 +f 2094//782 2080//782 2095//782 2109//782 +f 2095//783 2096//783 2111//783 2110//783 +f 2096//784 2097//784 2112//784 2111//784 +f 2097//144 2098//144 2113//144 2112//144 +f 2098//785 2099//785 2114//785 2113//785 +f 2099//786 2100//786 2115//786 2114//786 +f 2100//787 2101//787 2116//787 2115//787 +f 2101//788 2102//788 2117//788 2116//788 +f 2102//789 2103//789 2118//789 2117//789 +f 2103//790 2104//790 2119//790 2118//790 +f 2104//440 2105//440 2120//440 2119//440 +f 2105//161 2106//161 2121//161 2120//161 +f 2106//791 2107//791 2122//791 2121//791 +f 2107//792 2108//792 2123//792 2122//792 +f 2108//793 2109//793 2124//793 2123//793 +f 2109//794 2095//794 2110//794 2124//794 +f 2125//1 2138//1 2137//1 +f 2165//2 2166//2 2153//2 +f 2137//1 2136//1 2135//1 +f 2163//2 2164//2 2165//2 +f 2125//1 2137//1 2135//1 +f 2163//2 2165//2 2153//2 +f 2125//1 2135//1 2134//1 +f 2162//2 2163//2 2153//2 +f 2125//1 2134//1 2133//1 +f 2161//2 2162//2 2153//2 +f 2125//1 2133//1 2132//1 +f 2160//2 2161//2 2153//2 +f 2125//1 2132//1 2131//1 +f 2159//2 2160//2 2153//2 +f 2125//1 2131//1 2130//1 +f 2158//2 2159//2 2153//2 +f 2125//1 2130//1 2129//1 +f 2157//2 2158//2 2153//2 +f 2129//1 2128//1 2127//1 +f 2155//2 2156//2 2157//2 +f 2125//1 2129//1 2127//1 +f 2155//2 2157//2 2153//2 +f 2127//1 2126//1 2125//1 +f 2153//2 2154//2 2155//2 +f 2125//124 2126//124 2140//124 2139//124 +f 2126//795 2127//795 2141//795 2140//795 +f 2127//510 2128//510 2142//510 2141//510 +f 2128//511 2129//511 2143//511 2142//511 +f 2129//8 2130//8 2144//8 2143//8 +f 2130//455 2131//455 2145//455 2144//455 +f 2131//796 2132//796 2146//796 2145//796 +f 2132//797 2133//797 2147//797 2146//797 +f 2133//798 2134//798 2148//798 2147//798 +f 2134//799 2135//799 2149//799 2148//799 +f 2135//800 2136//800 2150//800 2149//800 +f 2136//801 2137//801 2151//801 2150//801 +f 2137//710 2138//710 2152//710 2151//710 +f 2138//663 2125//663 2139//663 2152//663 +f 2139//802 2140//802 2154//802 2153//802 +f 2140//803 2141//803 2155//803 2154//803 +f 2141//804 2142//804 2156//804 2155//804 +f 2142//805 2143//805 2157//805 2156//805 +f 2143//230 2144//230 2158//230 2157//230 +f 2144//462 2145//462 2159//462 2158//462 +f 2145//806 2146//806 2160//806 2159//806 +f 2146//807 2147//807 2161//807 2160//807 +f 2147//808 2148//808 2162//808 2161//808 +f 2148//809 2149//809 2163//809 2162//809 +f 2149//810 2150//810 2164//810 2163//810 +f 2150//811 2151//811 2165//811 2164//811 +f 2151//812 2152//812 2166//812 2165//812 +f 2152//813 2139//813 2153//813 2166//813 +usemtl glass +f 2011//1 2018//1 2017//1 +f 2033//2 2034//2 2027//2 +f 2011//1 2017//1 2016//1 +f 2032//2 2033//2 2027//2 +f 2011//1 2016//1 2015//1 +f 2031//2 2032//2 2027//2 +f 2011//1 2015//1 2014//1 +f 2030//2 2031//2 2027//2 +f 2011//1 2014//1 2013//1 +f 2029//2 2030//2 2027//2 +f 2013//1 2012//1 2011//1 +f 2027//2 2028//2 2029//2 +f 2011//27 2012//27 2020//27 2019//27 +f 2012//814 2013//814 2021//814 2020//814 +f 2013//815 2014//815 2022//815 2021//815 +f 2014//816 2015//816 2023//816 2022//816 +f 2015//817 2016//817 2024//817 2023//817 +f 2016//13 2017//13 2025//13 2024//13 +f 2017//488 2018//488 2026//488 2025//488 +f 2018//753 2011//753 2019//753 2026//753 +f 2019//818 2020//818 2028//818 2027//818 +f 2020//819 2021//819 2029//819 2028//819 +f 2021//820 2022//820 2030//820 2029//820 +f 2022//766 2023//766 2031//766 2030//766 +f 2023//821 2024//821 2032//821 2031//821 +f 2024//440 2025//440 2033//440 2032//440 +f 2025//822 2026//822 2034//822 2033//822 +f 2026//823 2019//823 2027//823 2034//823 +f 2167//1 2174//1 2173//1 +f 2189//2 2190//2 2183//2 +f 2167//1 2173//1 2172//1 +f 2188//2 2189//2 2183//2 +f 2167//1 2172//1 2171//1 +f 2187//2 2188//2 2183//2 +f 2167//1 2171//1 2170//1 +f 2186//2 2187//2 2183//2 +f 2167//1 2170//1 2169//1 +f 2185//2 2186//2 2183//2 +f 2169//1 2168//1 2167//1 +f 2183//2 2184//2 2185//2 +f 2167//824 2168//824 2176//824 2175//824 +f 2168//264 2169//264 2177//264 2176//264 +f 2169//825 2170//825 2178//825 2177//825 +f 2170//826 2171//826 2179//826 2178//826 +f 2171//827 2172//827 2180//827 2179//827 +f 2172//828 2173//828 2181//828 2180//828 +f 2173//780 2174//780 2182//780 2181//780 +f 2174//829 2167//829 2175//829 2182//829 +f 2175//830 2176//830 2184//830 2183//830 +f 2176//831 2177//831 2185//831 2184//831 +f 2177//832 2178//832 2186//832 2185//832 +f 2178//833 2179//833 2187//833 2186//833 +f 2179//834 2180//834 2188//834 2187//834 +f 2180//835 2181//835 2189//835 2188//835 +f 2181//836 2182//836 2190//836 2189//836 +f 2182//837 2175//837 2183//837 2190//837 +usemtl dark +f 2035//1 2049//1 2048//1 +f 2078//2 2079//2 2065//2 +f 2035//1 2048//1 2047//1 +f 2077//2 2078//2 2065//2 +f 2046//1 2045//1 2044//1 +f 2074//2 2075//2 2076//2 +f 2046//1 2044//1 2043//1 +f 2073//2 2074//2 2076//2 +f 2047//1 2046//1 2043//1 +f 2073//2 2076//2 2077//2 +f 2035//1 2047//1 2043//1 +f 2073//2 2077//2 2065//2 +f 2035//1 2043//1 2042//1 +f 2072//2 2073//2 2065//2 +f 2042//1 2041//1 2040//1 +f 2070//2 2071//2 2072//2 +f 2042//1 2040//1 2039//1 +f 2069//2 2070//2 2072//2 +f 2035//1 2042//1 2039//1 +f 2069//2 2072//2 2065//2 +f 2035//1 2039//1 2038//1 +f 2068//2 2069//2 2065//2 +f 2038//1 2037//1 2036//1 +f 2066//2 2067//2 2068//2 +f 2038//1 2036//1 2035//1 +f 2065//2 2066//2 2068//2 +f 2035//838 2036//838 2051//838 2050//838 +f 2036//839 2037//839 2052//839 2051//839 +f 2037//638 2038//638 2053//638 2052//638 +f 2038//840 2039//840 2054//840 2053//840 +f 2039//455 2040//455 2055//455 2054//455 +f 2040//841 2041//841 2056//841 2055//841 +f 2041//140 2042//140 2057//140 2056//140 +f 2042//842 2043//842 2058//842 2057//842 +f 2043//843 2044//843 2059//843 2058//843 +f 2044//844 2045//844 2060//844 2059//844 +f 2045//663 2046//663 2061//663 2060//663 +f 2046//845 2047//845 2062//845 2061//845 +f 2047//846 2048//846 2063//846 2062//846 +f 2048//847 2049//847 2064//847 2063//847 +f 2049//848 2035//848 2050//848 2064//848 +f 2050//849 2051//849 2066//849 2065//849 +f 2051//850 2052//850 2067//850 2066//850 +f 2052//524 2053//524 2068//524 2067//524 +f 2053//851 2054//851 2069//851 2068//851 +f 2054//726 2055//726 2070//726 2069//726 +f 2055//852 2056//852 2071//852 2070//852 +f 2056//169 2057//169 2072//169 2071//169 +f 2057//853 2058//853 2073//853 2072//853 +f 2058//854 2059//854 2074//854 2073//854 +f 2059//855 2060//855 2075//855 2074//855 +f 2060//479 2061//479 2076//479 2075//479 +f 2061//856 2062//856 2077//856 2076//856 +f 2062//857 2063//857 2078//857 2077//857 +f 2063//858 2064//858 2079//858 2078//858 +f 2064//859 2050//859 2065//859 2079//859 +o hindLegR +usemtl frame +f 2191//860 2192//860 2205//860 2204//860 +f 2192//4 2193//4 2206//4 2205//4 +f 2193//861 2194//861 2207//861 2206//861 +f 2194//4 2195//4 2208//4 2207//4 +f 2195//359 2196//359 2209//359 2208//359 +f 2196//4 2197//4 2210//4 2209//4 +f 2197//862 2198//862 2211//862 2210//862 +f 2198//13 2199//13 2212//13 2211//13 +f 2199//96 2200//96 2213//96 2212//96 +f 2200//13 2201//13 2214//13 2213//13 +f 2201//863 2202//863 2215//863 2214//863 +f 2202//864 2203//864 2216//864 2215//864 +f 2203//4 2191//4 2204//4 2216//4 +f 2191//1 2203//1 2202//1 +f 2215//2 2216//2 2204//2 +f 2191//1 2202//1 2201//1 +f 2214//2 2215//2 2204//2 +f 2191//1 2201//1 2200//1 +f 2213//2 2214//2 2204//2 +f 2191//1 2200//1 2199//1 +f 2212//2 2213//2 2204//2 +f 2191//1 2199//1 2198//1 +f 2211//2 2212//2 2204//2 +f 2198//1 2197//1 2196//1 +f 2209//2 2210//2 2211//2 +f 2198//1 2196//1 2195//1 +f 2208//2 2209//2 2211//2 +f 2191//1 2198//1 2195//1 +f 2208//2 2211//2 2204//2 +f 2191//1 2195//1 2194//1 +f 2207//2 2208//2 2204//2 +f 2191//1 2194//1 2193//1 +f 2206//2 2207//2 2204//2 +f 2193//1 2192//1 2191//1 +f 2204//2 2205//2 2206//2 +usemtl dark +f 2217//1 2226//1 2225//1 +f 2245//2 2246//2 2237//2 +f 2217//1 2225//1 2224//1 +f 2244//2 2245//2 2237//2 +f 2217//1 2224//1 2223//1 +f 2243//2 2244//2 2237//2 +f 2217//1 2223//1 2222//1 +f 2242//2 2243//2 2237//2 +f 2217//1 2222//1 2221//1 +f 2241//2 2242//2 2237//2 +f 2221//1 2220//1 2219//1 +f 2239//2 2240//2 2241//2 +f 2217//1 2221//1 2219//1 +f 2239//2 2241//2 2237//2 +f 2219//1 2218//1 2217//1 +f 2237//2 2238//2 2239//2 +f 2217//663 2218//663 2228//663 2227//663 +f 2218//865 2219//865 2229//865 2228//865 +f 2219//674 2220//674 2230//674 2229//674 +f 2220//866 2221//866 2231//866 2230//866 +f 2221//867 2222//867 2232//867 2231//867 +f 2222//868 2223//868 2233//868 2232//868 +f 2223//8 2224//8 2234//8 2233//8 +f 2224//869 2225//869 2235//869 2234//869 +f 2225//870 2226//870 2236//870 2235//870 +f 2226//871 2217//871 2227//871 2236//871 +f 2227//813 2228//813 2238//813 2237//813 +f 2228//872 2229//872 2239//872 2238//872 +f 2229//873 2230//873 2240//873 2239//873 +f 2230//874 2231//874 2241//874 2240//874 +f 2231//875 2232//875 2242//875 2241//875 +f 2232//876 2233//876 2243//876 2242//876 +f 2233//230 2234//230 2244//230 2243//230 +f 2234//877 2235//877 2245//877 2244//877 +f 2235//878 2236//878 2246//878 2245//878 +f 2236//879 2227//879 2237//879 2246//879 +f 2247//1 2255//1 2254//1 +f 2272//2 2273//2 2265//2 +f 2247//1 2254//1 2253//1 +f 2271//2 2272//2 2265//2 +f 2253//1 2252//1 2251//1 +f 2269//2 2270//2 2271//2 +f 2247//1 2253//1 2251//1 +f 2269//1 2271//1 2265//1 +f 2247//1 2251//1 2250//1 +f 2268//2 2269//2 2265//2 +f 2247//1 2250//1 2249//1 +f 2267//2 2268//2 2265//2 +f 2249//1 2248//1 2247//1 +f 2265//2 2266//2 2267//2 +f 2247//359 2248//359 2257//359 2256//359 +f 2248//4 2249//4 2258//4 2257//4 +f 2249//880 2250//880 2259//880 2258//880 +f 2250//95 2251//95 2260//95 2259//95 +f 2251//13 2252//13 2261//13 2260//13 +f 2252//881 2253//881 2262//881 2261//881 +f 2253//882 2254//882 2263//882 2262//882 +f 2254//883 2255//883 2264//883 2263//883 +f 2255//457 2247//457 2256//457 2264//457 +f 2256//385 2257//385 2266//385 2265//385 +f 2257//144 2258//144 2267//144 2266//144 +f 2258//884 2259//884 2268//884 2267//884 +f 2259//885 2260//885 2269//885 2268//885 +f 2260//440 2261//440 2270//440 2269//440 +f 2261//886 2262//886 2271//886 2270//886 +f 2262//887 2263//887 2272//887 2271//887 +f 2263//888 2264//888 2273//888 2272//888 +f 2264//144 2256//144 2265//144 2273//144 +usemtl champagne +f 2274//1 2286//1 2285//1 +f 2311//2 2312//2 2300//2 +f 2274//1 2285//1 2284//1 +f 2310//2 2311//2 2300//2 +f 2274//1 2284//1 2283//1 +f 2309//2 2310//2 2300//2 +f 2281//1 2280//1 2279//1 +f 2305//2 2306//2 2307//2 +f 2281//1 2279//1 2278//1 +f 2304//2 2305//2 2307//2 +f 2282//1 2281//1 2278//1 +f 2304//2 2307//2 2308//2 +f 2283//1 2282//1 2278//1 +f 2304//2 2308//2 2309//2 +f 2283//1 2278//1 2277//1 +f 2303//2 2304//2 2309//2 +f 2283//1 2277//1 2276//1 +f 2302//2 2303//2 2309//2 +f 2283//1 2276//1 2275//1 +f 2301//2 2302//2 2309//2 +f 2283//1 2275//1 2274//1 +f 2300//2 2301//2 2309//2 +f 2274//28 2275//28 2288//28 2287//28 +f 2275//889 2276//889 2289//889 2288//889 +f 2276//890 2277//890 2290//890 2289//890 +f 2277//28 2278//28 2291//28 2290//28 +f 2278//891 2279//891 2292//891 2291//891 +f 2279//8 2280//8 2293//8 2292//8 +f 2280//13 2281//13 2294//13 2293//13 +f 2281//8 2282//8 2295//8 2294//8 +f 2282//892 2283//892 2296//892 2295//892 +f 2283//13 2284//13 2297//13 2296//13 +f 2284//863 2285//863 2298//863 2297//863 +f 2285//864 2286//864 2299//864 2298//864 +f 2286//4 2274//4 2287//4 2299//4 +f 2287//229 2288//229 2301//229 2300//229 +f 2288//893 2289//893 2302//893 2301//893 +f 2289//894 2290//894 2303//894 2302//894 +f 2290//229 2291//229 2304//229 2303//229 +f 2291//895 2292//895 2305//895 2304//895 +f 2292//230 2293//230 2306//230 2305//230 +f 2293//440 2294//440 2307//440 2306//440 +f 2294//230 2295//230 2308//230 2307//230 +f 2295//896 2296//896 2309//896 2308//896 +f 2296//440 2297//440 2310//440 2309//440 +f 2297//897 2298//897 2311//897 2310//897 +f 2298//898 2299//898 2312//898 2311//898 +f 2299//144 2287//144 2300//144 2312//144 +o foreLegR +usemtl frame +f 2313//899 2314//899 2320//899 2319//899 +f 2314//900 2315//900 2321//900 2320//900 +f 2315//901 2316//901 2322//901 2321//901 +f 2316//902 2317//902 2323//902 2322//902 +f 2317//903 2318//903 2324//903 2323//903 +f 2318//904 2313//904 2319//904 2324//904 +f 2313//1 2318//1 2317//1 +f 2323//2 2324//2 2319//2 +f 2313//1 2317//1 2316//1 +f 2322//2 2323//2 2319//2 +f 2313//1 2316//1 2315//1 +f 2321//2 2322//2 2319//2 +f 2315//1 2314//1 2313//1 +f 2319//2 2320//2 2321//2 +usemtl glass +f 2325//1 2336//1 2335//1 +f 2359//2 2360//2 2349//2 +f 2325//1 2335//1 2334//1 +f 2358//2 2359//2 2349//2 +f 2325//1 2334//1 2333//1 +f 2357//2 2358//2 2349//2 +f 2325//1 2333//1 2332//1 +f 2356//2 2357//2 2349//2 +f 2325//1 2332//1 2331//1 +f 2355//2 2356//2 2349//2 +f 2325//1 2331//1 2330//1 +f 2354//2 2355//2 2349//2 +f 2325//1 2330//1 2329//1 +f 2353//2 2354//2 2349//2 +f 2325//1 2329//1 2328//1 +f 2352//2 2353//2 2349//2 +f 2325//1 2328//1 2327//1 +f 2351//2 2352//2 2349//2 +f 2327//1 2326//1 2325//1 +f 2349//2 2350//2 2351//2 +f 2325//905 2326//905 2338//905 2337//905 +f 2326//906 2327//906 2339//906 2338//906 +f 2327//9 2328//9 2340//9 2339//9 +f 2328//907 2329//907 2341//907 2340//907 +f 2329//675 2330//675 2342//675 2341//675 +f 2330//908 2331//908 2343//908 2342//908 +f 2331//737 2332//737 2344//737 2343//737 +f 2332//909 2333//909 2345//909 2344//909 +f 2333//62 2334//62 2346//62 2345//62 +f 2334//910 2335//910 2347//910 2346//910 +f 2335//710 2336//710 2348//710 2347//710 +f 2336//904 2325//904 2337//904 2348//904 +f 2337//911 2338//911 2350//911 2349//911 +f 2338//912 2339//912 2351//912 2350//912 +f 2339//437 2340//437 2352//437 2351//437 +f 2340//913 2341//913 2353//913 2352//913 +f 2341//914 2342//914 2354//914 2353//914 +f 2342//915 2343//915 2355//915 2354//915 +f 2343//916 2344//916 2356//916 2355//916 +f 2344//917 2345//917 2357//917 2356//917 +f 2345//918 2346//918 2358//918 2357//918 +f 2346//919 2347//919 2359//919 2358//919 +f 2347//920 2348//920 2360//920 2359//920 +f 2348//921 2337//921 2349//921 2360//921 +usemtl dark +f 2361//1 2369//1 2368//1 +f 2386//2 2387//2 2379//2 +f 2361//1 2368//1 2367//1 +f 2385//2 2386//2 2379//2 +f 2361//1 2367//1 2366//1 +f 2384//2 2385//2 2379//2 +f 2361//1 2366//1 2365//1 +f 2383//2 2384//2 2379//2 +f 2365//1 2364//1 2363//1 +f 2381//2 2382//2 2383//2 +f 2365//1 2363//1 2362//1 +f 2380//1 2381//1 2383//1 +f 2365//1 2362//1 2361//1 +f 2379//1 2380//1 2383//1 +f 2361//922 2362//922 2371//922 2370//922 +f 2362//923 2363//923 2372//923 2371//923 +f 2363//924 2364//924 2373//924 2372//924 +f 2364//486 2365//486 2374//486 2373//486 +f 2365//925 2366//925 2375//925 2374//925 +f 2366//103 2367//103 2376//103 2375//103 +f 2367//926 2368//926 2377//926 2376//926 +f 2368//710 2369//710 2378//710 2377//710 +f 2369//663 2361//663 2370//663 2378//663 +f 2370//927 2371//927 2380//927 2379//927 +f 2371//928 2372//928 2381//928 2380//928 +f 2372//929 2373//929 2382//929 2381//929 +f 2373//491 2374//491 2383//491 2382//491 +f 2374//930 2375//930 2384//930 2383//930 +f 2375//931 2376//931 2385//931 2384//931 +f 2376//932 2377//932 2386//932 2385//932 +f 2377//933 2378//933 2387//933 2386//933 +f 2378//671 2370//671 2379//671 2387//671 +o hornR +usemtl frame +f 2388//934 2389//934 2394//934 2393//934 +f 2389//935 2390//935 2395//935 2394//935 +f 2390//936 2391//936 2396//936 2395//936 +f 2391//937 2392//937 2397//937 2396//937 +f 2392//4 2388//4 2393//4 2397//4 +f 2388//1 2392//1 2391//1 +f 2396//2 2397//2 2393//2 +f 2388//1 2391//1 2390//1 +f 2395//2 2396//2 2393//2 +f 2390//1 2389//1 2388//1 +f 2393//2 2394//2 2395//2 +usemtl dark +f 2398//1 2403//1 2402//1 +f 2414//2 2415//2 2410//2 +f 2398//1 2402//1 2401//1 +f 2413//2 2414//2 2410//2 +f 2398//1 2401//1 2400//1 +f 2412//2 2413//2 2410//2 +f 2400//1 2399//1 2398//1 +f 2410//2 2411//2 2412//2 +f 2398//934 2399//934 2405//934 2404//934 +f 2399//935 2400//935 2406//935 2405//935 +f 2400//938 2401//938 2407//938 2406//938 +f 2401//939 2402//939 2408//939 2407//939 +f 2402//940 2403//940 2409//940 2408//940 +f 2403//4 2398//4 2404//4 2409//4 +f 2404//941 2405//941 2411//941 2410//941 +f 2405//942 2406//942 2412//942 2411//942 +f 2406//943 2407//943 2413//943 2412//943 +f 2407//944 2408//944 2414//944 2413//944 +f 2408//945 2409//945 2415//945 2414//945 +f 2409//144 2404//144 2410//144 2415//144 +o crest +usemtl frame +f 2416//946 2417//946 2422//946 2421//946 +f 2417//947 2418//947 2423//947 2422//947 +f 2418//936 2419//936 2424//936 2423//936 +f 2419//948 2420//948 2425//948 2424//948 +f 2420//949 2416//949 2421//949 2425//949 +f 2416//1 2420//1 2419//1 +f 2424//2 2425//2 2421//2 +f 2416//1 2419//1 2418//1 +f 2423//2 2424//2 2421//2 +f 2418//1 2417//1 2416//1 +f 2421//2 2422//2 2423//2 +usemtl champagne +f 2426//1 2433//1 2432//1 +f 2448//2 2449//2 2442//2 +f 2426//1 2432//1 2431//1 +f 2447//2 2448//2 2442//2 +f 2426//1 2431//1 2430//1 +f 2446//2 2447//2 2442//2 +f 2426//1 2430//1 2429//1 +f 2445//2 2446//2 2442//2 +f 2426//1 2429//1 2428//1 +f 2444//2 2445//2 2442//2 +f 2428//1 2427//1 2426//1 +f 2442//2 2443//2 2444//2 +f 2426//950 2427//950 2435//950 2434//950 +f 2427//951 2428//951 2436//951 2435//951 +f 2428//9 2429//9 2437//9 2436//9 +f 2429//947 2430//947 2438//947 2437//947 +f 2430//938 2431//938 2439//938 2438//938 +f 2431//758 2432//758 2440//758 2439//758 +f 2432//114 2433//114 2441//114 2440//114 +f 2433//4 2426//4 2434//4 2441//4 +f 2434//952 2435//952 2443//952 2442//952 +f 2435//953 2436//953 2444//953 2443//953 +f 2436//954 2437//954 2445//954 2444//954 +f 2437//955 2438//955 2446//955 2445//955 +f 2438//956 2439//956 2447//956 2446//956 +f 2439//792 2440//792 2448//792 2447//792 +f 2440//957 2441//957 2449//957 2448//957 +f 2441//479 2434//479 2442//479 2449//479 +o wingR +usemtl frame +f 2450//958 2451//958 2465//958 2464//958 +f 2451//15 2452//15 2466//15 2465//15 +f 2452//4 2453//4 2467//4 2466//4 +f 2453//959 2454//959 2468//959 2467//959 +f 2454//429 2455//429 2469//429 2468//429 +f 2455//960 2456//960 2470//960 2469//960 +f 2456//961 2457//961 2471//961 2470//961 +f 2457//962 2458//962 2472//962 2471//962 +f 2458//13 2459//13 2473//13 2472//13 +f 2459//963 2460//963 2474//963 2473//963 +f 2460//13 2461//13 2475//13 2474//13 +f 2461//11 2462//11 2476//11 2475//11 +f 2462//14 2463//14 2477//14 2476//14 +f 2463//4 2450//4 2464//4 2477//4 +f 2450//1 2463//1 2462//1 +f 2476//2 2477//2 2464//2 +f 2450//1 2462//1 2461//1 +f 2475//2 2476//2 2464//2 +f 2450//1 2461//1 2460//1 +f 2474//2 2475//2 2464//2 +f 2450//1 2460//1 2459//1 +f 2473//2 2474//2 2464//2 +f 2450//1 2459//1 2458//1 +f 2472//2 2473//2 2464//2 +f 2450//1 2458//1 2457//1 +f 2471//2 2472//2 2464//2 +f 2457//1 2456//1 2455//1 +f 2469//2 2470//2 2471//2 +f 2457//1 2455//1 2454//1 +f 2468//2 2469//2 2471//2 +f 2457//1 2454//1 2453//1 +f 2467//2 2468//2 2471//2 +f 2457//1 2453//1 2452//1 +f 2466//2 2467//2 2471//2 +f 2450//1 2457//1 2452//1 +f 2466//2 2471//2 2464//2 +f 2452//1 2451//1 2450//1 +f 2464//2 2465//2 2466//2 +usemtl dark +f 2478//1 2490//1 2489//1 +f 2515//2 2516//2 2504//2 +f 2478//1 2489//1 2488//1 +f 2514//2 2515//2 2504//2 +f 2478//1 2488//1 2487//1 +f 2513//2 2514//2 2504//2 +f 2478//1 2487//1 2486//1 +f 2512//2 2513//2 2504//2 +f 2478//1 2486//1 2485//1 +f 2511//2 2512//2 2504//2 +f 2478//1 2485//1 2484//1 +f 2510//2 2511//2 2504//2 +f 2478//1 2484//1 2483//1 +f 2509//2 2510//2 2504//2 +f 2483//1 2482//1 2481//1 +f 2507//2 2508//2 2509//2 +f 2483//1 2481//1 2480//1 +f 2506//2 2507//2 2509//2 +f 2478//1 2483//1 2480//1 +f 2506//2 2509//2 2504//2 +f 2480//1 2479//1 2478//1 +f 2504//2 2505//2 2506//2 +f 2478//964 2479//964 2492//964 2491//964 +f 2479//15 2480//15 2493//15 2492//15 +f 2480//663 2481//663 2494//663 2493//663 +f 2481//965 2482//965 2495//965 2494//965 +f 2482//966 2483//966 2496//966 2495//966 +f 2483//967 2484//967 2497//967 2496//967 +f 2484//8 2485//8 2498//8 2497//8 +f 2485//968 2486//968 2499//968 2498//968 +f 2486//34 2487//34 2500//34 2499//34 +f 2487//969 2488//969 2501//969 2500//969 +f 2488//488 2489//488 2502//488 2501//488 +f 2489//14 2490//14 2503//14 2502//14 +f 2490//663 2478//663 2491//663 2503//663 +f 2491//970 2492//970 2505//970 2504//970 +f 2492//971 2493//971 2506//971 2505//971 +f 2493//144 2494//144 2507//144 2506//144 +f 2494//972 2495//972 2508//972 2507//972 +f 2495//973 2496//973 2509//973 2508//973 +f 2496//974 2497//974 2510//974 2509//974 +f 2497//230 2498//230 2511//230 2510//230 +f 2498//703 2499//703 2512//703 2511//703 +f 2499//975 2500//975 2513//975 2512//975 +f 2500//976 2501//976 2514//976 2513//976 +f 2501//822 2502//822 2515//822 2514//822 +f 2502//977 2503//977 2516//977 2515//977 +f 2503//671 2491//671 2504//671 2516//671 +usemtl glass +f 2517//1 2527//1 2526//1 +f 2548//2 2549//2 2539//2 +f 2526//1 2525//1 2524//1 +f 2546//2 2547//2 2548//2 +f 2517//1 2526//1 2524//1 +f 2546//2 2548//2 2539//2 +f 2517//1 2524//1 2523//1 +f 2545//2 2546//2 2539//2 +f 2517//1 2523//1 2522//1 +f 2544//2 2545//2 2539//2 +f 2517//1 2522//1 2521//1 +f 2543//2 2544//2 2539//2 +f 2517//1 2521//1 2520//1 +f 2542//2 2543//2 2539//2 +f 2517//1 2520//1 2519//1 +f 2541//2 2542//2 2539//2 +f 2519//1 2518//1 2517//1 +f 2539//2 2540//2 2541//2 +f 2517//958 2518//958 2529//958 2528//958 +f 2518//978 2519//978 2530//978 2529//978 +f 2519//9 2520//9 2531//9 2530//9 +f 2520//979 2521//979 2532//979 2531//979 +f 2521//980 2522//980 2533//980 2532//980 +f 2522//981 2523//981 2534//981 2533//981 +f 2523//982 2524//982 2535//982 2534//982 +f 2524//983 2525//983 2536//983 2535//983 +f 2525//171 2526//171 2537//171 2536//171 +f 2526//984 2527//984 2538//984 2537//984 +f 2527//663 2517//663 2528//663 2538//663 +f 2528//985 2529//985 2540//985 2539//985 +f 2529//986 2530//986 2541//986 2540//986 +f 2530//437 2531//437 2542//437 2541//437 +f 2531//987 2532//987 2543//987 2542//987 +f 2532//988 2533//988 2544//988 2543//988 +f 2533//856 2534//856 2545//856 2544//856 +f 2534//989 2535//989 2546//989 2545//989 +f 2535//990 2536//990 2547//990 2546//990 +f 2536//180 2537//180 2548//180 2547//180 +f 2537//991 2538//991 2549//991 2548//991 +f 2538//671 2528//671 2539//671 2549//671 +f 2550//1 2562//1 2561//1 +f 2587//2 2588//2 2576//2 +f 2550//1 2561//1 2560//1 +f 2586//2 2587//2 2576//2 +f 2550//1 2560//1 2559//1 +f 2585//2 2586//2 2576//2 +f 2550//1 2559//1 2558//1 +f 2584//2 2585//2 2576//2 +f 2550//1 2558//1 2557//1 +f 2583//2 2584//2 2576//2 +f 2550//1 2557//1 2556//1 +f 2582//2 2583//2 2576//2 +f 2550//1 2556//1 2555//1 +f 2581//2 2582//2 2576//2 +f 2550//1 2555//1 2554//1 +f 2580//2 2581//2 2576//2 +f 2550//1 2554//1 2553//1 +f 2579//2 2580//2 2576//2 +f 2553//1 2552//1 2551//1 +f 2577//2 2578//2 2579//2 +f 2553//1 2551//1 2550//1 +f 2576//2 2577//2 2579//2 +f 2550//992 2551//992 2564//992 2563//992 +f 2551//359 2552//359 2565//359 2564//359 +f 2552//4 2553//4 2566//4 2565//4 +f 2553//993 2554//993 2567//993 2566//993 +f 2554//190 2555//190 2568//190 2567//190 +f 2555//486 2556//486 2569//486 2568//486 +f 2556//994 2557//994 2570//994 2569//994 +f 2557//995 2558//995 2571//995 2570//995 +f 2558//13 2559//13 2572//13 2571//13 +f 2559//996 2560//996 2573//996 2572//996 +f 2560//997 2561//997 2574//997 2573//997 +f 2561//863 2562//863 2575//863 2574//863 +f 2562//998 2550//998 2563//998 2575//998 +f 2563//999 2564//999 2577//999 2576//999 +f 2564//385 2565//385 2578//385 2577//385 +f 2565//144 2566//144 2579//144 2578//144 +f 2566//1000 2567//1000 2580//1000 2579//1000 +f 2567//1001 2568//1001 2581//1001 2580//1001 +f 2568//442 2569//442 2582//442 2581//442 +f 2569//1002 2570//1002 2583//1002 2582//1002 +f 2570//1003 2571//1003 2584//1003 2583//1003 +f 2571//1004 2572//1004 2585//1004 2584//1004 +f 2572//1005 2573//1005 2586//1005 2585//1005 +f 2573//1006 2574//1006 2587//1006 2586//1006 +f 2574//1007 2575//1007 2588//1007 2587//1007 +f 2575//1008 2563//1008 2576//1008 2588//1008 diff --git a/assets/SingeText.glb b/assets/SingeText.glb new file mode 100644 index 000000000..9446c38f2 --- /dev/null +++ b/assets/SingeText.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:979ec317a81c7d47978e106cf29f59cfaa9855d11741ac3bc200fb1b92e6fbb6 +size 62136 diff --git a/assets/SingeText.mtl b/assets/SingeText.mtl new file mode 100644 index 000000000..6f7c2a17b --- /dev/null +++ b/assets/SingeText.mtl @@ -0,0 +1,12 @@ +# Materials for SingeText.obj +newmtl logo +Ka 0.060 0.062 0.066 +Kd 0.300 0.310 0.330 +Ks 0.700 0.700 0.700 +Ns 180.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.35 + diff --git a/assets/SingeText.obj b/assets/SingeText.obj new file mode 100644 index 000000000..18767cb53 --- /dev/null +++ b/assets/SingeText.obj @@ -0,0 +1,1735 @@ +# Singe logotype, extruded from SingeText.jpeg by util/textModel.py +# Units: 0.01 per image pixel; Y up, front faces toward +Z, resting on Y = 0, centred on X = 0 +mtllib SingeText.mtl +v -3.60267 4.62648 -0.15000 +v -4.22784 4.21467 -0.15000 +v -4.93891 3.63288 -0.15000 +v -5.20536 3.36643 -0.15000 +v -5.39561 3.13337 -0.15000 +v -5.39837 3.11821 -0.15000 +v -5.36178 3.08467 -0.15000 +v -4.32338 2.41071 -0.15000 +v -3.98950 2.14837 -0.15000 +v -4.62411 1.68883 -0.15000 +v -5.88678 0.86020 -0.15000 +v -5.89128 0.83320 -0.15000 +v -5.69555 0.17752 -0.15000 +v -5.64412 0.06363 -0.15000 +v -5.63565 0.06575 -0.15000 +v -4.76870 0.78409 -0.15000 +v -3.49459 1.76340 -0.15000 +v -3.22833 2.00500 -0.15000 +v -3.01961 2.24620 -0.15000 +v -3.02026 2.25205 -0.15000 +v -3.07565 2.30744 -0.15000 +v -3.29599 2.47882 -0.15000 +v -4.42097 3.16747 -0.15000 +v -4.13323 3.42459 -0.15000 +v -3.70312 3.71302 -0.15000 +v -2.74711 4.21517 -0.15000 +v -2.93473 4.84839 -0.15000 +v -2.97051 4.93585 -0.15000 +v -2.96990 4.93562 -0.15000 +v -3.62750 4.67000 -0.10000 +v -4.25750 4.25500 -0.10000 +v -4.97250 3.67000 -0.10000 +v -5.24250 3.40000 -0.10000 +v -5.44250 3.15500 -0.10000 +v -5.45250 3.10000 -0.10000 +v -5.39250 3.04500 -0.10000 +v -4.35250 2.37000 -0.10000 +v -4.07250 2.15000 -0.10000 +v -4.65250 1.73000 -0.10000 +v -5.93250 0.89000 -0.10000 +v -5.94250 0.83000 -0.10000 +v -5.74250 0.16000 -0.10000 +v -5.67250 0.00500 -0.10000 +v -5.61250 0.02000 -0.10000 +v -4.73750 0.74500 -0.10000 +v -3.46250 1.72500 -0.10000 +v -3.19250 1.97000 -0.10000 +v -2.96750 2.23000 -0.10000 +v -2.97250 2.27500 -0.10000 +v -3.04250 2.34500 -0.10000 +v -3.26750 2.52000 -0.10000 +v -4.33750 3.17500 -0.10000 +v -4.10250 3.38500 -0.10000 +v -3.67750 3.67000 -0.10000 +v -2.68750 4.19000 -0.10000 +v -2.88750 4.86500 -0.10000 +v -2.93250 4.97500 -0.10000 +v -2.97250 4.99000 -0.10000 +v -3.62750 4.67000 0.10000 +v -4.25750 4.25500 0.10000 +v -4.97250 3.67000 0.10000 +v -5.24250 3.40000 0.10000 +v -5.44250 3.15500 0.10000 +v -5.45250 3.10000 0.10000 +v -5.39250 3.04500 0.10000 +v -4.35250 2.37000 0.10000 +v -4.07250 2.15000 0.10000 +v -4.65250 1.73000 0.10000 +v -5.93250 0.89000 0.10000 +v -5.94250 0.83000 0.10000 +v -5.74250 0.16000 0.10000 +v -5.67250 0.00500 0.10000 +v -5.61250 0.02000 0.10000 +v -4.73750 0.74500 0.10000 +v -3.46250 1.72500 0.10000 +v -3.19250 1.97000 0.10000 +v -2.96750 2.23000 0.10000 +v -2.97250 2.27500 0.10000 +v -3.04250 2.34500 0.10000 +v -3.26750 2.52000 0.10000 +v -4.33750 3.17500 0.10000 +v -4.10250 3.38500 0.10000 +v -3.67750 3.67000 0.10000 +v -2.68750 4.19000 0.10000 +v -2.88750 4.86500 0.10000 +v -2.93250 4.97500 0.10000 +v -2.97250 4.99000 0.10000 +v -3.60267 4.62648 0.15000 +v -4.22784 4.21467 0.15000 +v -4.93891 3.63288 0.15000 +v -5.20536 3.36643 0.15000 +v -5.39561 3.13337 0.15000 +v -5.39837 3.11821 0.15000 +v -5.36178 3.08467 0.15000 +v -4.32338 2.41071 0.15000 +v -3.98950 2.14837 0.15000 +v -4.62411 1.68883 0.15000 +v -5.88678 0.86020 0.15000 +v -5.89128 0.83320 0.15000 +v -5.69555 0.17752 0.15000 +v -5.64412 0.06363 0.15000 +v -5.63565 0.06575 0.15000 +v -4.76870 0.78409 0.15000 +v -3.49459 1.76340 0.15000 +v -3.22833 2.00500 0.15000 +v -3.01961 2.24620 0.15000 +v -3.02026 2.25205 0.15000 +v -3.07565 2.30744 0.15000 +v -3.29599 2.47882 0.15000 +v -4.42097 3.16747 0.15000 +v -4.13323 3.42459 0.15000 +v -3.70312 3.71302 0.15000 +v -2.74711 4.21517 0.15000 +v -2.93473 4.84839 0.15000 +v -2.97051 4.93585 0.15000 +v -2.96990 4.93562 0.15000 +v -2.41704 3.44114 -0.15000 +v -2.45748 3.40743 -0.15000 +v -3.04353 0.81987 -0.15000 +v -3.07049 0.67608 -0.15000 +v -3.07604 0.68857 -0.15000 +v -2.55820 0.88562 -0.15000 +v -2.42103 0.95849 -0.15000 +v -2.09606 2.35191 -0.15000 +v -1.74821 3.70359 -0.15000 +v -1.74802 3.70190 -0.15000 +v -1.72754 3.69848 -0.15000 +v -2.44250 3.48500 -0.10000 +v -2.50250 3.43500 -0.10000 +v -3.09250 0.83000 -0.10000 +v -3.12250 0.67000 -0.10000 +v -3.10250 0.62500 -0.10000 +v -2.53750 0.84000 -0.10000 +v -2.37750 0.92500 -0.10000 +v -2.04750 2.34000 -0.10000 +v -1.69750 3.70000 -0.10000 +v -1.70250 3.74500 -0.10000 +v -1.73250 3.75000 -0.10000 +v -2.44250 3.48500 0.10000 +v -2.50250 3.43500 0.10000 +v -3.09250 0.83000 0.10000 +v -3.12250 0.67000 0.10000 +v -3.10250 0.62500 0.10000 +v -2.53750 0.84000 0.10000 +v -2.37750 0.92500 0.10000 +v -2.04750 2.34000 0.10000 +v -1.69750 3.70000 0.10000 +v -1.70250 3.74500 0.10000 +v -1.73250 3.75000 0.10000 +v -2.41704 3.44114 0.15000 +v -2.45748 3.40743 0.15000 +v -3.04353 0.81987 0.15000 +v -3.07049 0.67608 0.15000 +v -3.07604 0.68857 0.15000 +v -2.55820 0.88562 0.15000 +v -2.42103 0.95849 0.15000 +v -2.09606 2.35191 0.15000 +v -1.74821 3.70359 0.15000 +v -1.74802 3.70190 0.15000 +v -1.72754 3.69848 0.15000 +v -2.23752 4.50288 -0.15000 +v -2.24654 4.49205 -0.15000 +v -2.39874 3.84636 -0.15000 +v -2.41691 3.85318 -0.15000 +v -2.33522 3.88177 -0.15000 +v -1.62225 4.17278 -0.15000 +v -1.38750 4.84350 -0.15000 +v -1.38750 4.83896 -0.15000 +v -1.36645 4.83195 -0.15000 +v -2.26750 4.54500 -0.10000 +v -2.29250 4.51500 -0.10000 +v -2.45750 3.81500 -0.10000 +v -2.41750 3.80000 -0.10000 +v -2.31750 3.83500 -0.10000 +v -1.58250 4.13500 -0.10000 +v -1.33750 4.83500 -0.10000 +v -1.33750 4.87500 -0.10000 +v -1.36750 4.88500 -0.10000 +v -2.26750 4.54500 0.10000 +v -2.29250 4.51500 0.10000 +v -2.45750 3.81500 0.10000 +v -2.41750 3.80000 0.10000 +v -2.31750 3.83500 0.10000 +v -1.58250 4.13500 0.10000 +v -1.33750 4.83500 0.10000 +v -1.33750 4.87500 0.10000 +v -1.36750 4.88500 0.10000 +v -2.23752 4.50288 0.15000 +v -2.24654 4.49205 0.15000 +v -2.39874 3.84636 0.15000 +v -2.41691 3.85318 0.15000 +v -2.33522 3.88177 0.15000 +v -1.62225 4.17278 0.15000 +v -1.38750 4.84350 0.15000 +v -1.38750 4.83896 0.15000 +v -1.36645 4.83195 0.15000 +v 0.62484 4.44254 -0.15000 +v 0.08200 2.24246 -0.15000 +v -0.66267 4.13256 -0.15000 +v -0.68346 4.13416 -0.15000 +v -1.17442 3.90972 -0.15000 +v -1.73376 1.52384 -0.15000 +v -1.93250 0.63448 -0.15000 +v -1.93250 0.59500 -0.15000 +v -1.93727 0.59500 -0.15000 +v -1.35518 0.83162 -0.15000 +v -0.85368 2.91242 -0.15000 +v -0.01619 0.89315 -0.15000 +v -0.01459 0.89280 -0.15000 +v 0.30960 1.06390 -0.15000 +v 0.84934 3.15848 -0.15000 +v 1.31823 4.70981 -0.15000 +v 1.31910 4.71849 -0.15000 +v 1.33632 4.71111 -0.15000 +v 0.58250 4.48000 -0.10000 +v 0.07250 2.38500 -0.10000 +v -0.62750 4.18000 -0.10000 +v -0.69250 4.18500 -0.10000 +v -1.21750 3.94500 -0.10000 +v -1.78250 1.53500 -0.10000 +v -1.98250 0.64000 -0.10000 +v -1.98250 0.54500 -0.10000 +v -1.92750 0.54500 -0.10000 +v -1.31250 0.79500 -0.10000 +v -0.84250 2.77000 -0.10000 +v -0.05250 0.85000 -0.10000 +v -0.00750 0.84000 -0.10000 +v 0.35250 1.03000 -0.10000 +v 0.89750 3.14500 -0.10000 +v 1.36750 4.70000 -0.10000 +v 1.37250 4.75000 -0.10000 +v 1.33750 4.76500 -0.10000 +v 0.58250 4.48000 0.10000 +v 0.07250 2.38500 0.10000 +v -0.62750 4.18000 0.10000 +v -0.69250 4.18500 0.10000 +v -1.21750 3.94500 0.10000 +v -1.78250 1.53500 0.10000 +v -1.98250 0.64000 0.10000 +v -1.98250 0.54500 0.10000 +v -1.92750 0.54500 0.10000 +v -1.31250 0.79500 0.10000 +v -0.84250 2.77000 0.10000 +v -0.05250 0.85000 0.10000 +v -0.00750 0.84000 0.10000 +v 0.35250 1.03000 0.10000 +v 0.89750 3.14500 0.10000 +v 1.36750 4.70000 0.10000 +v 1.37250 4.75000 0.10000 +v 1.33750 4.76500 0.10000 +v 0.62484 4.44254 0.15000 +v 0.08200 2.24246 0.15000 +v -0.66267 4.13256 0.15000 +v -0.68346 4.13416 0.15000 +v -1.17442 3.90972 0.15000 +v -1.73376 1.52384 0.15000 +v -1.93250 0.63448 0.15000 +v -1.93250 0.59500 0.15000 +v -1.93727 0.59500 0.15000 +v -1.35518 0.83162 0.15000 +v -0.85368 2.91242 0.15000 +v -0.01619 0.89315 0.15000 +v -0.01459 0.89280 0.15000 +v 0.30960 1.06390 0.15000 +v 0.84934 3.15848 0.15000 +v 1.31823 4.70981 0.15000 +v 1.31910 4.71849 0.15000 +v 1.33632 4.71111 0.15000 +v 2.32948 4.35264 -0.15000 +v 2.09340 4.23701 -0.15000 +v 1.88028 4.08201 -0.15000 +v 1.63082 3.82767 -0.15000 +v 1.39999 3.50843 -0.15000 +v 1.21315 3.15933 -0.15000 +v 1.06535 2.76520 -0.15000 +v 0.98691 2.44655 -0.15000 +v 0.94766 2.04423 -0.15000 +v 0.96704 1.75351 -0.15000 +v 1.04391 1.47967 -0.15000 +v 1.17620 1.25289 -0.15000 +v 1.36200 1.09961 -0.15000 +v 1.55884 1.03552 -0.15000 +v 1.77241 1.04945 -0.15000 +v 2.14299 1.17948 -0.15000 +v 1.82010 0.08165 -0.15000 +v 1.81705 0.09005 -0.15000 +v 2.36726 0.30304 -0.15000 +v 3.25393 3.04143 -0.15000 +v 3.26001 3.03687 -0.15000 +v 3.03824 2.99614 -0.15000 +v 2.09096 2.75324 -0.15000 +v 1.93513 2.30307 -0.15000 +v 1.93656 2.30248 -0.15000 +v 2.48640 2.43735 -0.15000 +v 2.53303 2.35964 -0.15000 +v 2.42093 2.07380 -0.15000 +v 2.19114 1.80745 -0.15000 +v 2.02124 1.69064 -0.15000 +v 1.88146 1.63473 -0.15000 +v 1.67419 1.64049 -0.15000 +v 1.54892 1.74190 -0.15000 +v 1.50454 1.81798 -0.15000 +v 1.45687 2.12523 -0.15000 +v 1.52393 2.53277 -0.15000 +v 1.65717 2.89661 -0.15000 +v 1.90751 3.31556 -0.15000 +v 2.17509 3.59342 -0.15000 +v 2.30863 3.68424 -0.15000 +v 2.46507 3.74897 -0.15000 +v 2.57762 3.76082 -0.15000 +v 2.69254 3.72453 -0.15000 +v 2.78598 3.64276 -0.15000 +v 2.85021 3.54000 -0.15000 +v 2.83360 3.54000 -0.15000 +v 3.26550 4.07519 -0.15000 +v 3.26412 4.08765 -0.15000 +v 3.19910 4.18735 -0.15000 +v 3.04034 4.33210 -0.15000 +v 2.86129 4.40555 -0.15000 +v 2.61195 4.41967 -0.15000 +v 2.31250 4.40000 -0.10000 +v 2.06750 4.28000 -0.10000 +v 1.84750 4.12000 -0.10000 +v 1.59250 3.86000 -0.10000 +v 1.35750 3.53500 -0.10000 +v 1.16750 3.18000 -0.10000 +v 1.01750 2.78000 -0.10000 +v 0.93750 2.45500 -0.10000 +v 0.89750 2.04500 -0.10000 +v 0.91750 1.74500 -0.10000 +v 0.99750 1.46000 -0.10000 +v 1.13750 1.22000 -0.10000 +v 1.33750 1.05500 -0.10000 +v 1.55250 0.98500 -0.10000 +v 1.78250 1.00000 -0.10000 +v 2.06750 1.10000 -0.10000 +v 1.76750 0.08000 -0.10000 +v 1.78750 0.02500 -0.10000 +v 2.40750 0.26500 -0.10000 +v 3.31250 3.06000 -0.10000 +v 3.27250 3.09000 -0.10000 +v 3.02750 3.04500 -0.10000 +v 2.05250 2.79500 -0.10000 +v 1.87250 2.27500 -0.10000 +v 1.93250 2.25000 -0.10000 +v 2.46250 2.38000 -0.10000 +v 2.47750 2.35500 -0.10000 +v 2.37750 2.10000 -0.10000 +v 2.15750 1.84500 -0.10000 +v 1.99750 1.73500 -0.10000 +v 1.87250 1.68500 -0.10000 +v 1.69250 1.69000 -0.10000 +v 1.58750 1.77500 -0.10000 +v 1.55250 1.83500 -0.10000 +v 1.50750 2.12500 -0.10000 +v 1.57250 2.52000 -0.10000 +v 1.70250 2.87500 -0.10000 +v 1.94750 3.28500 -0.10000 +v 2.20750 3.55500 -0.10000 +v 2.33250 3.64000 -0.10000 +v 2.47750 3.70000 -0.10000 +v 2.57250 3.71000 -0.10000 +v 2.66750 3.68000 -0.10000 +v 2.74750 3.61000 -0.10000 +v 2.82250 3.49000 -0.10000 +v 2.85750 3.49000 -0.10000 +v 3.31750 4.06000 -0.10000 +v 3.31250 4.10500 -0.10000 +v 3.23750 4.22000 -0.10000 +v 3.06750 4.37500 -0.10000 +v 2.87250 4.45500 -0.10000 +v 2.60750 4.47000 -0.10000 +v 2.31250 4.40000 0.10000 +v 2.06750 4.28000 0.10000 +v 1.84750 4.12000 0.10000 +v 1.59250 3.86000 0.10000 +v 1.35750 3.53500 0.10000 +v 1.16750 3.18000 0.10000 +v 1.01750 2.78000 0.10000 +v 0.93750 2.45500 0.10000 +v 0.89750 2.04500 0.10000 +v 0.91750 1.74500 0.10000 +v 0.99750 1.46000 0.10000 +v 1.13750 1.22000 0.10000 +v 1.33750 1.05500 0.10000 +v 1.55250 0.98500 0.10000 +v 1.78250 1.00000 0.10000 +v 2.06750 1.10000 0.10000 +v 1.76750 0.08000 0.10000 +v 1.78750 0.02500 0.10000 +v 2.40750 0.26500 0.10000 +v 3.31250 3.06000 0.10000 +v 3.27250 3.09000 0.10000 +v 3.02750 3.04500 0.10000 +v 2.05250 2.79500 0.10000 +v 1.87250 2.27500 0.10000 +v 1.93250 2.25000 0.10000 +v 2.46250 2.38000 0.10000 +v 2.47750 2.35500 0.10000 +v 2.37750 2.10000 0.10000 +v 2.15750 1.84500 0.10000 +v 1.99750 1.73500 0.10000 +v 1.87250 1.68500 0.10000 +v 1.69250 1.69000 0.10000 +v 1.58750 1.77500 0.10000 +v 1.55250 1.83500 0.10000 +v 1.50750 2.12500 0.10000 +v 1.57250 2.52000 0.10000 +v 1.70250 2.87500 0.10000 +v 1.94750 3.28500 0.10000 +v 2.20750 3.55500 0.10000 +v 2.33250 3.64000 0.10000 +v 2.47750 3.70000 0.10000 +v 2.57250 3.71000 0.10000 +v 2.66750 3.68000 0.10000 +v 2.74750 3.61000 0.10000 +v 2.82250 3.49000 0.10000 +v 2.85750 3.49000 0.10000 +v 3.31750 4.06000 0.10000 +v 3.31250 4.10500 0.10000 +v 3.23750 4.22000 0.10000 +v 3.06750 4.37500 0.10000 +v 2.87250 4.45500 0.10000 +v 2.60750 4.47000 0.10000 +v 2.32948 4.35264 0.15000 +v 2.09340 4.23701 0.15000 +v 1.88028 4.08201 0.15000 +v 1.63082 3.82767 0.15000 +v 1.39999 3.50843 0.15000 +v 1.21315 3.15933 0.15000 +v 1.06535 2.76520 0.15000 +v 0.98691 2.44655 0.15000 +v 0.94766 2.04423 0.15000 +v 0.96704 1.75351 0.15000 +v 1.04391 1.47967 0.15000 +v 1.17620 1.25289 0.15000 +v 1.36200 1.09961 0.15000 +v 1.55884 1.03552 0.15000 +v 1.77241 1.04945 0.15000 +v 2.14299 1.17948 0.15000 +v 1.82010 0.08165 0.15000 +v 1.81705 0.09005 0.15000 +v 2.36726 0.30304 0.15000 +v 3.25393 3.04143 0.15000 +v 3.26001 3.03687 0.15000 +v 3.03824 2.99614 0.15000 +v 2.09096 2.75324 0.15000 +v 1.93513 2.30307 0.15000 +v 1.93656 2.30248 0.15000 +v 2.48640 2.43735 0.15000 +v 2.53303 2.35964 0.15000 +v 2.42093 2.07380 0.15000 +v 2.19114 1.80745 0.15000 +v 2.02124 1.69064 0.15000 +v 1.88146 1.63473 0.15000 +v 1.67419 1.64049 0.15000 +v 1.54892 1.74190 0.15000 +v 1.50454 1.81798 0.15000 +v 1.45687 2.12523 0.15000 +v 1.52393 2.53277 0.15000 +v 1.65717 2.89661 0.15000 +v 1.90751 3.31556 0.15000 +v 2.17509 3.59342 0.15000 +v 2.30863 3.68424 0.15000 +v 2.46507 3.74897 0.15000 +v 2.57762 3.76082 0.15000 +v 2.69254 3.72453 0.15000 +v 2.78598 3.64276 0.15000 +v 2.85021 3.54000 0.15000 +v 2.83360 3.54000 0.15000 +v 3.26550 4.07519 0.15000 +v 3.26412 4.08765 0.15000 +v 3.19910 4.18735 0.15000 +v 3.04034 4.33210 0.15000 +v 2.86129 4.40555 0.15000 +v 2.61195 4.41967 0.15000 +v 4.42134 4.26192 -0.15000 +v 3.68779 4.02062 -0.15000 +v 3.51819 3.46274 -0.15000 +v 3.50554 3.46837 -0.15000 +v 3.69627 3.52831 -0.15000 +v 3.74725 3.46033 -0.15000 +v 3.10590 1.14743 -0.15000 +v 2.99540 0.69101 -0.15000 +v 2.98360 0.71460 -0.15000 +v 5.23877 1.36663 -0.15000 +v 5.32232 1.41040 -0.15000 +v 5.54853 2.01833 -0.15000 +v 5.55375 2.00657 -0.15000 +v 4.73027 1.80665 -0.15000 +v 3.85993 1.55798 -0.15000 +v 3.79723 1.63322 -0.15000 +v 4.02196 2.43048 -0.15000 +v 4.99579 2.70656 -0.15000 +v 5.19943 3.18789 -0.15000 +v 5.20030 3.18500 -0.15000 +v 5.19334 3.18500 -0.15000 +v 4.16568 2.94161 -0.15000 +v 4.38211 3.69034 -0.15000 +v 5.66044 4.06131 -0.15000 +v 5.88838 4.61489 -0.15000 +v 5.88417 4.62500 -0.15000 +v 5.88859 4.62500 -0.15000 +v 4.40750 4.31000 -0.10000 +v 3.64750 4.06000 -0.10000 +v 3.45750 3.43500 -0.10000 +v 3.50250 3.41500 -0.10000 +v 3.67750 3.47000 -0.10000 +v 3.69250 3.45000 -0.10000 +v 3.05750 1.16000 -0.10000 +v 2.94250 0.68500 -0.10000 +v 2.95750 0.65500 -0.10000 +v 5.25750 1.32000 -0.10000 +v 5.36250 1.37500 -0.10000 +v 5.60250 2.02000 -0.10000 +v 5.58250 2.06500 -0.10000 +v 4.71750 1.85500 -0.10000 +v 3.87750 1.61500 -0.10000 +v 3.85250 1.64500 -0.10000 +v 4.06250 2.39000 -0.10000 +v 5.03250 2.66500 -0.10000 +v 5.25250 3.18500 -0.10000 +v 5.23750 3.23500 -0.10000 +v 5.18750 3.23500 -0.10000 +v 4.23750 3.01000 -0.10000 +v 4.42250 3.65000 -0.10000 +v 5.69750 4.02000 -0.10000 +v 5.94250 4.61500 -0.10000 +v 5.91750 4.67500 -0.10000 +v 5.88250 4.67500 -0.10000 +v 4.40750 4.31000 0.10000 +v 3.64750 4.06000 0.10000 +v 3.45750 3.43500 0.10000 +v 3.50250 3.41500 0.10000 +v 3.67750 3.47000 0.10000 +v 3.69250 3.45000 0.10000 +v 3.05750 1.16000 0.10000 +v 2.94250 0.68500 0.10000 +v 2.95750 0.65500 0.10000 +v 5.25750 1.32000 0.10000 +v 5.36250 1.37500 0.10000 +v 5.60250 2.02000 0.10000 +v 5.58250 2.06500 0.10000 +v 4.71750 1.85500 0.10000 +v 3.87750 1.61500 0.10000 +v 3.85250 1.64500 0.10000 +v 4.06250 2.39000 0.10000 +v 5.03250 2.66500 0.10000 +v 5.25250 3.18500 0.10000 +v 5.23750 3.23500 0.10000 +v 5.18750 3.23500 0.10000 +v 4.23750 3.01000 0.10000 +v 4.42250 3.65000 0.10000 +v 5.69750 4.02000 0.10000 +v 5.94250 4.61500 0.10000 +v 5.91750 4.67500 0.10000 +v 5.88250 4.67500 0.10000 +v 4.42134 4.26192 0.15000 +v 3.68779 4.02062 0.15000 +v 3.51819 3.46274 0.15000 +v 3.50554 3.46837 0.15000 +v 3.69627 3.52831 0.15000 +v 3.74725 3.46033 0.15000 +v 3.10590 1.14743 0.15000 +v 2.99540 0.69101 0.15000 +v 2.98360 0.71460 0.15000 +v 5.23877 1.36663 0.15000 +v 5.32232 1.41040 0.15000 +v 5.54853 2.01833 0.15000 +v 5.55375 2.00657 0.15000 +v 4.73027 1.80665 0.15000 +v 3.85993 1.55798 0.15000 +v 3.79723 1.63322 0.15000 +v 4.02196 2.43048 0.15000 +v 4.99579 2.70656 0.15000 +v 5.19943 3.18789 0.15000 +v 5.20030 3.18500 0.15000 +v 5.19334 3.18500 0.15000 +v 4.16568 2.94161 0.15000 +v 4.38211 3.69034 0.15000 +v 5.66044 4.06131 0.15000 +v 5.88838 4.61489 0.15000 +v 5.88417 4.62500 0.15000 +v 5.88859 4.62500 0.15000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 0.0000 -1.0000 +vn -0.3890 0.5905 -0.7071 +vn -0.4478 0.5473 -0.7071 +vn -0.5000 0.5000 -0.7071 +vn -0.5478 0.4472 -0.7071 +vn -0.6957 0.1265 -0.7071 +vn -0.4778 -0.5212 -0.7071 +vn -0.3850 -0.5931 -0.7071 +vn -0.4369 -0.5560 -0.7071 +vn -0.4147 0.5727 -0.7071 +vn -0.3880 0.5912 -0.7071 +vn -0.6975 0.1163 -0.7071 +vn -0.6776 -0.2023 -0.7071 +vn -0.6445 -0.2910 -0.7071 +vn 0.1715 -0.6860 -0.7071 +vn 0.4511 -0.5445 -0.7071 +vn 0.4309 -0.5606 -0.7071 +vn 0.4752 -0.5237 -0.7071 +vn 0.5347 -0.4627 -0.7071 +vn 0.7028 0.0781 -0.7071 +vn 0.5000 0.5000 -0.7071 +vn 0.4341 0.5582 -0.7071 +vn 0.3692 0.6031 -0.7071 +vn 0.4712 -0.5273 -0.7071 +vn 0.3938 -0.5873 -0.7071 +vn 0.3288 -0.6260 -0.7071 +vn 0.6780 0.2009 -0.7071 +vn 0.6544 0.2677 -0.7071 +vn 0.2483 0.6621 -0.7071 +vn -0.3104 0.6353 -0.7071 +vn -0.5501 0.8351 0.0000 +vn -0.6332 0.7740 0.0000 +vn -0.7071 0.7071 0.0000 +vn -0.7747 0.6324 0.0000 +vn -0.9839 0.1789 0.0000 +vn -0.6757 -0.7372 0.0000 +vn -0.5444 -0.8388 0.0000 +vn -0.6178 -0.7863 0.0000 +vn -0.5865 0.8099 0.0000 +vn -0.5487 0.8360 0.0000 +vn -0.9864 0.1644 0.0000 +vn -0.9582 -0.2860 0.0000 +vn -0.9114 -0.4116 0.0000 +vn 0.2425 -0.9701 0.0000 +vn 0.6380 -0.7700 0.0000 +vn 0.6094 -0.7929 0.0000 +vn 0.6720 -0.7406 0.0000 +vn 0.7562 -0.6544 0.0000 +vn 0.9939 0.1104 0.0000 +vn 0.7071 0.7071 0.0000 +vn 0.6139 0.7894 0.0000 +vn 0.5221 0.8529 0.0000 +vn 0.6663 -0.7457 0.0000 +vn 0.5570 -0.8305 0.0000 +vn 0.4650 -0.8853 0.0000 +vn 0.9588 0.2841 0.0000 +vn 0.9255 0.3786 0.0000 +vn 0.3511 0.9363 0.0000 +vn -0.4390 0.8985 0.0000 +vn -0.3890 0.5905 0.7071 +vn -0.4478 0.5473 0.7071 +vn -0.5000 0.5000 0.7071 +vn -0.5478 0.4472 0.7071 +vn -0.6957 0.1265 0.7071 +vn -0.4778 -0.5212 0.7071 +vn -0.3850 -0.5931 0.7071 +vn -0.4369 -0.5560 0.7071 +vn -0.4147 0.5727 0.7071 +vn -0.3880 0.5912 0.7071 +vn -0.6975 0.1163 0.7071 +vn -0.6776 -0.2023 0.7071 +vn -0.6445 -0.2910 0.7071 +vn 0.1715 -0.6860 0.7071 +vn 0.4511 -0.5445 0.7071 +vn 0.4309 -0.5606 0.7071 +vn 0.4752 -0.5237 0.7071 +vn 0.5347 -0.4627 0.7071 +vn 0.7028 0.0781 0.7071 +vn 0.5000 0.5000 0.7071 +vn 0.4341 0.5582 0.7071 +vn 0.3692 0.6031 0.7071 +vn 0.4712 -0.5273 0.7071 +vn 0.3938 -0.5873 0.7071 +vn 0.3288 -0.6260 0.7071 +vn 0.6780 0.2009 0.7071 +vn 0.6544 0.2677 0.7071 +vn 0.2483 0.6621 0.7071 +vn -0.3104 0.6353 0.7071 +vn -0.4527 0.5432 -0.7071 +vn -0.6897 0.1562 -0.7071 +vn -0.6950 0.1303 -0.7071 +vn -0.6462 -0.2872 -0.7071 +vn 0.2515 -0.6609 -0.7071 +vn 0.3317 -0.6245 -0.7071 +vn 0.6886 -0.1606 -0.7071 +vn 0.6848 -0.1762 -0.7071 +vn 0.1158 0.6976 -0.7070 +vn -0.2473 0.6625 -0.7071 +vn -0.6402 0.7682 0.0000 +vn -0.9753 0.2209 0.0000 +vn -0.9829 0.1843 0.0000 +vn -0.9138 -0.4061 0.0000 +vn 0.3557 -0.9346 0.0000 +vn 0.4692 -0.8831 0.0000 +vn 0.9739 -0.2271 0.0000 +vn 0.9684 -0.2492 0.0000 +vn 0.1644 0.9864 0.0000 +vn -0.3497 0.9369 0.0000 +vn -0.4527 0.5432 0.7071 +vn -0.6897 0.1562 0.7071 +vn -0.6950 0.1303 0.7071 +vn -0.6462 -0.2872 0.7071 +vn 0.2515 -0.6609 0.7071 +vn 0.3317 -0.6245 0.7071 +vn 0.6886 -0.1606 0.7071 +vn 0.6848 -0.1762 0.7071 +vn 0.1158 0.6976 0.7070 +vn -0.2473 0.6625 0.7071 +vn -0.5433 0.4526 -0.7071 +vn -0.6883 0.1622 -0.7071 +vn -0.2481 -0.6622 -0.7071 +vn 0.2336 -0.6674 -0.7071 +vn 0.2672 -0.6547 -0.7071 +vn 0.6674 -0.2336 -0.7071 +vn 0.7071 0.0000 -0.7071 +vn 0.2240 0.6706 -0.7071 +vn -0.2499 0.6615 -0.7071 +vn -0.7682 0.6402 0.0000 +vn -0.9733 0.2294 0.0000 +vn -0.3511 -0.9363 0.0000 +vn 0.3304 -0.9439 0.0000 +vn 0.3779 -0.9258 0.0000 +vn 0.9439 -0.3304 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.3162 0.9487 0.0000 +vn -0.3534 0.9355 0.0000 +vn -0.5433 0.4526 0.7071 +vn -0.6883 0.1622 0.7071 +vn -0.2481 -0.6622 0.7071 +vn 0.2336 -0.6674 0.7071 +vn 0.2672 -0.6547 0.7071 +vn 0.6674 -0.2336 0.7071 +vn 0.7071 0.0000 0.7071 +vn 0.2240 0.6706 0.7071 +vn -0.2499 0.6615 0.7071 +vn -0.7105 0.1742 -0.6818 +vn 0.6809 0.2669 -0.6819 +vn 0.0542 0.7050 -0.7071 +vn -0.2940 0.6431 -0.7071 +vn -0.6885 0.1614 -0.7071 +vn -0.6901 0.1542 -0.7071 +vn -0.7071 0.0000 -0.7071 +vn 0.0000 -0.7071 -0.7071 +vn 0.2663 -0.6551 -0.7071 +vn 0.7083 -0.1697 -0.6852 +vn -0.6731 -0.2781 -0.6852 +vn -0.1533 -0.6903 -0.7071 +vn 0.3300 -0.6253 -0.7071 +vn 0.6847 -0.1764 -0.7071 +vn 0.6769 -0.2046 -0.7071 +vn 0.7036 -0.0704 -0.7071 +vn 0.2785 0.6499 -0.7071 +vn -0.2497 0.6615 -0.7071 +vn -0.9716 0.2365 0.0000 +vn 0.9317 0.3633 0.0000 +vn 0.0767 0.9971 0.0000 +vn -0.4158 0.9095 0.0000 +vn -0.9736 0.2283 0.0000 +vn -0.9759 0.2181 0.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.3766 -0.9264 0.0000 +vn 0.9728 -0.2315 0.0000 +vn -0.9248 -0.3805 0.0000 +vn -0.2169 -0.9762 0.0000 +vn 0.4668 -0.8844 0.0000 +vn 0.9684 -0.2495 0.0000 +vn 0.9572 -0.2893 0.0000 +vn 0.9950 -0.0995 0.0000 +vn 0.3939 0.9191 0.0000 +vn -0.3532 0.9356 0.0000 +vn -0.7105 0.1742 0.6818 +vn 0.6809 0.2669 0.6819 +vn 0.0542 0.7050 0.7071 +vn -0.2940 0.6431 0.7071 +vn -0.6885 0.1614 0.7071 +vn -0.6901 0.1542 0.7071 +vn -0.7071 0.0000 0.7071 +vn 0.0000 -0.7071 0.7071 +vn 0.2663 -0.6551 0.7071 +vn 0.7083 -0.1697 0.6852 +vn -0.6731 -0.2781 0.6852 +vn -0.1533 -0.6903 0.7071 +vn 0.3300 -0.6253 0.7071 +vn 0.6847 -0.1764 0.7071 +vn 0.6769 -0.2046 0.7071 +vn 0.7036 -0.0704 0.7071 +vn 0.2785 0.6499 0.7071 +vn -0.2497 0.6615 0.7071 +vn -0.3110 0.6350 -0.7071 +vn -0.4159 0.5718 -0.7071 +vn -0.5048 0.4951 -0.7071 +vn -0.5730 0.4143 -0.7071 +vn -0.6234 0.3337 -0.7071 +vn -0.6621 0.2483 -0.7071 +vn -0.6866 0.1690 -0.7071 +vn -0.7038 0.0687 -0.7071 +vn -0.7056 -0.0470 -0.7071 +vn -0.6808 -0.1911 -0.7071 +vn -0.6108 -0.3563 -0.7071 +vn -0.4500 -0.5454 -0.7071 +vn -0.2189 -0.6724 -0.7071 +vn 0.0460 -0.7056 -0.7071 +vn 0.2341 -0.6672 -0.7071 +vn -0.6784 0.1995 -0.7071 +vn -0.6645 -0.2417 -0.7071 +vn 0.2553 -0.6594 -0.7071 +vn 0.6727 -0.2178 -0.7071 +vn 0.4243 0.5657 -0.7071 +vn -0.1277 0.6955 -0.7071 +vn -0.1756 0.6849 -0.7071 +vn -0.6682 0.2313 -0.7071 +vn -0.2719 -0.6527 -0.7071 +vn 0.1684 -0.6867 -0.7071 +vn -0.6063 -0.3638 -0.7071 +vn -0.6583 0.2582 -0.7071 +vn -0.5354 0.4619 -0.7071 +vn -0.4006 0.5827 -0.7071 +vn -0.2626 0.6565 -0.7071 +vn 0.0196 0.7068 -0.7071 +vn 0.4449 0.5496 -0.7071 +vn 0.6108 0.3563 -0.7071 +vn 0.6988 0.1084 -0.7071 +vn 0.6977 -0.1148 -0.7071 +vn 0.6640 -0.2432 -0.7071 +vn 0.6070 -0.3627 -0.7071 +vn 0.5093 -0.4905 -0.7071 +vn 0.3976 -0.5847 -0.7071 +vn 0.2703 -0.6534 -0.7071 +vn 0.0740 -0.7032 -0.7071 +vn -0.2129 -0.6743 -0.7071 +vn -0.4657 -0.5322 -0.7071 +vn -0.5996 -0.3748 -0.7071 +vn 0.5503 -0.4441 -0.7071 +vn 0.7028 0.0780 -0.7071 +vn 0.5923 0.3863 -0.7071 +vn 0.4764 0.5225 -0.7071 +vn 0.2684 0.6542 -0.7071 +vn 0.0400 0.7060 -0.7071 +vn -0.1633 0.6880 -0.7071 +vn -0.4399 0.8981 0.0000 +vn -0.5882 0.8087 0.0000 +vn -0.7139 0.7002 0.0000 +vn -0.8104 0.5859 0.0000 +vn -0.8817 0.4719 0.0000 +vn -0.9363 0.3511 0.0000 +vn -0.9710 0.2390 0.0000 +vn -0.9953 0.0971 0.0000 +vn -0.9978 -0.0665 0.0000 +vn -0.9628 -0.2703 0.0000 +vn -0.8638 -0.5039 0.0000 +vn -0.6364 -0.7714 0.0000 +vn -0.3096 -0.9509 0.0000 +vn 0.0651 -0.9979 0.0000 +vn 0.3311 -0.9436 0.0000 +vn -0.9594 0.2822 0.0000 +vn -0.9398 -0.3417 0.0000 +vn 0.3610 -0.9326 0.0000 +vn 0.9514 -0.3080 0.0000 +vn 0.6000 0.8000 0.0000 +vn -0.1807 0.9835 0.0000 +vn -0.2484 0.9687 0.0000 +vn -0.9450 0.3271 0.0000 +vn -0.3846 -0.9231 0.0000 +vn 0.2382 -0.9712 0.0000 +vn -0.8575 -0.5145 0.0000 +vn -0.9310 0.3651 0.0000 +vn -0.7572 0.6532 0.0000 +vn -0.5665 0.8240 0.0000 +vn -0.3714 0.9285 0.0000 +vn 0.0278 0.9996 0.0000 +vn 0.6292 0.7772 0.0000 +vn 0.8638 0.5039 0.0000 +vn 0.9882 0.1533 0.0000 +vn 0.9867 -0.1624 0.0000 +vn 0.9390 -0.3439 0.0000 +vn 0.8584 -0.5130 0.0000 +vn 0.7203 -0.6936 0.0000 +vn 0.5623 -0.8269 0.0000 +vn 0.3824 -0.9240 0.0000 +vn 0.1047 -0.9945 0.0000 +vn -0.3011 -0.9536 0.0000 +vn -0.6585 -0.7526 0.0000 +vn -0.8480 -0.5300 0.0000 +vn 0.7782 -0.6280 0.0000 +vn 0.8376 0.5463 0.0000 +vn 0.6738 0.7390 0.0000 +vn 0.3796 0.9252 0.0000 +vn 0.0565 0.9984 0.0000 +vn -0.2309 0.9730 0.0000 +vn -0.3110 0.6350 0.7071 +vn -0.4159 0.5718 0.7071 +vn -0.5048 0.4951 0.7071 +vn -0.5730 0.4143 0.7071 +vn -0.6234 0.3337 0.7071 +vn -0.6621 0.2483 0.7071 +vn -0.6866 0.1690 0.7071 +vn -0.7038 0.0687 0.7071 +vn -0.7056 -0.0470 0.7071 +vn -0.6808 -0.1911 0.7071 +vn -0.6108 -0.3563 0.7071 +vn -0.4500 -0.5454 0.7071 +vn -0.2189 -0.6724 0.7071 +vn 0.0460 -0.7056 0.7071 +vn 0.2341 -0.6672 0.7071 +vn -0.6784 0.1995 0.7071 +vn -0.6645 -0.2417 0.7071 +vn 0.2553 -0.6594 0.7071 +vn 0.6727 -0.2178 0.7071 +vn 0.4243 0.5657 0.7071 +vn -0.1277 0.6955 0.7071 +vn -0.1756 0.6849 0.7071 +vn -0.6682 0.2313 0.7071 +vn -0.2719 -0.6527 0.7071 +vn 0.1684 -0.6867 0.7071 +vn -0.6063 -0.3638 0.7071 +vn -0.6583 0.2582 0.7071 +vn -0.5354 0.4619 0.7071 +vn -0.4006 0.5827 0.7071 +vn -0.2626 0.6565 0.7071 +vn 0.0196 0.7068 0.7071 +vn 0.4449 0.5496 0.7071 +vn 0.6108 0.3563 0.7071 +vn 0.6988 0.1084 0.7071 +vn 0.6977 -0.1148 0.7071 +vn 0.6640 -0.2432 0.7071 +vn 0.6070 -0.3627 0.7071 +vn 0.5093 -0.4905 0.7071 +vn 0.3976 -0.5847 0.7071 +vn 0.2703 -0.6534 0.7071 +vn 0.0740 -0.7032 0.7071 +vn -0.2129 -0.6743 0.7071 +vn -0.4657 -0.5322 0.7071 +vn -0.5996 -0.3748 0.7071 +vn 0.5503 -0.4441 0.7071 +vn 0.7028 0.0780 0.7071 +vn 0.5923 0.3863 0.7071 +vn 0.4764 0.5225 0.7071 +vn 0.2684 0.6542 0.7071 +vn 0.0400 0.7060 0.7071 +vn -0.1633 0.6880 0.7071 +vn -0.2210 0.6717 -0.7071 +vn -0.6765 0.2057 -0.7071 +vn -0.2871 -0.6462 -0.7071 +vn 0.2120 -0.6746 -0.7071 +vn -0.5657 -0.4242 -0.7071 +vn -0.6814 0.1889 -0.7071 +vn -0.6873 0.1664 -0.7071 +vn -0.6327 -0.3159 -0.7071 +vn 0.1964 -0.6793 -0.7071 +vn 0.3281 -0.6264 -0.7071 +vn 0.6627 -0.2466 -0.7071 +vn 0.6461 0.2873 -0.7071 +vn -0.1668 0.6872 -0.7071 +vn -0.1943 0.6799 -0.7071 +vn 0.5432 0.4527 -0.7071 +vn 0.6806 -0.1918 -0.7071 +vn 0.1929 -0.6803 -0.7071 +vn 0.6512 -0.2755 -0.7071 +vn 0.6773 0.2031 -0.7071 +vn 0.0000 0.7071 -0.7071 +vn -0.1630 0.6881 -0.7071 +vn 0.6793 -0.1964 -0.7071 +vn 0.1971 -0.6791 -0.7071 +vn 0.6539 -0.2692 -0.7071 +vn 0.6527 0.2720 -0.7071 +vn -0.1699 0.6864 -0.7071 +vn -0.3125 0.9499 0.0000 +vn -0.9568 0.2909 0.0000 +vn -0.4061 -0.9138 0.0000 +vn 0.2998 -0.9540 0.0000 +vn -0.8000 -0.6000 0.0000 +vn -0.9636 0.2672 0.0000 +vn -0.9719 0.2353 0.0000 +vn -0.8944 -0.4472 0.0000 +vn 0.2778 -0.9607 0.0000 +vn 0.4640 -0.8858 0.0000 +vn 0.9372 -0.3487 0.0000 +vn 0.9138 0.4061 0.0000 +vn -0.2359 0.9718 0.0000 +vn -0.2747 0.9615 0.0000 +vn 0.7682 0.6402 0.0000 +vn 0.9625 -0.2713 0.0000 +vn 0.2728 -0.9621 0.0000 +vn 0.9210 -0.3896 0.0000 +vn 0.9578 0.2873 0.0000 +vn 0.0000 1.0000 0.0000 +vn -0.2305 0.9731 0.0000 +vn 0.9607 -0.2777 0.0000 +vn 0.2787 -0.9604 0.0000 +vn 0.9247 -0.3807 0.0000 +vn 0.9231 0.3846 0.0000 +vn -0.2402 0.9707 0.0000 +vn -0.2210 0.6717 0.7071 +vn -0.6765 0.2057 0.7071 +vn -0.2871 -0.6462 0.7071 +vn 0.2120 -0.6746 0.7071 +vn -0.5657 -0.4242 0.7071 +vn -0.6814 0.1889 0.7071 +vn -0.6873 0.1664 0.7071 +vn -0.6327 -0.3159 0.7071 +vn 0.1964 -0.6793 0.7071 +vn 0.3281 -0.6264 0.7071 +vn 0.6627 -0.2466 0.7071 +vn 0.6461 0.2873 0.7071 +vn -0.1668 0.6872 0.7071 +vn -0.1943 0.6799 0.7071 +vn 0.5432 0.4527 0.7071 +vn 0.6806 -0.1918 0.7071 +vn 0.1929 -0.6803 0.7071 +vn 0.6512 -0.2755 0.7071 +vn 0.6773 0.2031 0.7071 +vn 0.0000 0.7071 0.7071 +vn -0.1630 0.6881 0.7071 +vn 0.6793 -0.1964 0.7071 +vn 0.1971 -0.6791 0.7071 +vn 0.6539 -0.2692 0.7071 +vn 0.6527 0.2720 0.7071 +vn -0.1699 0.6864 0.7071 +o letter01 +usemtl logo +f 1//1 29//1 28//1 +f 115//2 116//2 88//2 +f 1//2 28//2 27//2 +f 114//1 115//1 88//1 +f 1//2 27//2 26//2 +f 113//1 114//1 88//1 +f 1//2 26//2 25//2 +f 112//1 113//1 88//1 +f 1//2 25//2 24//2 +f 111//1 112//1 88//1 +f 1//2 24//2 23//2 +f 110//1 111//1 88//1 +f 23//2 22//2 21//2 +f 108//1 109//1 110//1 +f 23//2 21//2 20//2 +f 107//1 108//1 110//1 +f 23//2 20//2 19//2 +f 106//1 107//1 110//1 +f 23//2 19//2 18//2 +f 105//1 106//1 110//1 +f 23//2 18//2 17//2 +f 104//1 105//1 110//1 +f 16//2 15//2 14//2 +f 101//1 102//1 103//1 +f 16//2 14//2 13//2 +f 100//1 101//1 103//1 +f 17//2 16//2 13//2 +f 100//1 103//1 104//1 +f 17//2 13//2 12//2 +f 99//1 100//1 104//1 +f 17//2 12//2 11//2 +f 98//1 99//1 104//1 +f 17//2 11//2 10//2 +f 97//1 98//1 104//1 +f 17//2 10//2 9//2 +f 96//1 97//1 104//1 +f 23//2 17//2 9//2 +f 96//1 104//1 110//1 +f 23//2 9//2 8//2 +f 95//1 96//1 110//1 +f 23//2 8//2 7//2 +f 94//1 95//1 110//1 +f 1//2 23//2 7//2 +f 94//1 110//1 88//1 +f 1//2 7//2 6//2 +f 93//1 94//1 88//1 +f 1//2 6//2 5//2 +f 92//1 93//1 88//1 +f 1//2 5//2 4//2 +f 91//1 92//1 88//1 +f 1//2 4//2 3//2 +f 90//1 91//1 88//1 +f 3//2 2//2 1//2 +f 88//1 89//1 90//1 +f 1//3 2//3 31//3 30//3 +f 2//4 3//4 32//4 31//4 +f 3//5 4//5 33//5 32//5 +f 4//6 5//6 34//6 33//6 +f 5//7 6//7 35//7 34//7 +f 6//8 7//8 36//8 35//8 +f 7//9 8//9 37//9 36//9 +f 8//10 9//10 38//10 37//10 +f 9//11 10//11 39//11 38//11 +f 10//12 11//12 40//12 39//12 +f 11//13 12//13 41//13 40//13 +f 12//14 13//14 42//14 41//14 +f 13//15 14//15 43//15 42//15 +f 14//16 15//16 44//16 43//16 +f 15//17 16//17 45//17 44//17 +f 16//18 17//18 46//18 45//18 +f 17//19 18//19 47//19 46//19 +f 18//20 19//20 48//20 47//20 +f 19//21 20//21 49//21 48//21 +f 20//22 21//22 50//22 49//22 +f 21//23 22//23 51//23 50//23 +f 22//24 23//24 52//24 51//24 +f 23//25 24//25 53//25 52//25 +f 24//26 25//26 54//26 53//26 +f 25//27 26//27 55//27 54//27 +f 26//28 27//28 56//28 55//28 +f 27//29 28//29 57//29 56//29 +f 28//30 29//30 58//30 57//30 +f 29//31 1//31 30//31 58//31 +f 30//32 31//32 60//32 59//32 +f 31//33 32//33 61//33 60//33 +f 32//34 33//34 62//34 61//34 +f 33//35 34//35 63//35 62//35 +f 34//36 35//36 64//36 63//36 +f 35//37 36//37 65//37 64//37 +f 36//38 37//38 66//38 65//38 +f 37//39 38//39 67//39 66//39 +f 38//40 39//40 68//40 67//40 +f 39//41 40//41 69//41 68//41 +f 40//42 41//42 70//42 69//42 +f 41//43 42//43 71//43 70//43 +f 42//44 43//44 72//44 71//44 +f 43//45 44//45 73//45 72//45 +f 44//46 45//46 74//46 73//46 +f 45//47 46//47 75//47 74//47 +f 46//48 47//48 76//48 75//48 +f 47//49 48//49 77//49 76//49 +f 48//50 49//50 78//50 77//50 +f 49//51 50//51 79//51 78//51 +f 50//52 51//52 80//52 79//52 +f 51//53 52//53 81//53 80//53 +f 52//54 53//54 82//54 81//54 +f 53//55 54//55 83//55 82//55 +f 54//56 55//56 84//56 83//56 +f 55//57 56//57 85//57 84//57 +f 56//58 57//58 86//58 85//58 +f 57//59 58//59 87//59 86//59 +f 58//60 30//60 59//60 87//60 +f 59//61 60//61 89//61 88//61 +f 60//62 61//62 90//62 89//62 +f 61//63 62//63 91//63 90//63 +f 62//64 63//64 92//64 91//64 +f 63//65 64//65 93//65 92//65 +f 64//66 65//66 94//66 93//66 +f 65//67 66//67 95//67 94//67 +f 66//68 67//68 96//68 95//68 +f 67//69 68//69 97//69 96//69 +f 68//70 69//70 98//70 97//70 +f 69//71 70//71 99//71 98//71 +f 70//72 71//72 100//72 99//72 +f 71//73 72//73 101//73 100//73 +f 72//74 73//74 102//74 101//74 +f 73//75 74//75 103//75 102//75 +f 74//76 75//76 104//76 103//76 +f 75//77 76//77 105//77 104//77 +f 76//78 77//78 106//78 105//78 +f 77//79 78//79 107//79 106//79 +f 78//80 79//80 108//80 107//80 +f 79//81 80//81 109//81 108//81 +f 80//82 81//82 110//82 109//82 +f 81//83 82//83 111//83 110//83 +f 82//84 83//84 112//84 111//84 +f 83//85 84//85 113//85 112//85 +f 84//86 85//86 114//86 113//86 +f 85//87 86//87 115//87 114//87 +f 86//88 87//88 116//88 115//88 +f 87//89 59//89 88//89 116//89 +o letter02 +usemtl logo +f 117//1 127//1 126//1 +f 159//2 160//2 150//2 +f 117//1 126//1 125//1 +f 158//2 159//2 150//2 +f 117//2 125//2 124//2 +f 157//1 158//1 150//1 +f 117//2 124//2 123//2 +f 156//1 157//1 150//1 +f 117//2 123//2 122//2 +f 155//1 156//1 150//1 +f 117//2 122//2 121//2 +f 154//1 155//1 150//1 +f 117//1 121//1 120//1 +f 153//2 154//2 150//2 +f 117//2 120//2 119//2 +f 152//1 153//1 150//1 +f 119//2 118//2 117//2 +f 150//1 151//1 152//1 +f 117//90 118//90 129//90 128//90 +f 118//91 119//91 130//91 129//91 +f 119//92 120//92 131//92 130//92 +f 120//93 121//93 132//93 131//93 +f 121//94 122//94 133//94 132//94 +f 122//95 123//95 134//95 133//95 +f 123//96 124//96 135//96 134//96 +f 124//97 125//97 136//97 135//97 +f 125//21 126//21 137//21 136//21 +f 126//98 127//98 138//98 137//98 +f 127//99 117//99 128//99 138//99 +f 128//100 129//100 140//100 139//100 +f 129//101 130//101 141//101 140//101 +f 130//102 131//102 142//102 141//102 +f 131//103 132//103 143//103 142//103 +f 132//104 133//104 144//104 143//104 +f 133//105 134//105 145//105 144//105 +f 134//106 135//106 146//106 145//106 +f 135//107 136//107 147//107 146//107 +f 136//50 137//50 148//50 147//50 +f 137//108 138//108 149//108 148//108 +f 138//109 128//109 139//109 149//109 +f 139//110 140//110 151//110 150//110 +f 140//111 141//111 152//111 151//111 +f 141//112 142//112 153//112 152//112 +f 142//113 143//113 154//113 153//113 +f 143//114 144//114 155//114 154//114 +f 144//115 145//115 156//115 155//115 +f 145//116 146//116 157//116 156//116 +f 146//117 147//117 158//117 157//117 +f 147//79 148//79 159//79 158//79 +f 148//118 149//118 160//118 159//118 +f 149//119 139//119 150//119 160//119 +o letter03 +usemtl logo +f 161//1 169//1 168//1 +f 195//2 196//2 188//2 +f 161//1 168//1 167//1 +f 194//2 195//2 188//2 +f 161//2 167//2 166//2 +f 193//1 194//1 188//1 +f 161//2 166//2 165//2 +f 192//1 193//1 188//1 +f 161//2 165//2 164//2 +f 191//1 192//1 188//1 +f 161//1 164//1 163//1 +f 190//2 191//2 188//2 +f 163//2 162//2 161//2 +f 188//1 189//1 190//1 +f 161//120 162//120 171//120 170//120 +f 162//121 163//121 172//121 171//121 +f 163//122 164//122 173//122 172//122 +f 164//123 165//123 174//123 173//123 +f 165//124 166//124 175//124 174//124 +f 166//125 167//125 176//125 175//125 +f 167//126 168//126 177//126 176//126 +f 168//127 169//127 178//127 177//127 +f 169//128 161//128 170//128 178//128 +f 170//129 171//129 180//129 179//129 +f 171//130 172//130 181//130 180//130 +f 172//131 173//131 182//131 181//131 +f 173//132 174//132 183//132 182//132 +f 174//133 175//133 184//133 183//133 +f 175//134 176//134 185//134 184//134 +f 176//135 177//135 186//135 185//135 +f 177//136 178//136 187//136 186//136 +f 178//137 170//137 179//137 187//137 +f 179//138 180//138 189//138 188//138 +f 180//139 181//139 190//139 189//139 +f 181//140 182//140 191//140 190//140 +f 182//141 183//141 192//141 191//141 +f 183//142 184//142 193//142 192//142 +f 184//143 185//143 194//143 193//143 +f 185//144 186//144 195//144 194//144 +f 186//145 187//145 196//145 195//145 +f 187//146 179//146 188//146 196//146 +o letter04 +usemtl logo +f 197//1 214//1 213//1 +f 267//2 268//2 251//2 +f 197//2 213//2 212//2 +f 266//1 267//1 251//1 +f 197//2 212//2 211//2 +f 265//1 266//1 251//1 +f 197//2 211//2 210//2 +f 264//1 265//1 251//1 +f 197//2 210//2 209//2 +f 263//1 264//1 251//1 +f 197//2 209//2 208//2 +f 262//1 263//1 251//1 +f 207//2 206//2 205//2 +f 259//1 260//1 261//1 +f 207//1 205//1 204//1 +f 258//2 259//2 261//2 +f 207//2 204//2 203//2 +f 257//1 258//1 261//1 +f 207//2 203//2 202//2 +f 256//1 257//1 261//1 +f 207//2 202//2 201//2 +f 255//1 256//1 261//1 +f 208//2 207//2 201//2 +f 255//1 261//1 262//1 +f 208//2 201//2 200//2 +f 254//1 255//1 262//1 +f 208//2 200//2 199//2 +f 253//1 254//1 262//1 +f 208//2 199//2 198//2 +f 252//1 253//1 262//1 +f 208//2 198//2 197//2 +f 251//1 252//1 262//1 +f 197//147 198//147 216//147 215//147 +f 198//148 199//148 217//148 216//148 +f 199//149 200//149 218//149 217//149 +f 200//150 201//150 219//150 218//150 +f 201//151 202//151 220//151 219//151 +f 202//152 203//152 221//152 220//152 +f 203//153 204//153 222//153 221//153 +f 204//154 205//154 223//154 222//154 +f 205//155 206//155 224//155 223//155 +f 206//156 207//156 225//156 224//156 +f 207//157 208//157 226//157 225//157 +f 208//158 209//158 227//158 226//158 +f 209//159 210//159 228//159 227//159 +f 210//160 211//160 229//160 228//160 +f 211//161 212//161 230//161 229//161 +f 212//162 213//162 231//162 230//162 +f 213//163 214//163 232//163 231//163 +f 214//164 197//164 215//164 232//164 +f 215//165 216//165 234//165 233//165 +f 216//166 217//166 235//166 234//166 +f 217//167 218//167 236//167 235//167 +f 218//168 219//168 237//168 236//168 +f 219//169 220//169 238//169 237//169 +f 220//170 221//170 239//170 238//170 +f 221//171 222//171 240//171 239//171 +f 222//172 223//172 241//172 240//172 +f 223//173 224//173 242//173 241//173 +f 224//174 225//174 243//174 242//174 +f 225//175 226//175 244//175 243//175 +f 226//176 227//176 245//176 244//176 +f 227//177 228//177 246//177 245//177 +f 228//178 229//178 247//178 246//178 +f 229//179 230//179 248//179 247//179 +f 230//180 231//180 249//180 248//180 +f 231//181 232//181 250//181 249//181 +f 232//182 215//182 233//182 250//182 +f 233//183 234//183 252//183 251//183 +f 234//184 235//184 253//184 252//184 +f 235//185 236//185 254//185 253//185 +f 236//186 237//186 255//186 254//186 +f 237//187 238//187 256//187 255//187 +f 238//188 239//188 257//188 256//188 +f 239//189 240//189 258//189 257//189 +f 240//190 241//190 259//190 258//190 +f 241//191 242//191 260//191 259//191 +f 242//192 243//192 261//192 260//192 +f 243//193 244//193 262//193 261//193 +f 244//194 245//194 263//194 262//194 +f 245//195 246//195 264//195 263//195 +f 246//196 247//196 265//196 264//196 +f 247//197 248//197 266//197 265//197 +f 248//198 249//198 267//198 266//198 +f 249//199 250//199 268//199 267//199 +f 250//200 233//200 251//200 268//200 +o letter05 +usemtl logo +f 269//2 320//2 319//2 +f 475//1 476//1 425//1 +f 269//2 319//2 318//2 +f 474//1 475//1 425//1 +f 269//2 318//2 317//2 +f 473//1 474//1 425//1 +f 269//2 317//2 316//2 +f 472//1 473//1 425//1 +f 269//2 316//2 315//2 +f 471//1 472//1 425//1 +f 269//2 315//2 314//2 +f 470//1 471//1 425//1 +f 269//1 314//1 313//1 +f 469//2 470//2 425//2 +f 269//1 313//1 312//1 +f 468//2 469//2 425//2 +f 269//2 312//2 311//2 +f 467//1 468//1 425//1 +f 269//2 311//2 310//2 +f 466//1 467//1 425//1 +f 269//2 310//2 309//2 +f 465//1 466//1 425//1 +f 269//2 309//2 308//2 +f 464//1 465//1 425//1 +f 269//2 308//2 307//2 +f 463//1 464//1 425//1 +f 269//2 307//2 306//2 +f 462//1 463//1 425//1 +f 269//2 306//2 305//2 +f 461//1 462//1 425//1 +f 294//2 293//2 292//2 +f 448//1 449//1 450//1 +f 294//2 292//2 291//2 +f 447//1 448//1 450//1 +f 294//2 291//2 290//2 +f 446//1 447//1 450//1 +f 295//2 294//2 290//2 +f 446//1 450//1 451//1 +f 296//2 295//2 290//2 +f 446//1 451//1 452//1 +f 296//2 290//2 289//2 +f 445//1 446//1 452//1 +f 297//2 296//2 289//2 +f 445//1 452//1 453//1 +f 297//1 289//1 288//1 +f 444//2 445//2 453//2 +f 297//2 288//2 287//2 +f 443//1 444//1 453//1 +f 298//2 297//2 287//2 +f 443//1 453//1 454//1 +f 299//2 298//2 287//2 +f 443//1 454//1 455//1 +f 287//1 286//1 285//1 +f 441//2 442//2 443//2 +f 287//2 285//2 284//2 +f 440//1 441//1 443//1 +f 299//1 287//1 284//1 +f 440//2 443//2 455//2 +f 300//2 299//2 284//2 +f 440//1 455//1 456//1 +f 301//2 300//2 284//2 +f 440//1 456//1 457//1 +f 301//2 284//2 283//2 +f 439//1 440//1 457//1 +f 302//2 301//2 283//2 +f 439//1 457//1 458//1 +f 302//2 283//2 282//2 +f 438//1 439//1 458//1 +f 303//2 302//2 282//2 +f 438//1 458//1 459//1 +f 303//2 282//2 281//2 +f 437//1 438//1 459//1 +f 303//2 281//2 280//2 +f 436//1 437//1 459//1 +f 304//2 303//2 280//2 +f 436//1 459//1 460//1 +f 304//2 280//2 279//2 +f 435//1 436//1 460//1 +f 305//2 304//2 279//2 +f 435//1 460//1 461//1 +f 269//1 305//1 279//1 +f 435//2 461//2 425//2 +f 269//2 279//2 278//2 +f 434//1 435//1 425//1 +f 269//2 278//2 277//2 +f 433//1 434//1 425//1 +f 269//2 277//2 276//2 +f 432//1 433//1 425//1 +f 269//2 276//2 275//2 +f 431//1 432//1 425//1 +f 269//2 275//2 274//2 +f 430//1 431//1 425//1 +f 269//2 274//2 273//2 +f 429//1 430//1 425//1 +f 269//2 273//2 272//2 +f 428//1 429//1 425//1 +f 269//2 272//2 271//2 +f 427//1 428//1 425//1 +f 271//2 270//2 269//2 +f 425//1 426//1 427//1 +f 269//201 270//201 322//201 321//201 +f 270//202 271//202 323//202 322//202 +f 271//203 272//203 324//203 323//203 +f 272//204 273//204 325//204 324//204 +f 273//205 274//205 326//205 325//205 +f 274//206 275//206 327//206 326//206 +f 275//207 276//207 328//207 327//207 +f 276//208 277//208 329//208 328//208 +f 277//209 278//209 330//209 329//209 +f 278//210 279//210 331//210 330//210 +f 279//211 280//211 332//211 331//211 +f 280//212 281//212 333//212 332//212 +f 281//213 282//213 334//213 333//213 +f 282//214 283//214 335//214 334//214 +f 283//215 284//215 336//215 335//215 +f 284//216 285//216 337//216 336//216 +f 285//217 286//217 338//217 337//217 +f 286//218 287//218 339//218 338//218 +f 287//219 288//219 340//219 339//219 +f 288//220 289//220 341//220 340//220 +f 289//221 290//221 342//221 341//221 +f 290//222 291//222 343//222 342//222 +f 291//223 292//223 344//223 343//223 +f 292//224 293//224 345//224 344//224 +f 293//225 294//225 346//225 345//225 +f 294//226 295//226 347//226 346//226 +f 295//227 296//227 348//227 347//227 +f 296//228 297//228 349//228 348//228 +f 297//229 298//229 350//229 349//229 +f 298//230 299//230 351//230 350//230 +f 299//231 300//231 352//231 351//231 +f 300//232 301//232 353//232 352//232 +f 301//233 302//233 354//233 353//233 +f 302//234 303//234 355//234 354//234 +f 303//235 304//235 356//235 355//235 +f 304//236 305//236 357//236 356//236 +f 305//237 306//237 358//237 357//237 +f 306//238 307//238 359//238 358//238 +f 307//239 308//239 360//239 359//239 +f 308//240 309//240 361//240 360//240 +f 309//241 310//241 362//241 361//241 +f 310//242 311//242 363//242 362//242 +f 311//243 312//243 364//243 363//243 +f 312//244 313//244 365//244 364//244 +f 313//154 314//154 366//154 365//154 +f 314//245 315//245 367//245 366//245 +f 315//246 316//246 368//246 367//246 +f 316//247 317//247 369//247 368//247 +f 317//248 318//248 370//248 369//248 +f 318//249 319//249 371//249 370//249 +f 319//250 320//250 372//250 371//250 +f 320//251 269//251 321//251 372//251 +f 321//252 322//252 374//252 373//252 +f 322//253 323//253 375//253 374//253 +f 323//254 324//254 376//254 375//254 +f 324//255 325//255 377//255 376//255 +f 325//256 326//256 378//256 377//256 +f 326//257 327//257 379//257 378//257 +f 327//258 328//258 380//258 379//258 +f 328//259 329//259 381//259 380//259 +f 329//260 330//260 382//260 381//260 +f 330//261 331//261 383//261 382//261 +f 331//262 332//262 384//262 383//262 +f 332//263 333//263 385//263 384//263 +f 333//264 334//264 386//264 385//264 +f 334//265 335//265 387//265 386//265 +f 335//266 336//266 388//266 387//266 +f 336//267 337//267 389//267 388//267 +f 337//268 338//268 390//268 389//268 +f 338//269 339//269 391//269 390//269 +f 339//270 340//270 392//270 391//270 +f 340//271 341//271 393//271 392//271 +f 341//272 342//272 394//272 393//272 +f 342//273 343//273 395//273 394//273 +f 343//274 344//274 396//274 395//274 +f 344//275 345//275 397//275 396//275 +f 345//276 346//276 398//276 397//276 +f 346//277 347//277 399//277 398//277 +f 347//278 348//278 400//278 399//278 +f 348//279 349//279 401//279 400//279 +f 349//280 350//280 402//280 401//280 +f 350//281 351//281 403//281 402//281 +f 351//282 352//282 404//282 403//282 +f 352//283 353//283 405//283 404//283 +f 353//284 354//284 406//284 405//284 +f 354//285 355//285 407//285 406//285 +f 355//286 356//286 408//286 407//286 +f 356//287 357//287 409//287 408//287 +f 357//288 358//288 410//288 409//288 +f 358//289 359//289 411//289 410//289 +f 359//290 360//290 412//290 411//290 +f 360//291 361//291 413//291 412//291 +f 361//292 362//292 414//292 413//292 +f 362//293 363//293 415//293 414//293 +f 363//294 364//294 416//294 415//294 +f 364//295 365//295 417//295 416//295 +f 365//172 366//172 418//172 417//172 +f 366//296 367//296 419//296 418//296 +f 367//50 368//50 420//50 419//50 +f 368//297 369//297 421//297 420//297 +f 369//298 370//298 422//298 421//298 +f 370//299 371//299 423//299 422//299 +f 371//300 372//300 424//300 423//300 +f 372//301 321//301 373//301 424//301 +f 373//302 374//302 426//302 425//302 +f 374//303 375//303 427//303 426//303 +f 375//304 376//304 428//304 427//304 +f 376//305 377//305 429//305 428//305 +f 377//306 378//306 430//306 429//306 +f 378//307 379//307 431//307 430//307 +f 379//308 380//308 432//308 431//308 +f 380//309 381//309 433//309 432//309 +f 381//310 382//310 434//310 433//310 +f 382//311 383//311 435//311 434//311 +f 383//312 384//312 436//312 435//312 +f 384//313 385//313 437//313 436//313 +f 385//314 386//314 438//314 437//314 +f 386//315 387//315 439//315 438//315 +f 387//316 388//316 440//316 439//316 +f 388//317 389//317 441//317 440//317 +f 389//318 390//318 442//318 441//318 +f 390//319 391//319 443//319 442//319 +f 391//320 392//320 444//320 443//320 +f 392//321 393//321 445//321 444//321 +f 393//322 394//322 446//322 445//322 +f 394//323 395//323 447//323 446//323 +f 395//324 396//324 448//324 447//324 +f 396//325 397//325 449//325 448//325 +f 397//326 398//326 450//326 449//326 +f 398//327 399//327 451//327 450//327 +f 399//328 400//328 452//328 451//328 +f 400//329 401//329 453//329 452//329 +f 401//330 402//330 454//330 453//330 +f 402//331 403//331 455//331 454//331 +f 403//332 404//332 456//332 455//332 +f 404//333 405//333 457//333 456//333 +f 405//334 406//334 458//334 457//334 +f 406//335 407//335 459//335 458//335 +f 407//336 408//336 460//336 459//336 +f 408//337 409//337 461//337 460//337 +f 409//338 410//338 462//338 461//338 +f 410//339 411//339 463//339 462//339 +f 411//340 412//340 464//340 463//340 +f 412//341 413//341 465//341 464//341 +f 413//342 414//342 466//342 465//342 +f 414//343 415//343 467//343 466//343 +f 415//344 416//344 468//344 467//344 +f 416//345 417//345 469//345 468//345 +f 417//190 418//190 470//190 469//190 +f 418//346 419//346 471//346 470//346 +f 419//347 420//347 472//347 471//347 +f 420//348 421//348 473//348 472//348 +f 421//349 422//349 474//349 473//349 +f 422//350 423//350 475//350 474//350 +f 423//351 424//351 476//351 475//351 +f 424//352 373//352 425//352 476//352 +o letter06 +usemtl logo +f 477//1 503//1 502//1 +f 583//2 584//2 558//2 +f 477//2 502//2 501//2 +f 582//1 583//1 558//1 +f 477//2 501//2 500//2 +f 581//1 582//1 558//1 +f 477//2 500//2 499//2 +f 580//1 581//1 558//1 +f 477//2 499//2 498//2 +f 579//1 580//1 558//1 +f 498//2 497//2 496//2 +f 577//1 578//1 579//1 +f 498//1 496//1 495//1 +f 576//2 577//2 579//2 +f 498//2 495//2 494//2 +f 575//1 576//1 579//1 +f 498//2 494//2 493//2 +f 574//1 575//1 579//1 +f 477//2 498//2 493//2 +f 574//1 579//1 558//1 +f 477//2 493//2 492//2 +f 573//1 574//1 558//1 +f 491//2 490//2 489//2 +f 570//1 571//1 572//1 +f 491//1 489//1 488//1 +f 569//2 570//2 572//2 +f 491//2 488//2 487//2 +f 568//1 569//1 572//1 +f 491//2 487//2 486//2 +f 567//1 568//1 572//1 +f 491//2 486//2 485//2 +f 566//1 567//1 572//1 +f 492//2 491//2 485//2 +f 566//1 572//1 573//1 +f 477//2 492//2 485//2 +f 566//1 573//1 558//1 +f 477//1 485//1 484//1 +f 565//2 566//2 558//2 +f 477//2 484//2 483//2 +f 564//1 565//1 558//1 +f 477//2 483//2 482//2 +f 563//1 564//1 558//1 +f 477//2 482//2 481//2 +f 562//1 563//1 558//1 +f 477//2 481//2 480//2 +f 561//1 562//1 558//1 +f 477//1 480//1 479//1 +f 560//2 561//2 558//2 +f 479//2 478//2 477//2 +f 558//1 559//1 560//1 +f 477//353 478//353 505//353 504//353 +f 478//354 479//354 506//354 505//354 +f 479//355 480//355 507//355 506//355 +f 480//356 481//356 508//356 507//356 +f 481//357 482//357 509//357 508//357 +f 482//358 483//358 510//358 509//358 +f 483//359 484//359 511//359 510//359 +f 484//360 485//360 512//360 511//360 +f 485//361 486//361 513//361 512//361 +f 486//362 487//362 514//362 513//362 +f 487//363 488//363 515//363 514//363 +f 488//364 489//364 516//364 515//364 +f 489//365 490//365 517//365 516//365 +f 490//366 491//366 518//366 517//366 +f 491//367 492//367 519//367 518//367 +f 492//368 493//368 520//368 519//368 +f 493//369 494//369 521//369 520//369 +f 494//370 495//370 522//370 521//370 +f 495//371 496//371 523//371 522//371 +f 496//372 497//372 524//372 523//372 +f 497//373 498//373 525//373 524//373 +f 498//374 499//374 526//374 525//374 +f 499//375 500//375 527//375 526//375 +f 500//376 501//376 528//376 527//376 +f 501//377 502//377 529//377 528//377 +f 502//372 503//372 530//372 529//372 +f 503//378 477//378 504//378 530//378 +f 504//379 505//379 532//379 531//379 +f 505//380 506//380 533//380 532//380 +f 506//381 507//381 534//381 533//381 +f 507//382 508//382 535//382 534//382 +f 508//383 509//383 536//383 535//383 +f 509//384 510//384 537//384 536//384 +f 510//385 511//385 538//385 537//385 +f 511//386 512//386 539//386 538//386 +f 512//387 513//387 540//387 539//387 +f 513//388 514//388 541//388 540//388 +f 514//389 515//389 542//389 541//389 +f 515//390 516//390 543//390 542//390 +f 516//391 517//391 544//391 543//391 +f 517//392 518//392 545//392 544//392 +f 518//393 519//393 546//393 545//393 +f 519//394 520//394 547//394 546//394 +f 520//395 521//395 548//395 547//395 +f 521//396 522//396 549//396 548//396 +f 522//397 523//397 550//397 549//397 +f 523//398 524//398 551//398 550//398 +f 524//399 525//399 552//399 551//399 +f 525//400 526//400 553//400 552//400 +f 526//401 527//401 554//401 553//401 +f 527//402 528//402 555//402 554//402 +f 528//403 529//403 556//403 555//403 +f 529//398 530//398 557//398 556//398 +f 530//404 504//404 531//404 557//404 +f 531//405 532//405 559//405 558//405 +f 532//406 533//406 560//406 559//406 +f 533//407 534//407 561//407 560//407 +f 534//408 535//408 562//408 561//408 +f 535//409 536//409 563//409 562//409 +f 536//410 537//410 564//410 563//410 +f 537//411 538//411 565//411 564//411 +f 538//412 539//412 566//412 565//412 +f 539//413 540//413 567//413 566//413 +f 540//414 541//414 568//414 567//414 +f 541//415 542//415 569//415 568//415 +f 542//416 543//416 570//416 569//416 +f 543//417 544//417 571//417 570//417 +f 544//418 545//418 572//418 571//418 +f 545//419 546//419 573//419 572//419 +f 546//420 547//420 574//420 573//420 +f 547//421 548//421 575//421 574//421 +f 548//422 549//422 576//422 575//422 +f 549//423 550//423 577//423 576//423 +f 550//424 551//424 578//424 577//424 +f 551//425 552//425 579//425 578//425 +f 552//426 553//426 580//426 579//426 +f 553//427 554//427 581//427 580//427 +f 554//428 555//428 582//428 581//428 +f 555//429 556//429 583//429 582//429 +f 556//424 557//424 584//424 583//424 +f 557//430 531//430 558//430 584//430 diff --git a/assets/SingeText.svg b/assets/SingeText.svg new file mode 100644 index 000000000..c3bce40ab --- /dev/null +++ b/assets/SingeText.svg @@ -0,0 +1,8 @@ + + + + Singe + + + + diff --git a/assets/SingeTextPrint-face.stl b/assets/SingeTextPrint-face.stl new file mode 100644 index 000000000..f15fc4c3c --- /dev/null +++ b/assets/SingeTextPrint-face.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7b8d3d4e1b92e284a62bdc891c211aba1173d7e26e4d536705fc65580257878d +size 42684 diff --git a/assets/SingeTextPrint-plate.stl b/assets/SingeTextPrint-plate.stl new file mode 100644 index 000000000..274279274 --- /dev/null +++ b/assets/SingeTextPrint-plate.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61b2c735b7a0ba86cebd0f482beab4e2d3412d22e4fbfad365e6e7e2e5849961 +size 20884 diff --git a/assets/SingeTextPrint-walls.stl b/assets/SingeTextPrint-walls.stl new file mode 100644 index 000000000..82e19e928 --- /dev/null +++ b/assets/SingeTextPrint-walls.stl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae9c022b0948af943eee1c16ca3aab70d9ddcbff80395b8bab3928ac324a5d47 +size 28084 diff --git a/assets/SingeTextPrint.mtl b/assets/SingeTextPrint.mtl new file mode 100644 index 000000000..390f4352f --- /dev/null +++ b/assets/SingeTextPrint.mtl @@ -0,0 +1,34 @@ +# Materials for SingeTextPrint.obj +newmtl dark +Ka 0.060 0.062 0.066 +Kd 0.300 0.310 0.330 +Ks 0.700 0.700 0.700 +Ns 180.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.35 + +newmtl frame +Ka 0.024 0.022 0.020 +Kd 0.120 0.110 0.100 +Ks 0.300 0.300 0.300 +Ns 40.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.00 +Pr 0.70 + +newmtl champagne +Ka 0.144 0.128 0.108 +Kd 0.720 0.640 0.540 +Ks 0.850 0.800 0.700 +Ns 120.0 +d 1.00 +Ni 1.00 +illum 2 +Pm 0.80 +Pr 0.40 + diff --git a/assets/SingeTextPrint.obj b/assets/SingeTextPrint.obj new file mode 100644 index 000000000..8b52eedea --- /dev/null +++ b/assets/SingeTextPrint.obj @@ -0,0 +1,2617 @@ +# Singe logotype, printable extruded from SingeText.jpeg by util/textModel.py +# Units: 0.01 per image pixel; Y up, front faces toward +Z, resting on Y = 0, centred on X = 0 +mtllib SingeTextPrint.mtl +v -3.62750 4.91000 -0.31000 +v -4.43750 4.36000 -0.31000 +v -5.07750 3.81000 -0.31000 +v -5.56250 3.27500 -0.31000 +v -5.57250 3.22000 -0.31000 +v -5.43250 3.07500 -0.31000 +v -4.42250 2.41500 -0.31000 +v -4.28250 2.27000 -0.31000 +v -4.52750 2.05500 -0.31000 +v -5.90250 1.16000 -0.31000 +v -6.05250 1.01000 -0.31000 +v -6.06250 0.95000 -0.31000 +v -5.82250 0.16000 -0.31000 +v -5.67250 0.00500 -0.31000 +v -5.61250 0.02000 -0.31000 +v -4.61750 0.84000 -0.31000 +v -3.74250 1.50000 -0.31000 +v -3.13750 2.02500 -0.31000 +v -3.00250 1.88500 -0.31000 +v -3.24250 0.77000 -0.31000 +v -3.10250 0.62500 -0.31000 +v -2.39750 0.90000 -0.31000 +v -2.18750 1.11000 -0.31000 +v -2.05250 0.97500 -0.31000 +v -2.11250 0.68000 -0.31000 +v -1.97250 0.54000 -0.31000 +v -1.32750 0.78000 -0.31000 +v -1.18750 0.92500 -0.31000 +v -0.91750 2.12000 -0.31000 +v -0.77750 2.25500 -0.31000 +v -0.64250 2.12000 -0.31000 +v -0.18750 0.99500 -0.31000 +v -0.05250 0.85000 -0.31000 +v -0.00750 0.84000 -0.31000 +v 0.33750 1.01500 -0.31000 +v 0.47250 1.15000 -0.31000 +v 0.58750 1.62000 -0.31000 +v 0.72750 1.76500 -0.31000 +v 0.86250 1.63000 -0.31000 +v 0.99750 1.36500 -0.31000 +v 1.23250 1.12500 -0.31000 +v 1.49250 0.99500 -0.31000 +v 1.70750 0.99000 -0.31000 +v 1.84250 0.85500 -0.31000 +v 1.64750 0.16500 -0.31000 +v 1.78750 0.02500 -0.31000 +v 2.40250 0.26000 -0.31000 +v 2.52750 0.38500 -0.31000 +v 2.62750 0.70500 -0.31000 +v 2.76750 0.84500 -0.31000 +v 2.97250 0.65000 -0.31000 +v 5.32250 1.34000 -0.31000 +v 5.49250 1.51000 -0.31000 +v 5.72250 2.14000 -0.31000 +v 5.58250 2.30500 -0.31000 +v 4.20250 1.95000 -0.31000 +v 4.06250 2.09500 -0.31000 +v 4.12750 2.32000 -0.31000 +v 4.26250 2.45500 -0.31000 +v 5.02250 2.66000 -0.31000 +v 5.17250 2.80500 -0.31000 +v 5.37250 3.34000 -0.31000 +v 5.21750 3.48000 -0.31000 +v 4.58750 3.33500 -0.31000 +v 4.45250 3.47000 -0.31000 +v 4.48250 3.57500 -0.31000 +v 4.62250 3.72000 -0.31000 +v 5.69750 4.02000 -0.31000 +v 5.82750 4.15500 -0.31000 +v 6.06250 4.73500 -0.31000 +v 6.05750 4.77500 -0.31000 +v 5.91750 4.91500 -0.31000 +v 4.18250 4.48500 -0.31000 +v 3.68250 4.32500 -0.31000 +v 3.51750 4.17000 -0.31000 +v 3.16750 4.54000 -0.31000 +v 3.00250 4.65000 -0.31000 +v 2.77250 4.71000 -0.31000 +v 2.45750 4.68500 -0.31000 +v 2.22750 4.60500 -0.31000 +v 1.96250 4.45000 -0.31000 +v 1.36750 3.87500 -0.31000 +v 1.23250 4.01000 -0.31000 +v 1.49250 4.87000 -0.31000 +v 1.33750 5.00500 -0.31000 +v 0.60750 4.74500 -0.31000 +v 0.47250 4.61500 -0.31000 +v 0.14250 3.31500 -0.31000 +v 0.00750 3.18000 -0.31000 +v -0.13750 3.33500 -0.31000 +v -0.50750 4.30000 -0.31000 +v -0.65250 4.43500 -0.31000 +v -1.20250 4.20000 -0.31000 +v -1.33750 4.06500 -0.31000 +v -1.37250 3.92500 -0.31000 +v -1.50750 3.79000 -0.31000 +v -1.71750 4.00000 -0.31000 +v -1.43750 4.28500 -0.31000 +v -1.21250 4.97500 -0.31000 +v -1.34250 5.12000 -0.31000 +v -2.26750 4.78500 -0.31000 +v -2.53250 4.52500 -0.31000 +v -2.66750 4.66000 -0.31000 +v -2.81250 5.09500 -0.31000 +v -2.97250 5.23000 -0.31000 +v -3.62750 4.91000 -0.15000 +v -4.43750 4.36000 -0.15000 +v -5.07750 3.81000 -0.15000 +v -5.56250 3.27500 -0.15000 +v -5.57250 3.22000 -0.15000 +v -5.43250 3.07500 -0.15000 +v -4.42250 2.41500 -0.15000 +v -4.28250 2.27000 -0.15000 +v -4.52750 2.05500 -0.15000 +v -5.90250 1.16000 -0.15000 +v -6.05250 1.01000 -0.15000 +v -6.06250 0.95000 -0.15000 +v -5.82250 0.16000 -0.15000 +v -5.67250 0.00500 -0.15000 +v -5.61250 0.02000 -0.15000 +v -4.61750 0.84000 -0.15000 +v -3.74250 1.50000 -0.15000 +v -3.13750 2.02500 -0.15000 +v -3.00250 1.88500 -0.15000 +v -3.24250 0.77000 -0.15000 +v -3.10250 0.62500 -0.15000 +v -2.39750 0.90000 -0.15000 +v -2.18750 1.11000 -0.15000 +v -2.05250 0.97500 -0.15000 +v -2.11250 0.68000 -0.15000 +v -1.97250 0.54000 -0.15000 +v -1.32750 0.78000 -0.15000 +v -1.18750 0.92500 -0.15000 +v -0.91750 2.12000 -0.15000 +v -0.77750 2.25500 -0.15000 +v -0.64250 2.12000 -0.15000 +v -0.18750 0.99500 -0.15000 +v -0.05250 0.85000 -0.15000 +v -0.00750 0.84000 -0.15000 +v 0.33750 1.01500 -0.15000 +v 0.47250 1.15000 -0.15000 +v 0.58750 1.62000 -0.15000 +v 0.72750 1.76500 -0.15000 +v 0.86250 1.63000 -0.15000 +v 0.99750 1.36500 -0.15000 +v 1.23250 1.12500 -0.15000 +v 1.49250 0.99500 -0.15000 +v 1.70750 0.99000 -0.15000 +v 1.84250 0.85500 -0.15000 +v 1.64750 0.16500 -0.15000 +v 1.78750 0.02500 -0.15000 +v 2.40250 0.26000 -0.15000 +v 2.52750 0.38500 -0.15000 +v 2.62750 0.70500 -0.15000 +v 2.76750 0.84500 -0.15000 +v 2.97250 0.65000 -0.15000 +v 5.32250 1.34000 -0.15000 +v 5.49250 1.51000 -0.15000 +v 5.72250 2.14000 -0.15000 +v 5.58250 2.30500 -0.15000 +v 4.20250 1.95000 -0.15000 +v 4.06250 2.09500 -0.15000 +v 4.12750 2.32000 -0.15000 +v 4.26250 2.45500 -0.15000 +v 5.02250 2.66000 -0.15000 +v 5.17250 2.80500 -0.15000 +v 5.37250 3.34000 -0.15000 +v 5.21750 3.48000 -0.15000 +v 4.58750 3.33500 -0.15000 +v 4.45250 3.47000 -0.15000 +v 4.48250 3.57500 -0.15000 +v 4.62250 3.72000 -0.15000 +v 5.69750 4.02000 -0.15000 +v 5.82750 4.15500 -0.15000 +v 6.06250 4.73500 -0.15000 +v 6.05750 4.77500 -0.15000 +v 5.91750 4.91500 -0.15000 +v 4.18250 4.48500 -0.15000 +v 3.68250 4.32500 -0.15000 +v 3.51750 4.17000 -0.15000 +v 3.16750 4.54000 -0.15000 +v 3.00250 4.65000 -0.15000 +v 2.77250 4.71000 -0.15000 +v 2.45750 4.68500 -0.15000 +v 2.22750 4.60500 -0.15000 +v 1.96250 4.45000 -0.15000 +v 1.36750 3.87500 -0.15000 +v 1.23250 4.01000 -0.15000 +v 1.49250 4.87000 -0.15000 +v 1.33750 5.00500 -0.15000 +v 0.60750 4.74500 -0.15000 +v 0.47250 4.61500 -0.15000 +v 0.14250 3.31500 -0.15000 +v 0.00750 3.18000 -0.15000 +v -0.13750 3.33500 -0.15000 +v -0.50750 4.30000 -0.15000 +v -0.65250 4.43500 -0.15000 +v -1.20250 4.20000 -0.15000 +v -1.33750 4.06500 -0.15000 +v -1.37250 3.92500 -0.15000 +v -1.50750 3.79000 -0.15000 +v -1.71750 4.00000 -0.15000 +v -1.43750 4.28500 -0.15000 +v -1.21250 4.97500 -0.15000 +v -1.34250 5.12000 -0.15000 +v -2.26750 4.78500 -0.15000 +v -2.53250 4.52500 -0.15000 +v -2.66750 4.66000 -0.15000 +v -2.81250 5.09500 -0.15000 +v -2.97250 5.23000 -0.15000 +v -3.62750 4.79000 -0.15000 +v -4.25750 4.37500 -0.15000 +v -4.97250 3.79000 -0.15000 +v -5.24250 3.52000 -0.15000 +v -5.44250 3.27500 -0.15000 +v -5.45250 3.22000 -0.15000 +v -5.39250 3.16500 -0.15000 +v -4.35250 2.49000 -0.15000 +v -4.07250 2.27000 -0.15000 +v -4.65250 1.85000 -0.15000 +v -5.93250 1.01000 -0.15000 +v -5.94250 0.95000 -0.15000 +v -5.74250 0.28000 -0.15000 +v -5.67250 0.12500 -0.15000 +v -5.61250 0.14000 -0.15000 +v -4.73750 0.86500 -0.15000 +v -3.46250 1.84500 -0.15000 +v -3.19250 2.09000 -0.15000 +v -2.96750 2.35000 -0.15000 +v -2.97250 2.39500 -0.15000 +v -3.04250 2.46500 -0.15000 +v -3.26750 2.64000 -0.15000 +v -4.33750 3.29500 -0.15000 +v -4.10250 3.50500 -0.15000 +v -3.67750 3.79000 -0.15000 +v -2.68750 4.31000 -0.15000 +v -2.88750 4.98500 -0.15000 +v -2.93250 5.09500 -0.15000 +v -2.97250 5.11000 -0.15000 +v -3.62750 4.79000 0.09000 +v -4.25750 4.37500 0.09000 +v -4.97250 3.79000 0.09000 +v -5.24250 3.52000 0.09000 +v -5.44250 3.27500 0.09000 +v -5.45250 3.22000 0.09000 +v -5.39250 3.16500 0.09000 +v -4.35250 2.49000 0.09000 +v -4.07250 2.27000 0.09000 +v -4.65250 1.85000 0.09000 +v -5.93250 1.01000 0.09000 +v -5.94250 0.95000 0.09000 +v -5.74250 0.28000 0.09000 +v -5.67250 0.12500 0.09000 +v -5.61250 0.14000 0.09000 +v -4.73750 0.86500 0.09000 +v -3.46250 1.84500 0.09000 +v -3.19250 2.09000 0.09000 +v -2.96750 2.35000 0.09000 +v -2.97250 2.39500 0.09000 +v -3.04250 2.46500 0.09000 +v -3.26750 2.64000 0.09000 +v -4.33750 3.29500 0.09000 +v -4.10250 3.50500 0.09000 +v -3.67750 3.79000 0.09000 +v -2.68750 4.31000 0.09000 +v -2.88750 4.98500 0.09000 +v -2.93250 5.09500 0.09000 +v -2.97250 5.11000 0.09000 +v -3.62750 4.79000 0.09000 +v -4.25750 4.37500 0.09000 +v -4.97250 3.79000 0.09000 +v -5.24250 3.52000 0.09000 +v -5.44250 3.27500 0.09000 +v -5.45250 3.22000 0.09000 +v -5.39250 3.16500 0.09000 +v -4.35250 2.49000 0.09000 +v -4.07250 2.27000 0.09000 +v -4.65250 1.85000 0.09000 +v -5.93250 1.01000 0.09000 +v -5.94250 0.95000 0.09000 +v -5.74250 0.28000 0.09000 +v -5.67250 0.12500 0.09000 +v -5.61250 0.14000 0.09000 +v -4.73750 0.86500 0.09000 +v -3.46250 1.84500 0.09000 +v -3.19250 2.09000 0.09000 +v -2.96750 2.35000 0.09000 +v -2.97250 2.39500 0.09000 +v -3.04250 2.46500 0.09000 +v -3.26750 2.64000 0.09000 +v -4.33750 3.29500 0.09000 +v -4.10250 3.50500 0.09000 +v -3.67750 3.79000 0.09000 +v -2.68750 4.31000 0.09000 +v -2.88750 4.98500 0.09000 +v -2.93250 5.09500 0.09000 +v -2.97250 5.11000 0.09000 +v -3.62750 4.79000 0.10000 +v -4.25750 4.37500 0.10000 +v -4.97250 3.79000 0.10000 +v -5.24250 3.52000 0.10000 +v -5.44250 3.27500 0.10000 +v -5.45250 3.22000 0.10000 +v -5.39250 3.16500 0.10000 +v -4.35250 2.49000 0.10000 +v -4.07250 2.27000 0.10000 +v -4.65250 1.85000 0.10000 +v -5.93250 1.01000 0.10000 +v -5.94250 0.95000 0.10000 +v -5.74250 0.28000 0.10000 +v -5.67250 0.12500 0.10000 +v -5.61250 0.14000 0.10000 +v -4.73750 0.86500 0.10000 +v -3.46250 1.84500 0.10000 +v -3.19250 2.09000 0.10000 +v -2.96750 2.35000 0.10000 +v -2.97250 2.39500 0.10000 +v -3.04250 2.46500 0.10000 +v -3.26750 2.64000 0.10000 +v -4.33750 3.29500 0.10000 +v -4.10250 3.50500 0.10000 +v -3.67750 3.79000 0.10000 +v -2.68750 4.31000 0.10000 +v -2.88750 4.98500 0.10000 +v -2.93250 5.09500 0.10000 +v -2.97250 5.11000 0.10000 +v -3.60267 4.74648 0.15000 +v -4.22784 4.33467 0.15000 +v -4.93891 3.75288 0.15000 +v -5.20536 3.48643 0.15000 +v -5.39561 3.25337 0.15000 +v -5.39837 3.23821 0.15000 +v -5.36178 3.20467 0.15000 +v -4.32338 2.53071 0.15000 +v -3.98950 2.26837 0.15000 +v -4.62411 1.80883 0.15000 +v -5.88678 0.98020 0.15000 +v -5.89128 0.95320 0.15000 +v -5.69555 0.29752 0.15000 +v -5.64412 0.18363 0.15000 +v -5.63565 0.18575 0.15000 +v -4.76870 0.90409 0.15000 +v -3.49459 1.88340 0.15000 +v -3.22833 2.12500 0.15000 +v -3.01961 2.36620 0.15000 +v -3.02026 2.37205 0.15000 +v -3.07565 2.42744 0.15000 +v -3.29599 2.59882 0.15000 +v -4.42097 3.28747 0.15000 +v -4.13323 3.54459 0.15000 +v -3.70312 3.83302 0.15000 +v -2.74711 4.33517 0.15000 +v -2.93473 4.96839 0.15000 +v -2.97051 5.05585 0.15000 +v -2.96990 5.05562 0.15000 +v -2.44250 3.60500 -0.15000 +v -2.50250 3.55500 -0.15000 +v -3.09250 0.95000 -0.15000 +v -3.12250 0.79000 -0.15000 +v -3.10250 0.74500 -0.15000 +v -2.53750 0.96000 -0.15000 +v -2.37750 1.04500 -0.15000 +v -2.04750 2.46000 -0.15000 +v -1.69750 3.82000 -0.15000 +v -1.70250 3.86500 -0.15000 +v -1.73250 3.87000 -0.15000 +v -2.44250 3.60500 0.09000 +v -2.50250 3.55500 0.09000 +v -3.09250 0.95000 0.09000 +v -3.12250 0.79000 0.09000 +v -3.10250 0.74500 0.09000 +v -2.53750 0.96000 0.09000 +v -2.37750 1.04500 0.09000 +v -2.04750 2.46000 0.09000 +v -1.69750 3.82000 0.09000 +v -1.70250 3.86500 0.09000 +v -1.73250 3.87000 0.09000 +v -2.44250 3.60500 0.09000 +v -2.50250 3.55500 0.09000 +v -3.09250 0.95000 0.09000 +v -3.12250 0.79000 0.09000 +v -3.10250 0.74500 0.09000 +v -2.53750 0.96000 0.09000 +v -2.37750 1.04500 0.09000 +v -2.04750 2.46000 0.09000 +v -1.69750 3.82000 0.09000 +v -1.70250 3.86500 0.09000 +v -1.73250 3.87000 0.09000 +v -2.44250 3.60500 0.10000 +v -2.50250 3.55500 0.10000 +v -3.09250 0.95000 0.10000 +v -3.12250 0.79000 0.10000 +v -3.10250 0.74500 0.10000 +v -2.53750 0.96000 0.10000 +v -2.37750 1.04500 0.10000 +v -2.04750 2.46000 0.10000 +v -1.69750 3.82000 0.10000 +v -1.70250 3.86500 0.10000 +v -1.73250 3.87000 0.10000 +v -2.41704 3.56114 0.15000 +v -2.45748 3.52743 0.15000 +v -3.04353 0.93987 0.15000 +v -3.07049 0.79608 0.15000 +v -3.07604 0.80857 0.15000 +v -2.55820 1.00562 0.15000 +v -2.42103 1.07849 0.15000 +v -2.09606 2.47191 0.15000 +v -1.74821 3.82359 0.15000 +v -1.74802 3.82190 0.15000 +v -1.72754 3.81848 0.15000 +v -2.26750 4.66500 -0.15000 +v -2.29250 4.63500 -0.15000 +v -2.45750 3.93500 -0.15000 +v -2.41750 3.92000 -0.15000 +v -2.31750 3.95500 -0.15000 +v -1.58250 4.25500 -0.15000 +v -1.33750 4.95500 -0.15000 +v -1.33750 4.99500 -0.15000 +v -1.36750 5.00500 -0.15000 +v -2.26750 4.66500 0.09000 +v -2.29250 4.63500 0.09000 +v -2.45750 3.93500 0.09000 +v -2.41750 3.92000 0.09000 +v -2.31750 3.95500 0.09000 +v -1.58250 4.25500 0.09000 +v -1.33750 4.95500 0.09000 +v -1.33750 4.99500 0.09000 +v -1.36750 5.00500 0.09000 +v -2.26750 4.66500 0.09000 +v -2.29250 4.63500 0.09000 +v -2.45750 3.93500 0.09000 +v -2.41750 3.92000 0.09000 +v -2.31750 3.95500 0.09000 +v -1.58250 4.25500 0.09000 +v -1.33750 4.95500 0.09000 +v -1.33750 4.99500 0.09000 +v -1.36750 5.00500 0.09000 +v -2.26750 4.66500 0.10000 +v -2.29250 4.63500 0.10000 +v -2.45750 3.93500 0.10000 +v -2.41750 3.92000 0.10000 +v -2.31750 3.95500 0.10000 +v -1.58250 4.25500 0.10000 +v -1.33750 4.95500 0.10000 +v -1.33750 4.99500 0.10000 +v -1.36750 5.00500 0.10000 +v -2.23752 4.62288 0.15000 +v -2.24654 4.61205 0.15000 +v -2.39874 3.96636 0.15000 +v -2.41691 3.97318 0.15000 +v -2.33522 4.00177 0.15000 +v -1.62225 4.29278 0.15000 +v -1.38750 4.96350 0.15000 +v -1.38750 4.95896 0.15000 +v -1.36645 4.95195 0.15000 +v 0.58250 4.60000 -0.15000 +v 0.07250 2.50500 -0.15000 +v -0.62750 4.30000 -0.15000 +v -0.69250 4.30500 -0.15000 +v -1.21750 4.06500 -0.15000 +v -1.78250 1.65500 -0.15000 +v -1.98250 0.76000 -0.15000 +v -1.98250 0.66500 -0.15000 +v -1.92750 0.66500 -0.15000 +v -1.31250 0.91500 -0.15000 +v -0.84250 2.89000 -0.15000 +v -0.05250 0.97000 -0.15000 +v -0.00750 0.96000 -0.15000 +v 0.35250 1.15000 -0.15000 +v 0.89750 3.26500 -0.15000 +v 1.36750 4.82000 -0.15000 +v 1.37250 4.87000 -0.15000 +v 1.33750 4.88500 -0.15000 +v 0.58250 4.60000 0.09000 +v 0.07250 2.50500 0.09000 +v -0.62750 4.30000 0.09000 +v -0.69250 4.30500 0.09000 +v -1.21750 4.06500 0.09000 +v -1.78250 1.65500 0.09000 +v -1.98250 0.76000 0.09000 +v -1.98250 0.66500 0.09000 +v -1.92750 0.66500 0.09000 +v -1.31250 0.91500 0.09000 +v -0.84250 2.89000 0.09000 +v -0.05250 0.97000 0.09000 +v -0.00750 0.96000 0.09000 +v 0.35250 1.15000 0.09000 +v 0.89750 3.26500 0.09000 +v 1.36750 4.82000 0.09000 +v 1.37250 4.87000 0.09000 +v 1.33750 4.88500 0.09000 +v 0.58250 4.60000 0.09000 +v 0.07250 2.50500 0.09000 +v -0.62750 4.30000 0.09000 +v -0.69250 4.30500 0.09000 +v -1.21750 4.06500 0.09000 +v -1.78250 1.65500 0.09000 +v -1.98250 0.76000 0.09000 +v -1.98250 0.66500 0.09000 +v -1.92750 0.66500 0.09000 +v -1.31250 0.91500 0.09000 +v -0.84250 2.89000 0.09000 +v -0.05250 0.97000 0.09000 +v -0.00750 0.96000 0.09000 +v 0.35250 1.15000 0.09000 +v 0.89750 3.26500 0.09000 +v 1.36750 4.82000 0.09000 +v 1.37250 4.87000 0.09000 +v 1.33750 4.88500 0.09000 +v 0.58250 4.60000 0.10000 +v 0.07250 2.50500 0.10000 +v -0.62750 4.30000 0.10000 +v -0.69250 4.30500 0.10000 +v -1.21750 4.06500 0.10000 +v -1.78250 1.65500 0.10000 +v -1.98250 0.76000 0.10000 +v -1.98250 0.66500 0.10000 +v -1.92750 0.66500 0.10000 +v -1.31250 0.91500 0.10000 +v -0.84250 2.89000 0.10000 +v -0.05250 0.97000 0.10000 +v -0.00750 0.96000 0.10000 +v 0.35250 1.15000 0.10000 +v 0.89750 3.26500 0.10000 +v 1.36750 4.82000 0.10000 +v 1.37250 4.87000 0.10000 +v 1.33750 4.88500 0.10000 +v 0.62484 4.56254 0.15000 +v 0.08200 2.36246 0.15000 +v -0.66267 4.25256 0.15000 +v -0.68346 4.25416 0.15000 +v -1.17442 4.02972 0.15000 +v -1.73376 1.64384 0.15000 +v -1.93250 0.75448 0.15000 +v -1.93250 0.71500 0.15000 +v -1.93727 0.71500 0.15000 +v -1.35518 0.95162 0.15000 +v -0.85368 3.03242 0.15000 +v -0.01619 1.01315 0.15000 +v -0.01459 1.01280 0.15000 +v 0.30960 1.18390 0.15000 +v 0.84934 3.27848 0.15000 +v 1.31823 4.82981 0.15000 +v 1.31910 4.83849 0.15000 +v 1.33632 4.83111 0.15000 +v 2.31250 4.52000 -0.15000 +v 2.06750 4.40000 -0.15000 +v 1.84750 4.24000 -0.15000 +v 1.59250 3.98000 -0.15000 +v 1.35750 3.65500 -0.15000 +v 1.16750 3.30000 -0.15000 +v 1.01750 2.90000 -0.15000 +v 0.93750 2.57500 -0.15000 +v 0.89750 2.16500 -0.15000 +v 0.91750 1.86500 -0.15000 +v 0.99750 1.58000 -0.15000 +v 1.13750 1.34000 -0.15000 +v 1.33750 1.17500 -0.15000 +v 1.55250 1.10500 -0.15000 +v 1.78250 1.12000 -0.15000 +v 2.06750 1.22000 -0.15000 +v 1.76750 0.20000 -0.15000 +v 1.78750 0.14500 -0.15000 +v 2.40750 0.38500 -0.15000 +v 3.31250 3.18000 -0.15000 +v 3.27250 3.21000 -0.15000 +v 3.02750 3.16500 -0.15000 +v 2.05250 2.91500 -0.15000 +v 1.87250 2.39500 -0.15000 +v 1.93250 2.37000 -0.15000 +v 2.46250 2.50000 -0.15000 +v 2.47750 2.47500 -0.15000 +v 2.37750 2.22000 -0.15000 +v 2.15750 1.96500 -0.15000 +v 1.99750 1.85500 -0.15000 +v 1.87250 1.80500 -0.15000 +v 1.69250 1.81000 -0.15000 +v 1.58750 1.89500 -0.15000 +v 1.55250 1.95500 -0.15000 +v 1.50750 2.24500 -0.15000 +v 1.57250 2.64000 -0.15000 +v 1.70250 2.99500 -0.15000 +v 1.94750 3.40500 -0.15000 +v 2.20750 3.67500 -0.15000 +v 2.33250 3.76000 -0.15000 +v 2.47750 3.82000 -0.15000 +v 2.57250 3.83000 -0.15000 +v 2.66750 3.80000 -0.15000 +v 2.74750 3.73000 -0.15000 +v 2.82250 3.61000 -0.15000 +v 2.85750 3.61000 -0.15000 +v 3.31750 4.18000 -0.15000 +v 3.31250 4.22500 -0.15000 +v 3.23750 4.34000 -0.15000 +v 3.06750 4.49500 -0.15000 +v 2.87250 4.57500 -0.15000 +v 2.60750 4.59000 -0.15000 +v 2.31250 4.52000 0.09000 +v 2.06750 4.40000 0.09000 +v 1.84750 4.24000 0.09000 +v 1.59250 3.98000 0.09000 +v 1.35750 3.65500 0.09000 +v 1.16750 3.30000 0.09000 +v 1.01750 2.90000 0.09000 +v 0.93750 2.57500 0.09000 +v 0.89750 2.16500 0.09000 +v 0.91750 1.86500 0.09000 +v 0.99750 1.58000 0.09000 +v 1.13750 1.34000 0.09000 +v 1.33750 1.17500 0.09000 +v 1.55250 1.10500 0.09000 +v 1.78250 1.12000 0.09000 +v 2.06750 1.22000 0.09000 +v 1.76750 0.20000 0.09000 +v 1.78750 0.14500 0.09000 +v 2.40750 0.38500 0.09000 +v 3.31250 3.18000 0.09000 +v 3.27250 3.21000 0.09000 +v 3.02750 3.16500 0.09000 +v 2.05250 2.91500 0.09000 +v 1.87250 2.39500 0.09000 +v 1.93250 2.37000 0.09000 +v 2.46250 2.50000 0.09000 +v 2.47750 2.47500 0.09000 +v 2.37750 2.22000 0.09000 +v 2.15750 1.96500 0.09000 +v 1.99750 1.85500 0.09000 +v 1.87250 1.80500 0.09000 +v 1.69250 1.81000 0.09000 +v 1.58750 1.89500 0.09000 +v 1.55250 1.95500 0.09000 +v 1.50750 2.24500 0.09000 +v 1.57250 2.64000 0.09000 +v 1.70250 2.99500 0.09000 +v 1.94750 3.40500 0.09000 +v 2.20750 3.67500 0.09000 +v 2.33250 3.76000 0.09000 +v 2.47750 3.82000 0.09000 +v 2.57250 3.83000 0.09000 +v 2.66750 3.80000 0.09000 +v 2.74750 3.73000 0.09000 +v 2.82250 3.61000 0.09000 +v 2.85750 3.61000 0.09000 +v 3.31750 4.18000 0.09000 +v 3.31250 4.22500 0.09000 +v 3.23750 4.34000 0.09000 +v 3.06750 4.49500 0.09000 +v 2.87250 4.57500 0.09000 +v 2.60750 4.59000 0.09000 +v 2.31250 4.52000 0.09000 +v 2.06750 4.40000 0.09000 +v 1.84750 4.24000 0.09000 +v 1.59250 3.98000 0.09000 +v 1.35750 3.65500 0.09000 +v 1.16750 3.30000 0.09000 +v 1.01750 2.90000 0.09000 +v 0.93750 2.57500 0.09000 +v 0.89750 2.16500 0.09000 +v 0.91750 1.86500 0.09000 +v 0.99750 1.58000 0.09000 +v 1.13750 1.34000 0.09000 +v 1.33750 1.17500 0.09000 +v 1.55250 1.10500 0.09000 +v 1.78250 1.12000 0.09000 +v 2.06750 1.22000 0.09000 +v 1.76750 0.20000 0.09000 +v 1.78750 0.14500 0.09000 +v 2.40750 0.38500 0.09000 +v 3.31250 3.18000 0.09000 +v 3.27250 3.21000 0.09000 +v 3.02750 3.16500 0.09000 +v 2.05250 2.91500 0.09000 +v 1.87250 2.39500 0.09000 +v 1.93250 2.37000 0.09000 +v 2.46250 2.50000 0.09000 +v 2.47750 2.47500 0.09000 +v 2.37750 2.22000 0.09000 +v 2.15750 1.96500 0.09000 +v 1.99750 1.85500 0.09000 +v 1.87250 1.80500 0.09000 +v 1.69250 1.81000 0.09000 +v 1.58750 1.89500 0.09000 +v 1.55250 1.95500 0.09000 +v 1.50750 2.24500 0.09000 +v 1.57250 2.64000 0.09000 +v 1.70250 2.99500 0.09000 +v 1.94750 3.40500 0.09000 +v 2.20750 3.67500 0.09000 +v 2.33250 3.76000 0.09000 +v 2.47750 3.82000 0.09000 +v 2.57250 3.83000 0.09000 +v 2.66750 3.80000 0.09000 +v 2.74750 3.73000 0.09000 +v 2.82250 3.61000 0.09000 +v 2.85750 3.61000 0.09000 +v 3.31750 4.18000 0.09000 +v 3.31250 4.22500 0.09000 +v 3.23750 4.34000 0.09000 +v 3.06750 4.49500 0.09000 +v 2.87250 4.57500 0.09000 +v 2.60750 4.59000 0.09000 +v 2.31250 4.52000 0.10000 +v 2.06750 4.40000 0.10000 +v 1.84750 4.24000 0.10000 +v 1.59250 3.98000 0.10000 +v 1.35750 3.65500 0.10000 +v 1.16750 3.30000 0.10000 +v 1.01750 2.90000 0.10000 +v 0.93750 2.57500 0.10000 +v 0.89750 2.16500 0.10000 +v 0.91750 1.86500 0.10000 +v 0.99750 1.58000 0.10000 +v 1.13750 1.34000 0.10000 +v 1.33750 1.17500 0.10000 +v 1.55250 1.10500 0.10000 +v 1.78250 1.12000 0.10000 +v 2.06750 1.22000 0.10000 +v 1.76750 0.20000 0.10000 +v 1.78750 0.14500 0.10000 +v 2.40750 0.38500 0.10000 +v 3.31250 3.18000 0.10000 +v 3.27250 3.21000 0.10000 +v 3.02750 3.16500 0.10000 +v 2.05250 2.91500 0.10000 +v 1.87250 2.39500 0.10000 +v 1.93250 2.37000 0.10000 +v 2.46250 2.50000 0.10000 +v 2.47750 2.47500 0.10000 +v 2.37750 2.22000 0.10000 +v 2.15750 1.96500 0.10000 +v 1.99750 1.85500 0.10000 +v 1.87250 1.80500 0.10000 +v 1.69250 1.81000 0.10000 +v 1.58750 1.89500 0.10000 +v 1.55250 1.95500 0.10000 +v 1.50750 2.24500 0.10000 +v 1.57250 2.64000 0.10000 +v 1.70250 2.99500 0.10000 +v 1.94750 3.40500 0.10000 +v 2.20750 3.67500 0.10000 +v 2.33250 3.76000 0.10000 +v 2.47750 3.82000 0.10000 +v 2.57250 3.83000 0.10000 +v 2.66750 3.80000 0.10000 +v 2.74750 3.73000 0.10000 +v 2.82250 3.61000 0.10000 +v 2.85750 3.61000 0.10000 +v 3.31750 4.18000 0.10000 +v 3.31250 4.22500 0.10000 +v 3.23750 4.34000 0.10000 +v 3.06750 4.49500 0.10000 +v 2.87250 4.57500 0.10000 +v 2.60750 4.59000 0.10000 +v 2.32948 4.47264 0.15000 +v 2.09340 4.35701 0.15000 +v 1.88028 4.20201 0.15000 +v 1.63082 3.94767 0.15000 +v 1.39999 3.62843 0.15000 +v 1.21315 3.27933 0.15000 +v 1.06535 2.88520 0.15000 +v 0.98691 2.56655 0.15000 +v 0.94766 2.16423 0.15000 +v 0.96704 1.87351 0.15000 +v 1.04391 1.59967 0.15000 +v 1.17620 1.37289 0.15000 +v 1.36200 1.21961 0.15000 +v 1.55884 1.15552 0.15000 +v 1.77241 1.16945 0.15000 +v 2.14299 1.29948 0.15000 +v 1.82010 0.20165 0.15000 +v 1.81705 0.21005 0.15000 +v 2.36726 0.42304 0.15000 +v 3.25393 3.16143 0.15000 +v 3.26001 3.15687 0.15000 +v 3.03824 3.11614 0.15000 +v 2.09096 2.87324 0.15000 +v 1.93513 2.42307 0.15000 +v 1.93656 2.42248 0.15000 +v 2.48640 2.55735 0.15000 +v 2.53303 2.47964 0.15000 +v 2.42093 2.19380 0.15000 +v 2.19114 1.92745 0.15000 +v 2.02124 1.81064 0.15000 +v 1.88146 1.75473 0.15000 +v 1.67419 1.76049 0.15000 +v 1.54892 1.86190 0.15000 +v 1.50454 1.93798 0.15000 +v 1.45687 2.24523 0.15000 +v 1.52393 2.65277 0.15000 +v 1.65717 3.01661 0.15000 +v 1.90751 3.43556 0.15000 +v 2.17509 3.71342 0.15000 +v 2.30863 3.80424 0.15000 +v 2.46507 3.86897 0.15000 +v 2.57762 3.88082 0.15000 +v 2.69254 3.84453 0.15000 +v 2.78598 3.76276 0.15000 +v 2.85021 3.66000 0.15000 +v 2.83360 3.66000 0.15000 +v 3.26550 4.19519 0.15000 +v 3.26412 4.20765 0.15000 +v 3.19910 4.30735 0.15000 +v 3.04034 4.45210 0.15000 +v 2.86129 4.52555 0.15000 +v 2.61195 4.53967 0.15000 +v 4.40750 4.43000 -0.15000 +v 3.64750 4.18000 -0.15000 +v 3.45750 3.55500 -0.15000 +v 3.50250 3.53500 -0.15000 +v 3.67750 3.59000 -0.15000 +v 3.69250 3.57000 -0.15000 +v 3.05750 1.28000 -0.15000 +v 2.94250 0.80500 -0.15000 +v 2.95750 0.77500 -0.15000 +v 5.25750 1.44000 -0.15000 +v 5.36250 1.49500 -0.15000 +v 5.60250 2.14000 -0.15000 +v 5.58250 2.18500 -0.15000 +v 4.71750 1.97500 -0.15000 +v 3.87750 1.73500 -0.15000 +v 3.85250 1.76500 -0.15000 +v 4.06250 2.51000 -0.15000 +v 5.03250 2.78500 -0.15000 +v 5.25250 3.30500 -0.15000 +v 5.23750 3.35500 -0.15000 +v 5.18750 3.35500 -0.15000 +v 4.23750 3.13000 -0.15000 +v 4.42250 3.77000 -0.15000 +v 5.69750 4.14000 -0.15000 +v 5.94250 4.73500 -0.15000 +v 5.91750 4.79500 -0.15000 +v 5.88250 4.79500 -0.15000 +v 4.40750 4.43000 0.09000 +v 3.64750 4.18000 0.09000 +v 3.45750 3.55500 0.09000 +v 3.50250 3.53500 0.09000 +v 3.67750 3.59000 0.09000 +v 3.69250 3.57000 0.09000 +v 3.05750 1.28000 0.09000 +v 2.94250 0.80500 0.09000 +v 2.95750 0.77500 0.09000 +v 5.25750 1.44000 0.09000 +v 5.36250 1.49500 0.09000 +v 5.60250 2.14000 0.09000 +v 5.58250 2.18500 0.09000 +v 4.71750 1.97500 0.09000 +v 3.87750 1.73500 0.09000 +v 3.85250 1.76500 0.09000 +v 4.06250 2.51000 0.09000 +v 5.03250 2.78500 0.09000 +v 5.25250 3.30500 0.09000 +v 5.23750 3.35500 0.09000 +v 5.18750 3.35500 0.09000 +v 4.23750 3.13000 0.09000 +v 4.42250 3.77000 0.09000 +v 5.69750 4.14000 0.09000 +v 5.94250 4.73500 0.09000 +v 5.91750 4.79500 0.09000 +v 5.88250 4.79500 0.09000 +v 4.40750 4.43000 0.09000 +v 3.64750 4.18000 0.09000 +v 3.45750 3.55500 0.09000 +v 3.50250 3.53500 0.09000 +v 3.67750 3.59000 0.09000 +v 3.69250 3.57000 0.09000 +v 3.05750 1.28000 0.09000 +v 2.94250 0.80500 0.09000 +v 2.95750 0.77500 0.09000 +v 5.25750 1.44000 0.09000 +v 5.36250 1.49500 0.09000 +v 5.60250 2.14000 0.09000 +v 5.58250 2.18500 0.09000 +v 4.71750 1.97500 0.09000 +v 3.87750 1.73500 0.09000 +v 3.85250 1.76500 0.09000 +v 4.06250 2.51000 0.09000 +v 5.03250 2.78500 0.09000 +v 5.25250 3.30500 0.09000 +v 5.23750 3.35500 0.09000 +v 5.18750 3.35500 0.09000 +v 4.23750 3.13000 0.09000 +v 4.42250 3.77000 0.09000 +v 5.69750 4.14000 0.09000 +v 5.94250 4.73500 0.09000 +v 5.91750 4.79500 0.09000 +v 5.88250 4.79500 0.09000 +v 4.40750 4.43000 0.10000 +v 3.64750 4.18000 0.10000 +v 3.45750 3.55500 0.10000 +v 3.50250 3.53500 0.10000 +v 3.67750 3.59000 0.10000 +v 3.69250 3.57000 0.10000 +v 3.05750 1.28000 0.10000 +v 2.94250 0.80500 0.10000 +v 2.95750 0.77500 0.10000 +v 5.25750 1.44000 0.10000 +v 5.36250 1.49500 0.10000 +v 5.60250 2.14000 0.10000 +v 5.58250 2.18500 0.10000 +v 4.71750 1.97500 0.10000 +v 3.87750 1.73500 0.10000 +v 3.85250 1.76500 0.10000 +v 4.06250 2.51000 0.10000 +v 5.03250 2.78500 0.10000 +v 5.25250 3.30500 0.10000 +v 5.23750 3.35500 0.10000 +v 5.18750 3.35500 0.10000 +v 4.23750 3.13000 0.10000 +v 4.42250 3.77000 0.10000 +v 5.69750 4.14000 0.10000 +v 5.94250 4.73500 0.10000 +v 5.91750 4.79500 0.10000 +v 5.88250 4.79500 0.10000 +v 4.42134 4.38192 0.15000 +v 3.68779 4.14062 0.15000 +v 3.51819 3.58274 0.15000 +v 3.50554 3.58837 0.15000 +v 3.69627 3.64831 0.15000 +v 3.74725 3.58033 0.15000 +v 3.10590 1.26743 0.15000 +v 2.99540 0.81101 0.15000 +v 2.98360 0.83460 0.15000 +v 5.23877 1.48663 0.15000 +v 5.32232 1.53040 0.15000 +v 5.54853 2.13833 0.15000 +v 5.55375 2.12657 0.15000 +v 4.73027 1.92665 0.15000 +v 3.85993 1.67798 0.15000 +v 3.79723 1.75322 0.15000 +v 4.02196 2.55048 0.15000 +v 4.99579 2.82656 0.15000 +v 5.19943 3.30789 0.15000 +v 5.20030 3.30500 0.15000 +v 5.19334 3.30500 0.15000 +v 4.16568 3.06161 0.15000 +v 4.38211 3.81034 0.15000 +v 5.66044 4.18131 0.15000 +v 5.88838 4.73489 0.15000 +v 5.88417 4.74500 0.15000 +v 5.88859 4.74500 0.15000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +vn -0.5618 0.8273 0.0000 +vn -0.6518 0.7584 0.0000 +vn -0.7409 0.6716 0.0000 +vn -0.9839 0.1789 0.0000 +vn -0.7194 -0.6946 0.0000 +vn -0.5470 -0.8371 0.0000 +vn -0.6596 0.7516 0.0000 +vn -0.5455 0.8381 0.0000 +vn -0.7071 0.7071 0.0000 +vn -0.9864 0.1644 0.0000 +vn -0.9568 -0.2907 0.0000 +vn -0.7186 -0.6954 0.0000 +vn 0.2425 -0.9701 0.0000 +vn 0.6360 -0.7717 0.0000 +vn 0.6022 -0.7984 0.0000 +vn 0.6554 -0.7553 0.0000 +vn -0.7198 -0.6941 0.0000 +vn -0.9776 0.2104 0.0000 +vn 0.3634 -0.9316 0.0000 +vn 0.7071 -0.7071 0.0000 +vn -0.7071 -0.7071 0.0000 +vn -0.9799 0.1993 0.0000 +vn 0.3487 -0.9372 0.0000 +vn 0.7194 -0.6946 0.0000 +vn 0.9754 -0.2204 0.0000 +vn 0.6941 -0.7198 0.0000 +vn -0.9270 -0.3749 0.0000 +vn -0.7319 -0.6814 0.0000 +vn -0.2169 -0.9762 0.0000 +vn 0.4524 -0.8918 0.0000 +vn 0.9713 -0.2377 0.0000 +vn -0.8910 -0.4539 0.0000 +vn -0.7145 -0.6996 0.0000 +vn -0.4472 -0.8944 0.0000 +vn -0.0232 -0.9997 0.0000 +vn -0.9623 0.2720 0.0000 +vn 0.3569 -0.9341 0.0000 +vn 0.9545 -0.2983 0.0000 +vn -0.6892 -0.7246 0.0000 +vn 0.2817 -0.9595 0.0000 +vn 0.9394 -0.3429 0.0000 +vn 0.7625 0.6470 0.0000 +vn -0.2491 0.9685 0.0000 +vn 0.7194 0.6946 0.0000 +vn 0.9607 -0.2775 0.0000 +vn 0.2604 -0.9655 0.0000 +vn 0.6950 -0.7190 0.0000 +vn 0.9367 -0.3502 0.0000 +vn 0.6703 0.7421 0.0000 +vn -0.2243 0.9745 0.0000 +vn 0.7071 0.7071 0.0000 +vn 0.9615 -0.2747 0.0000 +vn 0.2688 -0.9632 0.0000 +vn 0.7203 -0.6936 0.0000 +vn 0.9268 -0.3755 0.0000 +vn 0.9923 0.1240 0.0000 +vn -0.2406 0.9706 0.0000 +vn -0.3048 0.9524 0.0000 +vn -0.6847 0.7288 0.0000 +vn 0.7265 0.6872 0.0000 +vn 0.5547 0.8321 0.0000 +vn 0.2524 0.9676 0.0000 +vn -0.0791 0.9969 0.0000 +vn -0.3285 0.9445 0.0000 +vn -0.5049 0.8632 0.0000 +vn -0.6949 0.7191 0.0000 +vn 0.9572 -0.2894 0.0000 +vn 0.6568 0.7541 0.0000 +vn -0.3355 0.9420 0.0000 +vn -0.6936 0.7203 0.0000 +vn -0.9693 0.2460 0.0000 +vn 0.7303 0.6832 0.0000 +vn 0.9337 0.3580 0.0000 +vn 0.6814 0.7319 0.0000 +vn -0.3929 0.9196 0.0000 +vn -0.9701 0.2425 0.0000 +vn 0.7133 -0.7008 0.0000 +vn 0.9507 -0.3100 0.0000 +vn 0.7446 0.6675 0.0000 +vn -0.3405 0.9402 0.0000 +vn -0.7003 0.7138 0.0000 +vn 0.9487 0.3162 0.0000 +vn 0.6449 0.7643 0.0000 +vn -0.4390 0.8985 0.0000 +vn -0.5501 0.8351 0.0000 +vn -0.6332 0.7740 0.0000 +vn -0.7747 0.6324 0.0000 +vn -0.6757 -0.7372 0.0000 +vn -0.5444 -0.8388 0.0000 +vn -0.6178 -0.7863 0.0000 +vn -0.5865 0.8099 0.0000 +vn -0.5487 0.8360 0.0000 +vn -0.9582 -0.2860 0.0000 +vn -0.9114 -0.4116 0.0000 +vn 0.6380 -0.7700 0.0000 +vn 0.6094 -0.7929 0.0000 +vn 0.6720 -0.7406 0.0000 +vn 0.7562 -0.6544 0.0000 +vn 0.9939 0.1104 0.0000 +vn 0.6139 0.7894 0.0000 +vn 0.5221 0.8529 0.0000 +vn 0.6663 -0.7457 0.0000 +vn 0.5570 -0.8305 0.0000 +vn 0.4650 -0.8853 0.0000 +vn 0.9588 0.2841 0.0000 +vn 0.9255 0.3786 0.0000 +vn 0.3511 0.9363 0.0000 +vn -0.3890 0.5905 0.7071 +vn -0.4478 0.5473 0.7071 +vn -0.5000 0.5000 0.7071 +vn -0.5478 0.4472 0.7071 +vn -0.6957 0.1265 0.7071 +vn -0.4778 -0.5212 0.7071 +vn -0.3850 -0.5931 0.7071 +vn -0.4369 -0.5560 0.7071 +vn -0.4147 0.5727 0.7071 +vn -0.3880 0.5912 0.7071 +vn -0.6975 0.1163 0.7071 +vn -0.6776 -0.2023 0.7071 +vn -0.6445 -0.2910 0.7071 +vn 0.1715 -0.6860 0.7071 +vn 0.4511 -0.5445 0.7071 +vn 0.4309 -0.5606 0.7071 +vn 0.4752 -0.5237 0.7071 +vn 0.5347 -0.4627 0.7071 +vn 0.7028 0.0781 0.7071 +vn 0.5000 0.5000 0.7071 +vn 0.4341 0.5582 0.7071 +vn 0.3692 0.6031 0.7071 +vn 0.4712 -0.5273 0.7071 +vn 0.3938 -0.5873 0.7071 +vn 0.3288 -0.6260 0.7071 +vn 0.6780 0.2009 0.7071 +vn 0.6544 0.2677 0.7071 +vn 0.2483 0.6621 0.7071 +vn -0.3104 0.6353 0.7071 +vn -0.6402 0.7682 0.0000 +vn -0.9753 0.2209 0.0000 +vn -0.9829 0.1843 0.0000 +vn -0.9138 -0.4061 0.0000 +vn 0.3557 -0.9346 0.0000 +vn 0.4692 -0.8831 0.0000 +vn 0.9739 -0.2271 0.0000 +vn 0.9684 -0.2492 0.0000 +vn 0.1644 0.9864 0.0000 +vn -0.3497 0.9369 0.0000 +vn -0.4527 0.5432 0.7071 +vn -0.6897 0.1562 0.7071 +vn -0.6950 0.1303 0.7071 +vn -0.6462 -0.2872 0.7071 +vn 0.2515 -0.6609 0.7071 +vn 0.3317 -0.6245 0.7071 +vn 0.6886 -0.1606 0.7071 +vn 0.6848 -0.1762 0.7071 +vn 0.1158 0.6976 0.7070 +vn -0.2473 0.6625 0.7071 +vn -0.7682 0.6402 0.0000 +vn -0.9733 0.2294 0.0000 +vn -0.3511 -0.9363 0.0000 +vn 0.3304 -0.9439 0.0000 +vn 0.3779 -0.9258 0.0000 +vn 0.9439 -0.3304 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.3162 0.9487 0.0000 +vn -0.3534 0.9355 0.0000 +vn -0.5433 0.4526 0.7071 +vn -0.6883 0.1622 0.7071 +vn -0.2481 -0.6622 0.7071 +vn 0.2336 -0.6674 0.7071 +vn 0.2672 -0.6547 0.7071 +vn 0.6674 -0.2336 0.7071 +vn 0.7071 0.0000 0.7071 +vn 0.2240 0.6706 0.7071 +vn -0.2499 0.6615 0.7071 +vn -0.9716 0.2365 0.0000 +vn 0.9317 0.3633 0.0000 +vn 0.0767 0.9971 0.0000 +vn -0.4158 0.9095 0.0000 +vn -0.9736 0.2283 0.0000 +vn -0.9759 0.2181 0.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.3766 -0.9264 0.0000 +vn 0.9728 -0.2315 0.0000 +vn -0.9248 -0.3805 0.0000 +vn 0.4668 -0.8844 0.0000 +vn 0.9684 -0.2495 0.0000 +vn 0.9572 -0.2893 0.0000 +vn 0.9950 -0.0995 0.0000 +vn 0.3939 0.9191 0.0000 +vn -0.3532 0.9356 0.0000 +vn -0.7105 0.1742 0.6818 +vn 0.6809 0.2669 0.6819 +vn 0.0542 0.7050 0.7071 +vn -0.2940 0.6431 0.7071 +vn -0.6885 0.1614 0.7071 +vn -0.6901 0.1542 0.7071 +vn -0.7071 0.0000 0.7071 +vn 0.0000 -0.7071 0.7071 +vn 0.2663 -0.6551 0.7071 +vn 0.7083 -0.1697 0.6852 +vn -0.6731 -0.2781 0.6852 +vn -0.1533 -0.6903 0.7071 +vn 0.3300 -0.6253 0.7071 +vn 0.6847 -0.1764 0.7071 +vn 0.6769 -0.2046 0.7071 +vn 0.7036 -0.0704 0.7071 +vn 0.2785 0.6499 0.7071 +vn -0.2497 0.6615 0.7071 +vn -0.4399 0.8981 0.0000 +vn -0.5882 0.8087 0.0000 +vn -0.7139 0.7002 0.0000 +vn -0.8104 0.5859 0.0000 +vn -0.8817 0.4719 0.0000 +vn -0.9363 0.3511 0.0000 +vn -0.9710 0.2390 0.0000 +vn -0.9953 0.0971 0.0000 +vn -0.9978 -0.0665 0.0000 +vn -0.9628 -0.2703 0.0000 +vn -0.8638 -0.5039 0.0000 +vn -0.6364 -0.7714 0.0000 +vn -0.3096 -0.9509 0.0000 +vn 0.0651 -0.9979 0.0000 +vn 0.3311 -0.9436 0.0000 +vn -0.9594 0.2822 0.0000 +vn -0.9398 -0.3417 0.0000 +vn 0.3610 -0.9326 0.0000 +vn 0.9514 -0.3080 0.0000 +vn 0.6000 0.8000 0.0000 +vn -0.1807 0.9835 0.0000 +vn -0.2484 0.9687 0.0000 +vn -0.9450 0.3271 0.0000 +vn -0.3846 -0.9231 0.0000 +vn 0.2382 -0.9712 0.0000 +vn -0.8575 -0.5145 0.0000 +vn -0.9310 0.3651 0.0000 +vn -0.7572 0.6532 0.0000 +vn -0.5665 0.8240 0.0000 +vn -0.3714 0.9285 0.0000 +vn 0.0278 0.9996 0.0000 +vn 0.6292 0.7772 0.0000 +vn 0.8638 0.5039 0.0000 +vn 0.9882 0.1533 0.0000 +vn 0.9867 -0.1624 0.0000 +vn 0.9390 -0.3439 0.0000 +vn 0.8584 -0.5130 0.0000 +vn 0.5623 -0.8269 0.0000 +vn 0.3824 -0.9240 0.0000 +vn 0.1047 -0.9945 0.0000 +vn -0.3011 -0.9536 0.0000 +vn -0.6585 -0.7526 0.0000 +vn -0.8480 -0.5300 0.0000 +vn 0.7782 -0.6280 0.0000 +vn 0.8376 0.5463 0.0000 +vn 0.6738 0.7390 0.0000 +vn 0.3796 0.9252 0.0000 +vn 0.0565 0.9984 0.0000 +vn -0.2309 0.9730 0.0000 +vn -0.3110 0.6350 0.7071 +vn -0.4159 0.5718 0.7071 +vn -0.5048 0.4951 0.7071 +vn -0.5730 0.4143 0.7071 +vn -0.6234 0.3337 0.7071 +vn -0.6621 0.2483 0.7071 +vn -0.6866 0.1690 0.7071 +vn -0.7038 0.0687 0.7071 +vn -0.7056 -0.0470 0.7071 +vn -0.6808 -0.1911 0.7071 +vn -0.6108 -0.3563 0.7071 +vn -0.4500 -0.5454 0.7071 +vn -0.2189 -0.6724 0.7071 +vn 0.0460 -0.7056 0.7071 +vn 0.2341 -0.6672 0.7071 +vn -0.6784 0.1995 0.7071 +vn -0.6645 -0.2417 0.7071 +vn 0.2553 -0.6594 0.7071 +vn 0.6727 -0.2178 0.7071 +vn 0.4243 0.5657 0.7071 +vn -0.1277 0.6955 0.7071 +vn -0.1756 0.6849 0.7071 +vn -0.6682 0.2313 0.7071 +vn -0.2719 -0.6527 0.7071 +vn 0.1684 -0.6867 0.7071 +vn -0.6063 -0.3638 0.7071 +vn -0.6583 0.2582 0.7071 +vn -0.5354 0.4619 0.7071 +vn -0.4006 0.5827 0.7071 +vn -0.2626 0.6565 0.7071 +vn 0.0196 0.7068 0.7071 +vn 0.4449 0.5496 0.7071 +vn 0.6108 0.3563 0.7071 +vn 0.6988 0.1084 0.7071 +vn 0.6977 -0.1148 0.7071 +vn 0.6640 -0.2432 0.7071 +vn 0.6070 -0.3627 0.7071 +vn 0.5093 -0.4905 0.7071 +vn 0.3976 -0.5847 0.7071 +vn 0.2703 -0.6534 0.7071 +vn 0.0740 -0.7032 0.7071 +vn -0.2129 -0.6743 0.7071 +vn -0.4657 -0.5322 0.7071 +vn -0.5996 -0.3748 0.7071 +vn 0.5503 -0.4441 0.7071 +vn 0.7028 0.0780 0.7071 +vn 0.5923 0.3863 0.7071 +vn 0.4764 0.5225 0.7071 +vn 0.2684 0.6542 0.7071 +vn 0.0400 0.7060 0.7071 +vn -0.1633 0.6880 0.7071 +vn -0.3125 0.9499 0.0000 +vn -0.9568 0.2909 0.0000 +vn -0.4061 -0.9138 0.0000 +vn 0.2998 -0.9540 0.0000 +vn -0.8000 -0.6000 0.0000 +vn -0.9636 0.2672 0.0000 +vn -0.9719 0.2353 0.0000 +vn -0.8944 -0.4472 0.0000 +vn 0.2778 -0.9607 0.0000 +vn 0.4640 -0.8858 0.0000 +vn 0.9372 -0.3487 0.0000 +vn 0.9138 0.4061 0.0000 +vn -0.2359 0.9718 0.0000 +vn -0.2747 0.9615 0.0000 +vn 0.7682 0.6402 0.0000 +vn 0.9625 -0.2713 0.0000 +vn 0.2728 -0.9621 0.0000 +vn 0.9210 -0.3896 0.0000 +vn 0.9578 0.2873 0.0000 +vn 0.0000 1.0000 0.0000 +vn -0.2305 0.9731 0.0000 +vn 0.9607 -0.2777 0.0000 +vn 0.2787 -0.9604 0.0000 +vn 0.9247 -0.3807 0.0000 +vn 0.9231 0.3846 0.0000 +vn -0.2402 0.9707 0.0000 +vn -0.2210 0.6717 0.7071 +vn -0.6765 0.2057 0.7071 +vn -0.2871 -0.6462 0.7071 +vn 0.2120 -0.6746 0.7071 +vn -0.5657 -0.4242 0.7071 +vn -0.6814 0.1889 0.7071 +vn -0.6873 0.1664 0.7071 +vn -0.6327 -0.3159 0.7071 +vn 0.1964 -0.6793 0.7071 +vn 0.3281 -0.6264 0.7071 +vn 0.6627 -0.2466 0.7071 +vn 0.6461 0.2873 0.7071 +vn -0.1668 0.6872 0.7071 +vn -0.1943 0.6799 0.7071 +vn 0.5432 0.4527 0.7071 +vn 0.6806 -0.1918 0.7071 +vn 0.1929 -0.6803 0.7071 +vn 0.6512 -0.2755 0.7071 +vn 0.6773 0.2031 0.7071 +vn 0.0000 0.7071 0.7071 +vn -0.1630 0.6881 0.7071 +vn 0.6793 -0.1964 0.7071 +vn 0.1971 -0.6791 0.7071 +vn 0.6539 -0.2692 0.7071 +vn 0.6527 0.2720 0.7071 +vn -0.1699 0.6864 0.7071 +o plate +usemtl champagne +f 1//1 105//1 104//1 +f 209//2 210//2 106//2 +f 1//1 104//1 103//1 +f 208//2 209//2 106//2 +f 1//1 103//1 102//1 +f 207//2 208//2 106//2 +f 102//1 101//1 100//1 +f 205//2 206//2 207//2 +f 102//1 100//1 99//1 +f 204//2 205//2 207//2 +f 102//1 99//1 98//1 +f 203//2 204//2 207//2 +f 102//1 98//1 97//1 +f 202//2 203//2 207//2 +f 1//1 102//1 97//1 +f 202//2 207//2 106//2 +f 1//1 97//1 96//1 +f 201//2 202//2 106//2 +f 95//1 94//1 93//1 +f 198//2 199//2 200//2 +f 95//1 93//1 92//1 +f 197//2 198//2 200//2 +f 96//1 95//1 92//1 +f 197//2 200//2 201//2 +f 96//1 92//1 91//1 +f 196//2 197//2 201//2 +f 96//1 91//1 90//1 +f 195//2 196//2 201//2 +f 96//1 90//1 89//1 +f 194//2 195//2 201//2 +f 88//1 87//1 86//1 +f 191//2 192//2 193//2 +f 88//1 86//1 85//1 +f 190//2 191//2 193//2 +f 88//1 85//1 84//1 +f 189//2 190//2 193//2 +f 88//1 84//1 83//1 +f 188//2 189//2 193//2 +f 89//1 88//1 83//1 +f 188//2 193//2 194//2 +f 89//1 83//1 82//1 +f 187//2 188//2 194//2 +f 82//1 81//1 80//1 +f 185//2 186//2 187//2 +f 82//1 80//1 79//1 +f 184//2 185//2 187//2 +f 82//1 79//1 78//1 +f 183//2 184//2 187//2 +f 82//1 78//1 77//1 +f 182//2 183//2 187//2 +f 89//1 82//1 77//1 +f 182//2 187//2 194//2 +f 89//1 77//1 76//1 +f 181//2 182//2 194//2 +f 89//1 76//1 75//1 +f 180//2 181//2 194//2 +f 75//1 74//1 73//1 +f 178//2 179//2 180//2 +f 75//1 73//1 72//1 +f 177//2 178//2 180//2 +f 75//1 72//1 71//1 +f 176//2 177//2 180//2 +f 89//1 75//1 71//1 +f 176//2 180//2 194//2 +f 89//1 71//1 70//1 +f 175//2 176//2 194//2 +f 89//1 70//1 69//1 +f 174//2 175//2 194//2 +f 89//1 69//1 68//1 +f 173//2 174//2 194//2 +f 89//1 68//1 67//1 +f 172//2 173//2 194//2 +f 89//1 67//1 66//1 +f 171//2 172//2 194//2 +f 89//1 66//1 65//1 +f 170//2 171//2 194//2 +f 89//1 65//1 64//1 +f 169//2 170//2 194//2 +f 64//1 63//1 62//1 +f 167//2 168//2 169//2 +f 89//1 64//1 62//1 +f 167//2 169//2 194//2 +f 89//1 62//1 61//1 +f 166//2 167//2 194//2 +f 89//1 61//1 60//1 +f 165//2 166//2 194//2 +f 89//1 60//1 59//1 +f 164//2 165//2 194//2 +f 89//1 59//1 58//1 +f 163//2 164//2 194//2 +f 89//1 58//1 57//1 +f 162//2 163//2 194//2 +f 89//1 57//1 56//1 +f 161//2 162//2 194//2 +f 56//1 55//1 54//1 +f 159//2 160//2 161//2 +f 56//1 54//1 53//1 +f 158//2 159//2 161//2 +f 89//1 56//1 53//1 +f 158//2 161//2 194//2 +f 89//1 53//1 52//1 +f 157//2 158//2 194//2 +f 52//1 51//1 50//1 +f 155//2 156//2 157//2 +f 89//1 52//1 50//1 +f 155//2 157//2 194//2 +f 96//1 89//1 50//1 +f 155//2 194//2 201//2 +f 1//1 96//1 50//1 +f 155//2 201//2 106//2 +f 1//1 50//1 49//1 +f 154//2 155//2 106//2 +f 49//1 48//1 47//1 +f 152//2 153//2 154//2 +f 49//1 47//1 46//1 +f 151//2 152//2 154//2 +f 49//1 46//1 45//1 +f 150//2 151//2 154//2 +f 49//1 45//1 44//1 +f 149//2 150//2 154//2 +f 49//1 44//1 43//1 +f 148//2 149//2 154//2 +f 43//1 42//1 41//1 +f 146//2 147//2 148//2 +f 43//1 41//1 40//1 +f 145//2 146//2 148//2 +f 49//1 43//1 40//1 +f 145//2 148//2 154//2 +f 49//1 40//1 39//1 +f 144//2 145//2 154//2 +f 49//1 39//1 38//1 +f 143//2 144//2 154//2 +f 1//1 49//1 38//1 +f 143//2 154//2 106//2 +f 1//1 38//1 37//1 +f 142//2 143//2 106//2 +f 1//1 37//1 36//1 +f 141//2 142//2 106//2 +f 36//1 35//1 34//1 +f 139//2 140//2 141//2 +f 36//1 34//1 33//1 +f 138//2 139//2 141//2 +f 36//1 33//1 32//1 +f 137//2 138//2 141//2 +f 36//1 32//1 31//1 +f 136//2 137//2 141//2 +f 36//1 31//1 30//1 +f 135//2 136//2 141//2 +f 1//1 36//1 30//1 +f 135//2 141//2 106//2 +f 1//1 30//1 29//1 +f 134//2 135//2 106//2 +f 1//1 29//1 28//1 +f 133//2 134//2 106//2 +f 1//1 28//1 27//1 +f 132//2 133//2 106//2 +f 27//1 26//1 25//1 +f 130//2 131//2 132//2 +f 27//1 25//1 24//1 +f 129//2 130//2 132//2 +f 1//1 27//1 24//1 +f 129//2 132//2 106//2 +f 1//1 24//1 23//1 +f 128//2 129//2 106//2 +f 1//1 23//1 22//1 +f 127//2 128//2 106//2 +f 22//1 21//1 20//1 +f 125//2 126//2 127//2 +f 22//1 20//1 19//1 +f 124//2 125//2 127//2 +f 1//1 22//1 19//1 +f 124//2 127//2 106//2 +f 1//1 19//1 18//1 +f 123//2 124//2 106//2 +f 1//1 18//1 17//1 +f 122//2 123//2 106//2 +f 1//1 17//1 16//1 +f 121//2 122//2 106//2 +f 16//1 15//1 14//1 +f 119//2 120//2 121//2 +f 16//1 14//1 13//1 +f 118//2 119//2 121//2 +f 16//1 13//1 12//1 +f 117//2 118//2 121//2 +f 16//1 12//1 11//1 +f 116//2 117//2 121//2 +f 16//1 11//1 10//1 +f 115//2 116//2 121//2 +f 16//1 10//1 9//1 +f 114//2 115//2 121//2 +f 16//1 9//1 8//1 +f 113//2 114//2 121//2 +f 1//1 16//1 8//1 +f 113//2 121//2 106//2 +f 1//1 8//1 7//1 +f 112//2 113//2 106//2 +f 1//1 7//1 6//1 +f 111//2 112//2 106//2 +f 1//1 6//1 5//1 +f 110//2 111//2 106//2 +f 1//1 5//1 4//1 +f 109//2 110//2 106//2 +f 1//1 4//1 3//1 +f 108//2 109//2 106//2 +f 3//1 2//1 1//1 +f 106//2 107//2 108//2 +f 1//3 2//3 107//3 106//3 +f 2//4 3//4 108//4 107//4 +f 3//5 4//5 109//5 108//5 +f 4//6 5//6 110//6 109//6 +f 5//7 6//7 111//7 110//7 +f 6//8 7//8 112//8 111//8 +f 7//7 8//7 113//7 112//7 +f 8//9 9//9 114//9 113//9 +f 9//10 10//10 115//10 114//10 +f 10//11 11//11 116//11 115//11 +f 11//12 12//12 117//12 116//12 +f 12//13 13//13 118//13 117//13 +f 13//14 14//14 119//14 118//14 +f 14//15 15//15 120//15 119//15 +f 15//16 16//16 121//16 120//16 +f 16//17 17//17 122//17 121//17 +f 17//18 18//18 123//18 122//18 +f 18//19 19//19 124//19 123//19 +f 19//20 20//20 125//20 124//20 +f 20//7 21//7 126//7 125//7 +f 21//21 22//21 127//21 126//21 +f 22//22 23//22 128//22 127//22 +f 23//23 24//23 129//23 128//23 +f 24//24 25//24 130//24 129//24 +f 25//23 26//23 131//23 130//23 +f 26//25 27//25 132//25 131//25 +f 27//26 28//26 133//26 132//26 +f 28//27 29//27 134//27 133//27 +f 29//28 30//28 135//28 134//28 +f 30//23 31//23 136//23 135//23 +f 31//29 32//29 137//29 136//29 +f 32//30 33//30 138//30 137//30 +f 33//31 34//31 139//31 138//31 +f 34//32 35//32 140//32 139//32 +f 35//22 36//22 141//22 140//22 +f 36//33 37//33 142//33 141//33 +f 37//26 38//26 143//26 142//26 +f 38//23 39//23 144//23 143//23 +f 39//34 40//34 145//34 144//34 +f 40//35 41//35 146//35 145//35 +f 41//36 42//36 147//36 146//36 +f 42//37 43//37 148//37 147//37 +f 43//23 44//23 149//23 148//23 +f 44//38 45//38 150//38 149//38 +f 45//23 46//23 151//23 150//23 +f 46//39 47//39 152//39 151//39 +f 47//22 48//22 153//22 152//22 +f 48//40 49//40 154//40 153//40 +f 49//22 50//22 155//22 154//22 +f 50//41 51//41 156//41 155//41 +f 51//42 52//42 157//42 156//42 +f 52//22 53//22 158//22 157//22 +f 53//43 54//43 159//43 158//43 +f 54//44 55//44 160//44 159//44 +f 55//45 56//45 161//45 160//45 +f 56//46 57//46 162//46 161//46 +f 57//47 58//47 163//47 162//47 +f 58//22 59//22 164//22 163//22 +f 59//48 60//48 165//48 164//48 +f 60//49 61//49 166//49 165//49 +f 61//50 62//50 167//50 166//50 +f 62//51 63//51 168//51 167//51 +f 63//52 64//52 169//52 168//52 +f 64//53 65//53 170//53 169//53 +f 65//54 66//54 171//54 170//54 +f 66//26 67//26 172//26 171//26 +f 67//55 68//55 173//55 172//55 +f 68//56 69//56 174//56 173//56 +f 69//57 70//57 175//57 174//57 +f 70//58 71//58 176//58 175//58 +f 71//53 72//53 177//53 176//53 +f 72//59 73//59 178//59 177//59 +f 73//60 74//60 179//60 178//60 +f 74//61 75//61 180//61 179//61 +f 75//62 76//62 181//62 180//62 +f 76//63 77//63 182//63 181//63 +f 77//64 78//64 183//64 182//64 +f 78//65 79//65 184//65 183//65 +f 79//66 80//66 185//66 184//66 +f 80//67 81//67 186//67 185//67 +f 81//68 82//68 187//68 186//68 +f 82//53 83//53 188//53 187//53 +f 83//69 84//69 189//69 188//69 +f 84//70 85//70 190//70 189//70 +f 85//71 86//71 191//71 190//71 +f 86//72 87//72 192//72 191//72 +f 87//73 88//73 193//73 192//73 +f 88//11 89//11 194//11 193//11 +f 89//74 90//74 195//74 194//74 +f 90//75 91//75 196//75 195//75 +f 91//76 92//76 197//76 196//76 +f 92//77 93//77 198//77 197//77 +f 93//11 94//11 199//11 198//11 +f 94//78 95//78 200//78 199//78 +f 95//11 96//11 201//11 200//11 +f 96//53 97//53 202//53 201//53 +f 97//79 98//79 203//79 202//79 +f 98//80 99//80 204//80 203//80 +f 99//81 100//81 205//81 204//81 +f 100//82 101//82 206//82 205//82 +f 101//83 102//83 207//83 206//83 +f 102//53 103//53 208//53 207//53 +f 103//84 104//84 209//84 208//84 +f 104//85 105//85 210//85 209//85 +f 105//86 1//86 106//86 210//86 +o letter01Walls +usemtl frame +f 211//1 239//1 238//1 +f 267//2 268//2 240//2 +f 211//1 238//1 237//1 +f 266//2 267//2 240//2 +f 211//1 237//1 236//1 +f 265//2 266//2 240//2 +f 211//1 236//1 235//1 +f 264//2 265//2 240//2 +f 211//1 235//1 234//1 +f 263//2 264//2 240//2 +f 211//1 234//1 233//1 +f 262//2 263//2 240//2 +f 233//1 232//1 231//1 +f 260//2 261//2 262//2 +f 233//1 231//1 230//1 +f 259//2 260//2 262//2 +f 233//1 230//1 229//1 +f 258//2 259//2 262//2 +f 233//1 229//1 228//1 +f 257//2 258//2 262//2 +f 233//1 228//1 227//1 +f 256//2 257//2 262//2 +f 226//1 225//1 224//1 +f 253//2 254//2 255//2 +f 226//1 224//1 223//1 +f 252//2 253//2 255//2 +f 227//1 226//1 223//1 +f 252//2 255//2 256//2 +f 227//1 223//1 222//1 +f 251//2 252//2 256//2 +f 227//1 222//1 221//1 +f 250//2 251//2 256//2 +f 227//1 221//1 220//1 +f 249//2 250//2 256//2 +f 227//1 220//1 219//1 +f 248//2 249//2 256//2 +f 233//1 227//1 219//1 +f 248//2 256//2 262//2 +f 233//1 219//1 218//1 +f 247//2 248//2 262//2 +f 233//1 218//1 217//1 +f 246//2 247//2 262//2 +f 211//1 233//1 217//1 +f 246//2 262//2 240//2 +f 211//1 217//1 216//1 +f 245//2 246//2 240//2 +f 211//1 216//1 215//1 +f 244//2 245//2 240//2 +f 211//1 215//1 214//1 +f 243//2 244//2 240//2 +f 211//1 214//1 213//1 +f 242//2 243//2 240//2 +f 213//1 212//1 211//1 +f 240//2 241//2 242//2 +f 211//87 212//87 241//87 240//87 +f 212//88 213//88 242//88 241//88 +f 213//11 214//11 243//11 242//11 +f 214//89 215//89 244//89 243//89 +f 215//6 216//6 245//6 244//6 +f 216//90 217//90 246//90 245//90 +f 217//91 218//91 247//91 246//91 +f 218//92 219//92 248//92 247//92 +f 219//93 220//93 249//93 248//93 +f 220//94 221//94 250//94 249//94 +f 221//12 222//12 251//12 250//12 +f 222//95 223//95 252//95 251//95 +f 223//96 224//96 253//96 252//96 +f 224//15 225//15 254//15 253//15 +f 225//97 226//97 255//97 254//97 +f 226//98 227//98 256//98 255//98 +f 227//99 228//99 257//99 256//99 +f 228//100 229//100 258//100 257//100 +f 229//101 230//101 259//101 258//101 +f 230//53 231//53 260//53 259//53 +f 231//102 232//102 261//102 260//102 +f 232//103 233//103 262//103 261//103 +f 233//104 234//104 263//104 262//104 +f 234//105 235//105 264//105 263//105 +f 235//106 236//106 265//106 264//106 +f 236//107 237//107 266//107 265//107 +f 237//108 238//108 267//108 266//108 +f 238//109 239//109 268//109 267//109 +f 239//86 211//86 240//86 268//86 +o letter01Face +usemtl dark +f 269//1 297//1 296//1 +f 354//1 355//1 327//1 +f 269//1 296//1 295//1 +f 353//2 354//2 327//2 +f 269//1 295//1 294//1 +f 352//2 353//2 327//2 +f 269//1 294//1 293//1 +f 351//2 352//2 327//2 +f 269//1 293//1 292//1 +f 350//2 351//2 327//2 +f 269//1 292//1 291//1 +f 349//2 350//2 327//2 +f 291//1 290//1 289//1 +f 347//2 348//2 349//2 +f 291//1 289//1 288//1 +f 346//2 347//2 349//2 +f 291//1 288//1 287//1 +f 345//2 346//2 349//2 +f 291//1 287//1 286//1 +f 344//2 345//2 349//2 +f 291//1 286//1 285//1 +f 343//2 344//2 349//2 +f 284//1 283//1 282//1 +f 340//2 341//2 342//2 +f 284//1 282//1 281//1 +f 339//2 340//2 342//2 +f 285//1 284//1 281//1 +f 339//2 342//2 343//2 +f 285//1 281//1 280//1 +f 338//2 339//2 343//2 +f 285//1 280//1 279//1 +f 337//2 338//2 343//2 +f 285//1 279//1 278//1 +f 336//2 337//2 343//2 +f 285//1 278//1 277//1 +f 335//2 336//2 343//2 +f 291//1 285//1 277//1 +f 335//2 343//2 349//2 +f 291//1 277//1 276//1 +f 334//2 335//2 349//2 +f 291//1 276//1 275//1 +f 333//2 334//2 349//2 +f 269//1 291//1 275//1 +f 333//2 349//2 327//2 +f 269//1 275//1 274//1 +f 332//2 333//2 327//2 +f 269//1 274//1 273//1 +f 331//2 332//2 327//2 +f 269//1 273//1 272//1 +f 330//2 331//2 327//2 +f 269//1 272//1 271//1 +f 329//2 330//2 327//2 +f 271//1 270//1 269//1 +f 327//2 328//2 329//2 +f 269//87 270//87 299//87 298//87 +f 270//88 271//88 300//88 299//88 +f 271//11 272//11 301//11 300//11 +f 272//89 273//89 302//89 301//89 +f 273//6 274//6 303//6 302//6 +f 274//90 275//90 304//90 303//90 +f 275//91 276//91 305//91 304//91 +f 276//92 277//92 306//92 305//92 +f 277//93 278//93 307//93 306//93 +f 278//94 279//94 308//94 307//94 +f 279//12 280//12 309//12 308//12 +f 280//95 281//95 310//95 309//95 +f 281//96 282//96 311//96 310//96 +f 282//15 283//15 312//15 311//15 +f 283//97 284//97 313//97 312//97 +f 284//98 285//98 314//98 313//98 +f 285//99 286//99 315//99 314//99 +f 286//100 287//100 316//100 315//100 +f 287//101 288//101 317//101 316//101 +f 288//53 289//53 318//53 317//53 +f 289//102 290//102 319//102 318//102 +f 290//103 291//103 320//103 319//103 +f 291//104 292//104 321//104 320//104 +f 292//105 293//105 322//105 321//105 +f 293//106 294//106 323//106 322//106 +f 294//107 295//107 324//107 323//107 +f 295//108 296//108 325//108 324//108 +f 296//109 297//109 326//109 325//109 +f 297//86 269//86 298//86 326//86 +f 298//110 299//110 328//110 327//110 +f 299//111 300//111 329//111 328//111 +f 300//112 301//112 330//112 329//112 +f 301//113 302//113 331//113 330//113 +f 302//114 303//114 332//114 331//114 +f 303//115 304//115 333//115 332//115 +f 304//116 305//116 334//116 333//116 +f 305//117 306//117 335//117 334//117 +f 306//118 307//118 336//118 335//118 +f 307//119 308//119 337//119 336//119 +f 308//120 309//120 338//120 337//120 +f 309//121 310//121 339//121 338//121 +f 310//122 311//122 340//122 339//122 +f 311//123 312//123 341//123 340//123 +f 312//124 313//124 342//124 341//124 +f 313//125 314//125 343//125 342//125 +f 314//126 315//126 344//126 343//126 +f 315//127 316//127 345//127 344//127 +f 316//128 317//128 346//128 345//128 +f 317//129 318//129 347//129 346//129 +f 318//130 319//130 348//130 347//130 +f 319//131 320//131 349//131 348//131 +f 320//132 321//132 350//132 349//132 +f 321//133 322//133 351//133 350//133 +f 322//134 323//134 352//134 351//134 +f 323//135 324//135 353//135 352//135 +f 324//136 325//136 354//136 353//136 +f 325//137 326//137 355//137 354//137 +f 326//138 298//138 327//138 355//138 +o letter02Walls +usemtl frame +f 356//1 366//1 365//1 +f 376//2 377//2 367//2 +f 356//1 365//1 364//1 +f 375//2 376//2 367//2 +f 356//1 364//1 363//1 +f 374//2 375//2 367//2 +f 356//1 363//1 362//1 +f 373//2 374//2 367//2 +f 356//1 362//1 361//1 +f 372//2 373//2 367//2 +f 356//1 361//1 360//1 +f 371//2 372//2 367//2 +f 356//1 360//1 359//1 +f 370//2 371//2 367//2 +f 356//1 359//1 358//1 +f 369//2 370//2 367//2 +f 358//1 357//1 356//1 +f 367//2 368//2 369//2 +f 356//139 357//139 368//139 367//139 +f 357//140 358//140 369//140 368//140 +f 358//141 359//141 370//141 369//141 +f 359//142 360//142 371//142 370//142 +f 360//143 361//143 372//143 371//143 +f 361//144 362//144 373//144 372//144 +f 362//145 363//145 374//145 373//145 +f 363//146 364//146 375//146 374//146 +f 364//101 365//101 376//101 375//101 +f 365//147 366//147 377//147 376//147 +f 366//148 356//148 367//148 377//148 +o letter02Face +usemtl dark +f 378//1 388//1 387//1 +f 409//1 410//1 400//1 +f 378//1 387//1 386//1 +f 408//1 409//1 400//1 +f 378//1 386//1 385//1 +f 407//2 408//2 400//2 +f 378//1 385//1 384//1 +f 406//2 407//2 400//2 +f 378//1 384//1 383//1 +f 405//2 406//2 400//2 +f 378//1 383//1 382//1 +f 404//2 405//2 400//2 +f 378//1 382//1 381//1 +f 403//1 404//1 400//1 +f 378//1 381//1 380//1 +f 402//2 403//2 400//2 +f 380//1 379//1 378//1 +f 400//2 401//2 402//2 +f 378//139 379//139 390//139 389//139 +f 379//140 380//140 391//140 390//140 +f 380//141 381//141 392//141 391//141 +f 381//142 382//142 393//142 392//142 +f 382//143 383//143 394//143 393//143 +f 383//144 384//144 395//144 394//144 +f 384//145 385//145 396//145 395//145 +f 385//146 386//146 397//146 396//146 +f 386//101 387//101 398//101 397//101 +f 387//147 388//147 399//147 398//147 +f 388//148 378//148 389//148 399//148 +f 389//149 390//149 401//149 400//149 +f 390//150 391//150 402//150 401//150 +f 391//151 392//151 403//151 402//151 +f 392//152 393//152 404//152 403//152 +f 393//153 394//153 405//153 404//153 +f 394//154 395//154 406//154 405//154 +f 395//155 396//155 407//155 406//155 +f 396//156 397//156 408//156 407//156 +f 397//128 398//128 409//128 408//128 +f 398//157 399//157 410//157 409//157 +f 399//158 389//158 400//158 410//158 +o letter03Walls +usemtl frame +f 411//1 419//1 418//1 +f 427//2 428//2 420//2 +f 411//1 418//1 417//1 +f 426//2 427//2 420//2 +f 411//1 417//1 416//1 +f 425//2 426//2 420//2 +f 411//1 416//1 415//1 +f 424//2 425//2 420//2 +f 411//1 415//1 414//1 +f 423//2 424//2 420//2 +f 411//1 414//1 413//1 +f 422//2 423//2 420//2 +f 413//1 412//1 411//1 +f 420//2 421//2 422//2 +f 411//159 412//159 421//159 420//159 +f 412//160 413//160 422//160 421//160 +f 413//161 414//161 423//161 422//161 +f 414//162 415//162 424//162 423//162 +f 415//163 416//163 425//163 424//163 +f 416//164 417//164 426//164 425//164 +f 417//165 418//165 427//165 426//165 +f 418//166 419//166 428//166 427//166 +f 419//167 411//167 420//167 428//167 +o letter03Face +usemtl dark +f 429//1 437//1 436//1 +f 454//1 455//1 447//1 +f 429//1 436//1 435//1 +f 453//1 454//1 447//1 +f 429//1 435//1 434//1 +f 452//2 453//2 447//2 +f 429//1 434//1 433//1 +f 451//2 452//2 447//2 +f 429//1 433//1 432//1 +f 450//2 451//2 447//2 +f 429//1 432//1 431//1 +f 449//1 450//1 447//1 +f 431//1 430//1 429//1 +f 447//2 448//2 449//2 +f 429//159 430//159 439//159 438//159 +f 430//160 431//160 440//160 439//160 +f 431//161 432//161 441//161 440//161 +f 432//162 433//162 442//162 441//162 +f 433//163 434//163 443//163 442//163 +f 434//164 435//164 444//164 443//164 +f 435//165 436//165 445//165 444//165 +f 436//166 437//166 446//166 445//166 +f 437//167 429//167 438//167 446//167 +f 438//168 439//168 448//168 447//168 +f 439//169 440//169 449//169 448//169 +f 440//170 441//170 450//170 449//170 +f 441//171 442//171 451//171 450//171 +f 442//172 443//172 452//172 451//172 +f 443//173 444//173 453//173 452//173 +f 444//174 445//174 454//174 453//174 +f 445//175 446//175 455//175 454//175 +f 446//176 438//176 447//176 455//176 +o letter04Walls +usemtl frame +f 456//1 473//1 472//1 +f 490//2 491//2 474//2 +f 456//1 472//1 471//1 +f 489//2 490//2 474//2 +f 456//1 471//1 470//1 +f 488//2 489//2 474//2 +f 456//1 470//1 469//1 +f 487//2 488//2 474//2 +f 456//1 469//1 468//1 +f 486//2 487//2 474//2 +f 456//1 468//1 467//1 +f 485//2 486//2 474//2 +f 466//1 465//1 464//1 +f 482//2 483//2 484//2 +f 466//1 464//1 463//1 +f 481//2 482//2 484//2 +f 466//1 463//1 462//1 +f 480//2 481//2 484//2 +f 466//1 462//1 461//1 +f 479//2 480//2 484//2 +f 466//1 461//1 460//1 +f 478//2 479//2 484//2 +f 467//1 466//1 460//1 +f 478//2 484//2 485//2 +f 467//1 460//1 459//1 +f 477//2 478//2 485//2 +f 467//1 459//1 458//1 +f 476//2 477//2 485//2 +f 467//1 458//1 457//1 +f 475//2 476//2 485//2 +f 467//1 457//1 456//1 +f 474//2 475//2 485//2 +f 456//177 457//177 475//177 474//177 +f 457//178 458//178 476//178 475//178 +f 458//179 459//179 477//179 476//179 +f 459//180 460//180 478//180 477//180 +f 460//181 461//181 479//181 478//181 +f 461//182 462//182 480//182 479//182 +f 462//183 463//183 481//183 480//183 +f 463//184 464//184 482//184 481//184 +f 464//185 465//185 483//185 482//185 +f 465//186 466//186 484//186 483//186 +f 466//187 467//187 485//187 484//187 +f 467//31 468//31 486//31 485//31 +f 468//188 469//188 487//188 486//188 +f 469//189 470//189 488//189 487//189 +f 470//190 471//190 489//190 488//190 +f 471//191 472//191 490//191 489//191 +f 472//192 473//192 491//192 490//192 +f 473//193 456//193 474//193 491//193 +o letter04Face +usemtl dark +f 492//1 509//1 508//1 +f 544//1 545//1 528//1 +f 492//1 508//1 507//1 +f 543//2 544//2 528//2 +f 492//1 507//1 506//1 +f 542//2 543//2 528//2 +f 492//1 506//1 505//1 +f 541//2 542//2 528//2 +f 492//1 505//1 504//1 +f 540//2 541//2 528//2 +f 492//1 504//1 503//1 +f 539//2 540//2 528//2 +f 502//1 501//1 500//1 +f 536//2 537//2 538//2 +f 502//1 500//1 499//1 +f 535//1 536//1 538//1 +f 502//1 499//1 498//1 +f 534//2 535//2 538//2 +f 502//1 498//1 497//1 +f 533//2 534//2 538//2 +f 502//1 497//1 496//1 +f 532//2 533//2 538//2 +f 503//1 502//1 496//1 +f 532//2 538//2 539//2 +f 503//1 496//1 495//1 +f 531//2 532//2 539//2 +f 503//1 495//1 494//1 +f 530//2 531//2 539//2 +f 503//1 494//1 493//1 +f 529//2 530//2 539//2 +f 503//1 493//1 492//1 +f 528//2 529//2 539//2 +f 492//177 493//177 511//177 510//177 +f 493//178 494//178 512//178 511//178 +f 494//179 495//179 513//179 512//179 +f 495//180 496//180 514//180 513//180 +f 496//181 497//181 515//181 514//181 +f 497//182 498//182 516//182 515//182 +f 498//183 499//183 517//183 516//183 +f 499//184 500//184 518//184 517//184 +f 500//185 501//185 519//185 518//185 +f 501//186 502//186 520//186 519//186 +f 502//187 503//187 521//187 520//187 +f 503//31 504//31 522//31 521//31 +f 504//188 505//188 523//188 522//188 +f 505//189 506//189 524//189 523//189 +f 506//190 507//190 525//190 524//190 +f 507//191 508//191 526//191 525//191 +f 508//192 509//192 527//192 526//192 +f 509//193 492//193 510//193 527//193 +f 510//194 511//194 529//194 528//194 +f 511//195 512//195 530//195 529//195 +f 512//196 513//196 531//196 530//196 +f 513//197 514//197 532//197 531//197 +f 514//198 515//198 533//198 532//198 +f 515//199 516//199 534//199 533//199 +f 516//200 517//200 535//200 534//200 +f 517//201 518//201 536//201 535//201 +f 518//202 519//202 537//202 536//202 +f 519//203 520//203 538//203 537//203 +f 520//204 521//204 539//204 538//204 +f 521//205 522//205 540//205 539//205 +f 522//206 523//206 541//206 540//206 +f 523//207 524//207 542//207 541//207 +f 524//208 525//208 543//208 542//208 +f 525//209 526//209 544//209 543//209 +f 526//210 527//210 545//210 544//210 +f 527//211 510//211 528//211 545//211 +o letter05Walls +usemtl frame +f 546//1 597//1 596//1 +f 648//2 649//2 598//2 +f 546//1 596//1 595//1 +f 647//2 648//2 598//2 +f 546//1 595//1 594//1 +f 646//2 647//2 598//2 +f 546//1 594//1 593//1 +f 645//2 646//2 598//2 +f 546//1 593//1 592//1 +f 644//2 645//2 598//2 +f 546//1 592//1 591//1 +f 643//2 644//2 598//2 +f 546//1 591//1 590//1 +f 642//2 643//2 598//2 +f 546//1 590//1 589//1 +f 641//2 642//2 598//2 +f 546//1 589//1 588//1 +f 640//2 641//2 598//2 +f 546//1 588//1 587//1 +f 639//2 640//2 598//2 +f 546//1 587//1 586//1 +f 638//2 639//2 598//2 +f 546//1 586//1 585//1 +f 637//2 638//2 598//2 +f 546//1 585//1 584//1 +f 636//2 637//2 598//2 +f 546//1 584//1 583//1 +f 635//2 636//2 598//2 +f 546//1 583//1 582//1 +f 634//2 635//2 598//2 +f 571//1 570//1 569//1 +f 621//2 622//2 623//2 +f 571//1 569//1 568//1 +f 620//2 621//2 623//2 +f 571//1 568//1 567//1 +f 619//2 620//2 623//2 +f 572//1 571//1 567//1 +f 619//2 623//2 624//2 +f 573//1 572//1 567//1 +f 619//2 624//2 625//2 +f 573//1 567//1 566//1 +f 618//2 619//2 625//2 +f 574//1 573//1 566//1 +f 618//2 625//2 626//2 +f 574//1 566//1 565//1 +f 617//2 618//2 626//2 +f 574//1 565//1 564//1 +f 616//2 617//2 626//2 +f 575//1 574//1 564//1 +f 616//2 626//2 627//2 +f 576//1 575//1 564//1 +f 616//2 627//2 628//2 +f 564//1 563//1 562//1 +f 614//2 615//2 616//2 +f 564//1 562//1 561//1 +f 613//2 614//2 616//2 +f 576//1 564//1 561//1 +f 613//2 616//2 628//2 +f 577//1 576//1 561//1 +f 613//2 628//2 629//2 +f 578//1 577//1 561//1 +f 613//2 629//2 630//2 +f 578//1 561//1 560//1 +f 612//2 613//2 630//2 +f 579//1 578//1 560//1 +f 612//2 630//2 631//2 +f 579//1 560//1 559//1 +f 611//2 612//2 631//2 +f 580//1 579//1 559//1 +f 611//2 631//2 632//2 +f 580//1 559//1 558//1 +f 610//2 611//2 632//2 +f 580//1 558//1 557//1 +f 609//2 610//2 632//2 +f 581//1 580//1 557//1 +f 609//2 632//2 633//2 +f 581//1 557//1 556//1 +f 608//2 609//2 633//2 +f 582//1 581//1 556//1 +f 608//2 633//2 634//2 +f 546//1 582//1 556//1 +f 608//2 634//2 598//2 +f 546//1 556//1 555//1 +f 607//2 608//2 598//2 +f 546//1 555//1 554//1 +f 606//2 607//2 598//2 +f 546//1 554//1 553//1 +f 605//2 606//2 598//2 +f 546//1 553//1 552//1 +f 604//2 605//2 598//2 +f 546//1 552//1 551//1 +f 603//2 604//2 598//2 +f 546//1 551//1 550//1 +f 602//2 603//2 598//2 +f 546//1 550//1 549//1 +f 601//2 602//2 598//2 +f 546//1 549//1 548//1 +f 600//2 601//2 598//2 +f 548//1 547//1 546//1 +f 598//2 599//2 600//2 +f 546//212 547//212 599//212 598//212 +f 547//213 548//213 600//213 599//213 +f 548//214 549//214 601//214 600//214 +f 549//215 550//215 602//215 601//215 +f 550//216 551//216 603//216 602//216 +f 551//217 552//217 604//217 603//217 +f 552//218 553//218 605//218 604//218 +f 553//219 554//219 606//219 605//219 +f 554//220 555//220 607//220 606//220 +f 555//221 556//221 608//221 607//221 +f 556//222 557//222 609//222 608//222 +f 557//223 558//223 610//223 609//223 +f 558//224 559//224 611//224 610//224 +f 559//225 560//225 612//225 611//225 +f 560//226 561//226 613//226 612//226 +f 561//227 562//227 614//227 613//227 +f 562//228 563//228 615//228 614//228 +f 563//229 564//229 616//229 615//229 +f 564//230 565//230 617//230 616//230 +f 565//231 566//231 618//231 617//231 +f 566//232 567//232 619//232 618//232 +f 567//233 568//233 620//233 619//233 +f 568//234 569//234 621//234 620//234 +f 569//235 570//235 622//235 621//235 +f 570//236 571//236 623//236 622//236 +f 571//237 572//237 624//237 623//237 +f 572//238 573//238 625//238 624//238 +f 573//239 574//239 626//239 625//239 +f 574//240 575//240 627//240 626//240 +f 575//241 576//241 628//241 627//241 +f 576//242 577//242 629//242 628//242 +f 577//243 578//243 630//243 629//243 +f 578//244 579//244 631//244 630//244 +f 579//245 580//245 632//245 631//245 +f 580//246 581//246 633//246 632//246 +f 581//247 582//247 634//247 633//247 +f 582//248 583//248 635//248 634//248 +f 583//56 584//56 636//56 635//56 +f 584//249 585//249 637//249 636//249 +f 585//250 586//250 638//250 637//250 +f 586//251 587//251 639//251 638//251 +f 587//252 588//252 640//252 639//252 +f 588//253 589//253 641//253 640//253 +f 589//254 590//254 642//254 641//254 +f 590//184 591//184 643//184 642//184 +f 591//255 592//255 644//255 643//255 +f 592//101 593//101 645//101 644//101 +f 593//256 594//256 646//256 645//256 +f 594//257 595//257 647//257 646//257 +f 595//258 596//258 648//258 647//258 +f 596//259 597//259 649//259 648//259 +f 597//260 546//260 598//260 649//260 +o letter05Face +usemtl dark +f 650//1 701//1 700//1 +f 804//2 805//2 754//2 +f 650//1 700//1 699//1 +f 803//2 804//2 754//2 +f 650//1 699//1 698//1 +f 802//2 803//2 754//2 +f 650//1 698//1 697//1 +f 801//2 802//2 754//2 +f 650//1 697//1 696//1 +f 800//2 801//2 754//2 +f 650//1 696//1 695//1 +f 799//2 800//2 754//2 +f 650//1 695//1 694//1 +f 798//1 799//1 754//1 +f 650//1 694//1 693//1 +f 797//1 798//1 754//1 +f 650//1 693//1 692//1 +f 796//2 797//2 754//2 +f 650//1 692//1 691//1 +f 795//2 796//2 754//2 +f 650//1 691//1 690//1 +f 794//2 795//2 754//2 +f 650//1 690//1 689//1 +f 793//2 794//2 754//2 +f 650//1 689//1 688//1 +f 792//2 793//2 754//2 +f 650//1 688//1 687//1 +f 791//2 792//2 754//2 +f 650//1 687//1 686//1 +f 790//2 791//2 754//2 +f 675//1 674//1 673//1 +f 777//2 778//2 779//2 +f 675//1 673//1 672//1 +f 776//2 777//2 779//2 +f 675//1 672//1 671//1 +f 775//2 776//2 779//2 +f 676//1 675//1 671//1 +f 775//2 779//2 780//2 +f 677//1 676//1 671//1 +f 775//2 780//2 781//2 +f 677//1 671//1 670//1 +f 774//2 775//2 781//2 +f 678//1 677//1 670//1 +f 774//2 781//2 782//2 +f 678//1 670//1 669//1 +f 773//1 774//1 782//1 +f 678//1 669//1 668//1 +f 772//2 773//2 782//2 +f 679//1 678//1 668//1 +f 772//2 782//2 783//2 +f 680//1 679//1 668//1 +f 772//2 783//2 784//2 +f 668//1 667//1 666//1 +f 770//1 771//1 772//1 +f 668//1 666//1 665//1 +f 769//2 770//2 772//2 +f 680//1 668//1 665//1 +f 769//1 772//1 784//1 +f 681//1 680//1 665//1 +f 769//2 784//2 785//2 +f 682//1 681//1 665//1 +f 769//2 785//2 786//2 +f 682//1 665//1 664//1 +f 768//2 769//2 786//2 +f 683//1 682//1 664//1 +f 768//2 786//2 787//2 +f 683//1 664//1 663//1 +f 767//2 768//2 787//2 +f 684//1 683//1 663//1 +f 767//2 787//2 788//2 +f 684//1 663//1 662//1 +f 766//2 767//2 788//2 +f 684//1 662//1 661//1 +f 765//2 766//2 788//2 +f 685//1 684//1 661//1 +f 765//2 788//2 789//2 +f 685//1 661//1 660//1 +f 764//2 765//2 789//2 +f 686//1 685//1 660//1 +f 764//2 789//2 790//2 +f 650//1 686//1 660//1 +f 764//1 790//1 754//1 +f 650//1 660//1 659//1 +f 763//2 764//2 754//2 +f 650//1 659//1 658//1 +f 762//2 763//2 754//2 +f 650//1 658//1 657//1 +f 761//2 762//2 754//2 +f 650//1 657//1 656//1 +f 760//2 761//2 754//2 +f 650//1 656//1 655//1 +f 759//2 760//2 754//2 +f 650//1 655//1 654//1 +f 758//2 759//2 754//2 +f 650//1 654//1 653//1 +f 757//2 758//2 754//2 +f 650//1 653//1 652//1 +f 756//2 757//2 754//2 +f 652//1 651//1 650//1 +f 754//2 755//2 756//2 +f 650//212 651//212 703//212 702//212 +f 651//213 652//213 704//213 703//213 +f 652//214 653//214 705//214 704//214 +f 653//215 654//215 706//215 705//215 +f 654//216 655//216 707//216 706//216 +f 655//217 656//217 708//217 707//217 +f 656//218 657//218 709//218 708//218 +f 657//219 658//219 710//219 709//219 +f 658//220 659//220 711//220 710//220 +f 659//221 660//221 712//221 711//221 +f 660//222 661//222 713//222 712//222 +f 661//223 662//223 714//223 713//223 +f 662//224 663//224 715//224 714//224 +f 663//225 664//225 716//225 715//225 +f 664//226 665//226 717//226 716//226 +f 665//227 666//227 718//227 717//227 +f 666//228 667//228 719//228 718//228 +f 667//229 668//229 720//229 719//229 +f 668//230 669//230 721//230 720//230 +f 669//231 670//231 722//231 721//231 +f 670//232 671//232 723//232 722//232 +f 671//233 672//233 724//233 723//233 +f 672//234 673//234 725//234 724//234 +f 673//235 674//235 726//235 725//235 +f 674//236 675//236 727//236 726//236 +f 675//237 676//237 728//237 727//237 +f 676//238 677//238 729//238 728//238 +f 677//239 678//239 730//239 729//239 +f 678//240 679//240 731//240 730//240 +f 679//241 680//241 732//241 731//241 +f 680//242 681//242 733//242 732//242 +f 681//243 682//243 734//243 733//243 +f 682//244 683//244 735//244 734//244 +f 683//245 684//245 736//245 735//245 +f 684//246 685//246 737//246 736//246 +f 685//247 686//247 738//247 737//247 +f 686//248 687//248 739//248 738//248 +f 687//56 688//56 740//56 739//56 +f 688//249 689//249 741//249 740//249 +f 689//250 690//250 742//250 741//250 +f 690//251 691//251 743//251 742//251 +f 691//252 692//252 744//252 743//252 +f 692//253 693//253 745//253 744//253 +f 693//254 694//254 746//254 745//254 +f 694//184 695//184 747//184 746//184 +f 695//255 696//255 748//255 747//255 +f 696//101 697//101 749//101 748//101 +f 697//256 698//256 750//256 749//256 +f 698//257 699//257 751//257 750//257 +f 699//258 700//258 752//258 751//258 +f 700//259 701//259 753//259 752//259 +f 701//260 650//260 702//260 753//260 +f 702//261 703//261 755//261 754//261 +f 703//262 704//262 756//262 755//262 +f 704//263 705//263 757//263 756//263 +f 705//264 706//264 758//264 757//264 +f 706//265 707//265 759//265 758//265 +f 707//266 708//266 760//266 759//266 +f 708//267 709//267 761//267 760//267 +f 709//268 710//268 762//268 761//268 +f 710//269 711//269 763//269 762//269 +f 711//270 712//270 764//270 763//270 +f 712//271 713//271 765//271 764//271 +f 713//272 714//272 766//272 765//272 +f 714//273 715//273 767//273 766//273 +f 715//274 716//274 768//274 767//274 +f 716//275 717//275 769//275 768//275 +f 717//276 718//276 770//276 769//276 +f 718//277 719//277 771//277 770//277 +f 719//278 720//278 772//278 771//278 +f 720//279 721//279 773//279 772//279 +f 721//280 722//280 774//280 773//280 +f 722//281 723//281 775//281 774//281 +f 723//282 724//282 776//282 775//282 +f 724//283 725//283 777//283 776//283 +f 725//284 726//284 778//284 777//284 +f 726//285 727//285 779//285 778//285 +f 727//286 728//286 780//286 779//286 +f 728//287 729//287 781//287 780//287 +f 729//288 730//288 782//288 781//288 +f 730//289 731//289 783//289 782//289 +f 731//290 732//290 784//290 783//290 +f 732//291 733//291 785//291 784//291 +f 733//292 734//292 786//292 785//292 +f 734//293 735//293 787//293 786//293 +f 735//294 736//294 788//294 787//294 +f 736//295 737//295 789//295 788//295 +f 737//296 738//296 790//296 789//296 +f 738//297 739//297 791//297 790//297 +f 739//298 740//298 792//298 791//298 +f 740//299 741//299 793//299 792//299 +f 741//300 742//300 794//300 793//300 +f 742//301 743//301 795//301 794//301 +f 743//302 744//302 796//302 795//302 +f 744//303 745//303 797//303 796//303 +f 745//304 746//304 798//304 797//304 +f 746//201 747//201 799//201 798//201 +f 747//305 748//305 800//305 799//305 +f 748//306 749//306 801//306 800//306 +f 749//307 750//307 802//307 801//307 +f 750//308 751//308 803//308 802//308 +f 751//309 752//309 804//309 803//309 +f 752//310 753//310 805//310 804//310 +f 753//311 702//311 754//311 805//311 +o letter06Walls +usemtl frame +f 806//1 832//1 831//1 +f 858//2 859//2 833//2 +f 806//1 831//1 830//1 +f 857//2 858//2 833//2 +f 806//1 830//1 829//1 +f 856//2 857//2 833//2 +f 806//1 829//1 828//1 +f 855//2 856//2 833//2 +f 806//1 828//1 827//1 +f 854//2 855//2 833//2 +f 827//1 826//1 825//1 +f 852//2 853//2 854//2 +f 827//1 825//1 824//1 +f 851//2 852//2 854//2 +f 827//1 824//1 823//1 +f 850//2 851//2 854//2 +f 827//1 823//1 822//1 +f 849//2 850//2 854//2 +f 806//1 827//1 822//1 +f 849//2 854//2 833//2 +f 806//1 822//1 821//1 +f 848//2 849//2 833//2 +f 820//1 819//1 818//1 +f 845//2 846//2 847//2 +f 820//1 818//1 817//1 +f 844//2 845//2 847//2 +f 820//1 817//1 816//1 +f 843//2 844//2 847//2 +f 820//1 816//1 815//1 +f 842//2 843//2 847//2 +f 820//1 815//1 814//1 +f 841//2 842//2 847//2 +f 821//1 820//1 814//1 +f 841//2 847//2 848//2 +f 806//1 821//1 814//1 +f 841//2 848//2 833//2 +f 806//1 814//1 813//1 +f 840//2 841//2 833//2 +f 806//1 813//1 812//1 +f 839//2 840//2 833//2 +f 806//1 812//1 811//1 +f 838//2 839//2 833//2 +f 806//1 811//1 810//1 +f 837//2 838//2 833//2 +f 806//1 810//1 809//1 +f 836//2 837//2 833//2 +f 806//1 809//1 808//1 +f 835//2 836//2 833//2 +f 808//1 807//1 806//1 +f 833//2 834//2 835//2 +f 806//312 807//312 834//312 833//312 +f 807//313 808//313 835//313 834//313 +f 808//314 809//314 836//314 835//314 +f 809//315 810//315 837//315 836//315 +f 810//316 811//316 838//316 837//316 +f 811//317 812//317 839//317 838//317 +f 812//318 813//318 840//318 839//318 +f 813//319 814//319 841//319 840//319 +f 814//320 815//320 842//320 841//320 +f 815//321 816//321 843//321 842//321 +f 816//322 817//322 844//322 843//322 +f 817//323 818//323 845//323 844//323 +f 818//324 819//324 846//324 845//324 +f 819//325 820//325 847//325 846//325 +f 820//326 821//326 848//326 847//326 +f 821//327 822//327 849//327 848//327 +f 822//328 823//328 850//328 849//328 +f 823//329 824//329 851//329 850//329 +f 824//330 825//330 852//330 851//330 +f 825//331 826//331 853//331 852//331 +f 826//332 827//332 854//332 853//332 +f 827//333 828//333 855//333 854//333 +f 828//334 829//334 856//334 855//334 +f 829//335 830//335 857//335 856//335 +f 830//336 831//336 858//336 857//336 +f 831//331 832//331 859//331 858//331 +f 832//337 806//337 833//337 859//337 +o letter06Face +usemtl dark +f 860//1 886//1 885//1 +f 939//1 940//1 914//1 +f 860//1 885//1 884//1 +f 938//2 939//2 914//2 +f 860//1 884//1 883//1 +f 937//2 938//2 914//2 +f 860//1 883//1 882//1 +f 936//2 937//2 914//2 +f 860//1 882//1 881//1 +f 935//2 936//2 914//2 +f 881//1 880//1 879//1 +f 933//2 934//2 935//2 +f 881//1 879//1 878//1 +f 932//1 933//1 935//1 +f 881//1 878//1 877//1 +f 931//2 932//2 935//2 +f 881//1 877//1 876//1 +f 930//2 931//2 935//2 +f 860//1 881//1 876//1 +f 930//2 935//2 914//2 +f 860//1 876//1 875//1 +f 929//2 930//2 914//2 +f 874//1 873//1 872//1 +f 926//2 927//2 928//2 +f 874//1 872//1 871//1 +f 925//1 926//1 928//1 +f 874//1 871//1 870//1 +f 924//2 925//2 928//2 +f 874//1 870//1 869//1 +f 923//2 924//2 928//2 +f 874//1 869//1 868//1 +f 922//2 923//2 928//2 +f 875//1 874//1 868//1 +f 922//2 928//2 929//2 +f 860//1 875//1 868//1 +f 922//2 929//2 914//2 +f 860//1 868//1 867//1 +f 921//1 922//1 914//1 +f 860//1 867//1 866//1 +f 920//2 921//2 914//2 +f 860//1 866//1 865//1 +f 919//2 920//2 914//2 +f 860//1 865//1 864//1 +f 918//2 919//2 914//2 +f 860//1 864//1 863//1 +f 917//2 918//2 914//2 +f 860//1 863//1 862//1 +f 916//1 917//1 914//1 +f 862//1 861//1 860//1 +f 914//2 915//2 916//2 +f 860//312 861//312 888//312 887//312 +f 861//313 862//313 889//313 888//313 +f 862//314 863//314 890//314 889//314 +f 863//315 864//315 891//315 890//315 +f 864//316 865//316 892//316 891//316 +f 865//317 866//317 893//317 892//317 +f 866//318 867//318 894//318 893//318 +f 867//319 868//319 895//319 894//319 +f 868//320 869//320 896//320 895//320 +f 869//321 870//321 897//321 896//321 +f 870//322 871//322 898//322 897//322 +f 871//323 872//323 899//323 898//323 +f 872//324 873//324 900//324 899//324 +f 873//325 874//325 901//325 900//325 +f 874//326 875//326 902//326 901//326 +f 875//327 876//327 903//327 902//327 +f 876//328 877//328 904//328 903//328 +f 877//329 878//329 905//329 904//329 +f 878//330 879//330 906//330 905//330 +f 879//331 880//331 907//331 906//331 +f 880//332 881//332 908//332 907//332 +f 881//333 882//333 909//333 908//333 +f 882//334 883//334 910//334 909//334 +f 883//335 884//335 911//335 910//335 +f 884//336 885//336 912//336 911//336 +f 885//331 886//331 913//331 912//331 +f 886//337 860//337 887//337 913//337 +f 887//338 888//338 915//338 914//338 +f 888//339 889//339 916//339 915//339 +f 889//340 890//340 917//340 916//340 +f 890//341 891//341 918//341 917//341 +f 891//342 892//342 919//342 918//342 +f 892//343 893//343 920//343 919//343 +f 893//344 894//344 921//344 920//344 +f 894//345 895//345 922//345 921//345 +f 895//346 896//346 923//346 922//346 +f 896//347 897//347 924//347 923//347 +f 897//348 898//348 925//348 924//348 +f 898//349 899//349 926//349 925//349 +f 899//350 900//350 927//350 926//350 +f 900//351 901//351 928//351 927//351 +f 901//352 902//352 929//352 928//352 +f 902//353 903//353 930//353 929//353 +f 903//354 904//354 931//354 930//354 +f 904//355 905//355 932//355 931//355 +f 905//356 906//356 933//356 932//356 +f 906//357 907//357 934//357 933//357 +f 907//358 908//358 935//358 934//358 +f 908//359 909//359 936//359 935//359 +f 909//360 910//360 937//360 936//360 +f 910//361 911//361 938//361 937//361 +f 911//362 912//362 939//362 938//362 +f 912//357 913//357 940//357 939//357 +f 913//363 887//363 914//363 940//363 diff --git a/cmake/Superbuild.cmake b/cmake/Superbuild.cmake index d637ab6ec..0ed232a48 100644 --- a/cmake/Superbuild.cmake +++ b/cmake/Superbuild.cmake @@ -169,6 +169,9 @@ singeCmakeProject(zstd ${SB_THIRDPARTY}/zstd/build/cmake "" "-DZSTD_BUILD_SHARED singeCmakeProject(SDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=off;-DSDL_STATIC=on;-DSDL_TESTS=off;-DSDL_EXAMPLES=off" "${SB_ENV}") +# ===== Host tools: the shader compiler (see cmake/hostTools.cmake) ===== +include(${CMAKE_CURRENT_LIST_DIR}/hostTools.cmake) + # ===== Jolt Physics (C++; static, single precision, no tests or samples) ===== # The x86 baseline is SSE4.1/4.2 so a shipped binary runs on anything from 2008 on; AVX and the # bit-count instructions would silently raise it. No link-time optimisation through the cross @@ -315,8 +318,8 @@ set(singeBinary ${SB_PREFIX}/singe) ExternalProject_Add(singe SOURCE_DIR ${CMAKE_SOURCE_DIR} BINARY_DIR ${singeBinary} - DEPENDS zlib zstd SDL3 SDL3_image SDL3_mixer SDL3_ttf openssl ffmpeg Jolt - CONFIGURE_COMMAND ${SB_ENV} ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${singeBinary} ${SB_CMAKE_ARGS} -DSINGE_SUPERBUILD=OFF -DKANGAROO_OS=${KANGAROO_OS} -DKANGAROO_ARCH=${KANGAROO_ARCH} -DSINGE_TREE=${SINGE_TREE} + DEPENDS zlib zstd SDL3 SDL3_image SDL3_mixer SDL3_ttf openssl ffmpeg Jolt shadercross + CONFIGURE_COMMAND ${SB_ENV} ${CMAKE_COMMAND} -S ${CMAKE_SOURCE_DIR} -B ${singeBinary} ${SB_CMAKE_ARGS} -DSINGE_SHADERCROSS=${SINGE_SHADERCROSS} -DSINGE_SUPERBUILD=OFF -DKANGAROO_OS=${KANGAROO_OS} -DKANGAROO_ARCH=${KANGAROO_ARCH} -DSINGE_TREE=${SINGE_TREE} BUILD_COMMAND ${SB_ENV} ${CMAKE_COMMAND} --build ${singeBinary} --parallel ${SB_JOBS} INSTALL_COMMAND ${CMAKE_COMMAND} -E copy ${singeBinary}/${singeBinaryBase}${CMAKE_EXECUTABLE_SUFFIX} ${CMAKE_SOURCE_DIR}/.builddir/${singeBinaryName} BUILD_ALWAYS ON diff --git a/cmake/hostTools.cmake b/cmake/hostTools.cmake new file mode 100644 index 000000000..a6075998f --- /dev/null +++ b/cmake/hostTools.cmake @@ -0,0 +1,66 @@ +# Host tools the build needs at build time, built once for the host machine and shared by every +# preset: SDL_shadercross (the scene shaders are HLSL, compiled to SPIR-V, DXIL and MSL when Singe +# is built, the way the icon and the other embedded files are made) with SPIRV-Cross, a minimal +# host SDL3 for its command line tool, and Microsoft's prebuilt DirectX Shader Compiler, which +# SDL_shadercross drives and which is fetched like zig (building it from source is a half-hour +# job nobody should need). Everything lives under .builddir/toolchains. +# +# DXC's prebuilt release exists for x86_64 Linux hosts. On another host put an unpacked DXC +# (lib/libdxcompiler.so, lib/libdxil.so, include/dxc/dxcapi.h) at .builddir/toolchains/dxc. + +set(SINGE_DXC_VERSION "v1.10.2605.37") +set(SINGE_DXC_FILE "linux_dxc_2026_08_11.x86_64.tar.gz") +set(SINGE_DXC_SHA256 "dea02d61c0548a6163a5fb45daa9263faeb5334e35d25a30b0293a32d089ceae") + +get_filename_component(hostToolchains ${CMAKE_CURRENT_LIST_DIR}/../.builddir/toolchains ABSOLUTE) +set(hostPrefix ${hostToolchains}/host) +set(hostBuild ${hostToolchains}/hostBuild) +set(hostDxc ${hostToolchains}/dxc) +file(MAKE_DIRECTORY ${hostPrefix} ${hostBuild}) + +# ----- DirectX Shader Compiler, prebuilt ----- +if(NOT EXISTS ${hostDxc}/lib/libdxcompiler.so) + if((CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64")) + set(dxcUrl "https://github.com/microsoft/DirectXShaderCompiler/releases/download/${SINGE_DXC_VERSION}/${SINGE_DXC_FILE}") + message(STATUS "Downloading the DirectX Shader Compiler ${SINGE_DXC_VERSION}") + file(DOWNLOAD ${dxcUrl} ${hostToolchains}/${SINGE_DXC_FILE} EXPECTED_HASH SHA256=${SINGE_DXC_SHA256} STATUS status) + list(GET status 0 code) + if(NOT code EQUAL 0) + message(FATAL_ERROR "DirectX Shader Compiler download failed: ${status}") + endif() + file(MAKE_DIRECTORY ${hostDxc}) + file(ARCHIVE_EXTRACT INPUT ${hostToolchains}/${SINGE_DXC_FILE} DESTINATION ${hostDxc}) + else() + message(FATAL_ERROR "No prebuilt DirectX Shader Compiler for this host; put one at ${hostDxc} (see cmake/hostTools.cmake).") + endif() +endif() + +# ----- The host builds run with the host's own compilers, never the cross toolchain ----- +set(hostEnv ${CMAKE_COMMAND} -E env --unset=CC --unset=CXX --unset=AR --unset=RANLIB --unset=CFLAGS --unset=CXXFLAGS --unset=LDFLAGS --unset=PKG_CONFIG_LIBDIR --unset=PKG_CONFIG_SYSROOT_DIR "MAKEFLAGS=-j${SB_JOBS}") +# A | inside an argument becomes a ; (a CMake list) on the way to the child, see LIST_SEPARATOR. +set(hostArgs -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${hostPrefix} "-DCMAKE_INSTALL_RPATH=${hostPrefix}/lib|${hostDxc}/lib" -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON) + +# A host CMake project; its stamps and build tree live with the tools, so every preset shares them. +function(singeHostProject name source deps args) + ExternalProject_Add(${name} + SOURCE_DIR ${source} + BINARY_DIR ${hostBuild}/${name} + STAMP_DIR ${hostBuild}/${name}-stamp + TMP_DIR ${hostBuild}/${name}-tmp + DEPENDS ${deps} + LIST_SEPARATOR | + CONFIGURE_COMMAND ${hostEnv} ${CMAKE_COMMAND} -S ${source} -B ${hostBuild}/${name} ${hostArgs} ${args} + BUILD_COMMAND ${hostEnv} ${CMAKE_COMMAND} --build ${hostBuild}/${name} --parallel ${SB_JOBS} + INSTALL_COMMAND ${hostEnv} ${CMAKE_COMMAND} --build ${hostBuild}/${name} --target install + LOG_CONFIGURE ON LOG_BUILD ON LOG_INSTALL ON LOG_OUTPUT_ON_FAILURE ON + ) +endfunction() + +# SDL3 for the host, shared, with nothing SDL_shadercross's tool does not use. +singeHostProject(hostSDL3 ${SB_THIRDPARTY}/SDL3 "" "-DSDL_SHARED=ON;-DSDL_STATIC=OFF;-DSDL_TESTS=OFF;-DSDL_EXAMPLES=OFF;-DSDL_AUDIO=OFF;-DSDL_VIDEO=OFF;-DSDL_GPU=OFF;-DSDL_RENDER=OFF;-DSDL_CAMERA=OFF;-DSDL_JOYSTICK=OFF;-DSDL_HAPTIC=OFF;-DSDL_HIDAPI=OFF;-DSDL_POWER=OFF;-DSDL_SENSOR=OFF;-DSDL_DIALOG=OFF;-DSDL_UNIX_CONSOLE_BUILD=ON") +# SPIRV-Cross as the shared C library SDL_shadercross looks for. +singeHostProject(spirvCross ${SB_THIRDPARTY}/SPIRV-Cross "" "-DSPIRV_CROSS_SHARED=ON;-DSPIRV_CROSS_STATIC=OFF;-DSPIRV_CROSS_CLI=OFF;-DSPIRV_CROSS_ENABLE_TESTS=OFF") +# SDL_shadercross, unvendored: SPIRV-Cross and SDL3 from the host prefix, DXC from its download. +singeHostProject(shadercross ${SB_THIRDPARTY}/SDL_shadercross "hostSDL3;spirvCross" "-DSDLSHADERCROSS_VENDORED=OFF;-DSDLSHADERCROSS_DXC=ON;-DSDLSHADERCROSS_SHARED=OFF;-DSDLSHADERCROSS_STATIC=ON;-DSDLSHADERCROSS_CLI=ON;-DSDLSHADERCROSS_INSTALL=ON;-DSDLSHADERCROSS_INSTALL_CPACK=OFF;-DCMAKE_PREFIX_PATH=${hostPrefix}|${hostDxc};-DSDL3_DIR=${hostPrefix}/lib/cmake/SDL3") + +set(SINGE_SHADERCROSS ${hostPrefix}/bin/shadercross) diff --git a/cmake/shaderHeader.cmake b/cmake/shaderHeader.cmake new file mode 100644 index 000000000..9983cf017 --- /dev/null +++ b/cmake/shaderHeader.cmake @@ -0,0 +1,38 @@ +# Compiles src/shaders/scene.hlsl into a C header of SPIR-V, DXIL and MSL blobs with +# SDL_shadercross. Run by the build as +# cmake -DSHADERCROSS= -DSOURCE= -DOUTPUT= -P shaderHeader.cmake +# so the header is generated into the build tree like the icon and the other embedded files. + +set(entries vertexStatic:vertex vertexSkinned:vertex fragmentMain:fragment depthMain:fragment) +set(formats SPIRV DXIL MSL) +get_filename_component(sourceDir ${SOURCE} DIRECTORY) +get_filename_component(outputDir ${OUTPUT} DIRECTORY) +set(work ${outputDir}/work) +file(MAKE_DIRECTORY ${work}) + +set(header "// Generated from scene.hlsl by cmake/shaderHeader.cmake with SDL_shadercross; do not edit.\n") +string(APPEND header "// SPIR-V for Vulkan, DXIL for Direct3D 12, MSL for Metal, one set per entry point.\n\n") +string(APPEND header "#ifndef SCENE_SHADERS_H\n#define SCENE_SHADERS_H\n\n#include \n\n") +string(APPEND header "typedef struct SceneShaderS {\n\tconst char *entryPoint;\n\tconst unsigned char *spirv;\n\tsize_t spirvSize;\n\tconst unsigned char *dxil;\n\tsize_t dxilSize;\n\tconst unsigned char *msl;\n\tsize_t mslSize;\n} SceneShaderT;\n\n") +foreach(entry IN LISTS entries) + string(REPLACE ":" ";" parts ${entry}) + list(GET parts 0 name) + list(GET parts 1 stage) + foreach(format IN LISTS formats) + string(TOLOWER ${format} extension) + set(blob ${work}/${name}.${extension}) + execute_process(COMMAND ${SHADERCROSS} ${SOURCE} -s HLSL -d ${format} -t ${stage} -e ${name} -o ${blob} RESULT_VARIABLE code OUTPUT_VARIABLE out ERROR_VARIABLE err) + if(NOT code EQUAL 0) + message(FATAL_ERROR "Shader ${name} (${format}) failed to compile:\n${out}\n${err}") + endif() + file(READ ${blob} hex HEX) + string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," bytes "${hex}") + string(APPEND header "static const unsigned char _${name}${format}[] = {\n\t${bytes}\n};\n\n") + endforeach() + string(SUBSTRING ${name} 0 1 initial) + string(SUBSTRING ${name} 1 -1 rest) + string(TOUPPER ${initial} initial) + string(APPEND header "static const SceneShaderT sceneShader${initial}${rest} = { \"${name}\", _${name}SPIRV, sizeof(_${name}SPIRV), _${name}DXIL, sizeof(_${name}DXIL), _${name}MSL, sizeof(_${name}MSL) };\n\n") +endforeach() +string(APPEND header "#endif\n") +file(WRITE ${OUTPUT} "${header}") diff --git a/docs/Manual.adoc b/docs/Manual.adoc index c145eebb4..82ad409a5 100644 --- a/docs/Manual.adoc +++ b/docs/Manual.adoc @@ -771,9 +771,11 @@ for diagrams and HUD-like scenes. A model's animations are listed by `modelGetAnimations` and driven per instance with `animationPlay(node, nameOrIndex, loop, speed)` on the instance's root node, plus pause, resume, stop, and seek calls. Node -animation (things moving) and skinning (characters deforming) both work; -morph targets do not yet. Animations advance with real time and hold while -the game is paused. +animation (things moving), skinning (characters deforming) and morph +targets (blend shapes: faces, lip sync, muscle bulges) all work; a model's +morph weights can also be set from script with `nodeSetMorph`, by name or +number, on the node that carries the mesh. Animations advance with real +time and hold while the game is paused. .From scene to overlay and back `sceneProject(x, y, z)` turns a world point into overlay coordinates, so @@ -880,6 +882,39 @@ BODY_DYNAMIC, SHAPE_HULL)` wraps the whole model in a convex hull. Skinned characters are best driven kinematically with a capsule and left to push things rather than be pushed. +.2D games +Physics is not tied to the 3D scene. Nodes exist on every machine, GPU or +not, so a 2D game can give nodes bodies, call `physicsSet2D(true)` so they +move in X and Y and turn about Z only, work in overlay coordinates with +gravity pointing down the screen, and draw sprites where the nodes are: + +[source,lua] +---- +physicsSet2D(true) +physicsSetGravity(0, 900, 0) -- Pixels per second squared; overlay Y runs down. + +floor = nodeNew() +nodeSetPosition(floor, 360, 470, 0) +bodyNew(floor, BODY_STATIC, SHAPE_BOX, 720, 20, 50) + +crate = nodeNew() +nodeSetPosition(crate, 200, -40, 0) +bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, 40, 40, 50) +bodySetMass(crate, 1) + +function onOverlayUpdate() + local x, y = nodeGetPosition(crate) + local _, _, angle = nodeGetRotation(crate) + spriteRotate(crateArt, angle) + spriteDraw(crateArt, x, y, true) + return OVERLAY_UPDATED +end +---- + +Give every 2D body some depth (the third size) so shapes overlap in Z; +`50` is fine. Circles are `SHAPE_SPHERE`. This is the form that runs on a +Raspberry Pi 3, which has no 3D. + .Where it runs Physics runs on the CPU and needs no GPU, but the library is built for SSE4.1 on x86 (2008 and later) and NEON on the Pi; a machine below that @@ -1078,8 +1113,8 @@ These calls drive the animations a model carries, per instance. Every one takes the *root node* returned by `modelInstance`; other nodes raise an error. Animations are numbered from `1` in the order `modelGetAnimations` lists them, or named. Time advances with the wall clock and holds while the -game is paused. Node animation and skinning are supported; morph targets are -not. +game is paused. Node animation, skinning and morph target weights are all +driven. [#animationgettime] ==== animationGetTime @@ -3246,6 +3281,36 @@ The node's direct children as a table of handles, in the order they were added. *Since:* 3.00. *See also:* <> +[#nodegetmorph] +==== nodeGetMorph + +[source,text] +---- +weight = nodeGetMorph(node, nameOrIndex) +---- + +The current weight of one of the mesh's morph targets. + +*Returns:* A number. + +*Since:* 3.00. +*See also:* <>, <> + +[#nodegetmorphs] +==== nodeGetMorphs + +[source,text] +---- +names = nodeGetMorphs(node) +---- + +The morph targets of the node's mesh, in the order the file stores them, as a table of names. Exporters that write no names give empty strings; those targets can still be set by number. + +*Returns:* A table of strings. + +*Since:* 3.00. +*See also:* <> + [#nodegetname] ==== nodeGetName @@ -3404,6 +3469,32 @@ Gives the node a mesh to draw, optionally with a material (default look otherwis *Since:* 3.00. *See also:* <>, <>, <> +[#nodesetmorph] +==== nodeSetMorph + +[source,text] +---- +nodeSetMorph(node, nameOrIndex, weight) +---- + +How much of a morph target (blend shape) the node's mesh shows, usually `0` to `1`; several targets add up. The node is the one carrying the mesh, which for a model is a child of the instance root (`nodeFind` by the mesh's name, or `nodeGetChildren`). An animation with a weights channel overrides what you set while it plays. + +*Notes:* Up to eight targets with non-zero weights apply per mesh in a frame, the strongest if there are more. + +*Since:* 3.00. +*See also:* <>, <>, <> + +.Example +[source,lua] +---- +-- Lip sync from a table of mouth openings per disc frame. +face = nodeFind("Head", hero) +function onOverlayUpdate() + nodeSetMorph(face, "mouthOpen", mouthByFrame[discGetFrame()] or 0) + return OVERLAY_UPDATED +end +---- + [#nodesetname] ==== nodeSetName @@ -3723,6 +3814,19 @@ The first body along a ray from `(x, y, z)` in the direction `(dx, dy, dz)` (any *Since:* 3.00. *See also:* <>, <> +[#physicsset2d] +==== physicsSet2D + +[source,text] +---- +physicsSet2D(planar) +---- + +Bodies made from now on move only in X and Y and turn only about Z: a 2D game's world, usually in overlay coordinates with gravity pointing +Y. Bodies already made keep their freedom. Works without a GPU, the Raspberry Pi 3 included. + +*Since:* 3.00. +*See also:* <>, <> + [#physicssetenabled] ==== physicsSetEnabled diff --git a/src/model.c b/src/model.c index 8211d79b8..9e82f2223 100644 --- a/src/model.c +++ b/src/model.c @@ -43,6 +43,7 @@ #define NO_HANDLE -1 #define ERROR_LENGTH 256 +#define MAX_MORPH_TARGETS 64 #define DEGREES(r) ((r) * (180.0f / 3.14159265358979323846f)) @@ -93,9 +94,11 @@ static void _freeStaleInstances(void); static SDL_Surface *_loadImage(const cgltf_image *image); static int32_t _loadMaterial(const cgltf_material *material); static bool _loadMeshes(ModelT *model); -static int32_t _loadPrimitive(const cgltf_primitive *primitive); +static void _loadMorphs(int32_t mesh, const cgltf_mesh *source, const cgltf_primitive *primitive, int32_t vertexCount); +static int32_t _loadPrimitive(const cgltf_mesh *source, const cgltf_primitive *primitive); static bool _readAttribute(const cgltf_accessor *accessor, float *out, int32_t components, int32_t count); static void _sampleChannel(const cgltf_animation_channel *channel, double time, float *out, int32_t components); +static void _sampleWeights(const cgltf_animation_channel *channel, double time, float *out, int32_t count); static ModelT *_models; @@ -172,6 +175,7 @@ static void _applyAnimation(InstanceT *instance) { const cgltf_animation *animation; int32_t x; float values[4]; + float morphValues[MAX_MORPH_TARGETS]; if ((instance->animation < 0) || (instance->animation >= (int32_t)data->animations_count)) { return; @@ -203,6 +207,27 @@ static void _applyAnimation(InstanceT *instance) { } else if (channel->target_path == cgltf_animation_path_type_scale) { _sampleChannel(channel, instance->time, values, 3); nodeSetScale(node, vec3(values[0], values[1], values[2])); + } else if (channel->target_path == cgltf_animation_path_type_weights) { + // One weight per target per keyframe, to the node's own mesh or its primitive children. + int32_t count = (int32_t)(channel->sampler->output->count / channel->sampler->input->count); + int32_t w; + int32_t c; + + if ((count <= 0) || (count > MAX_MORPH_TARGETS)) { + continue; + } + _sampleWeights(channel, instance->time, morphValues, count); + if (nodeGetMorphCount(node) > 0) { + for (w = 0; w < count; w++) { + nodeSetMorphWeight(node, w, morphValues[w]); + } + } else { + for (c = 0; c < nodeGetChildCount(node); c++) { + for (w = 0; w < count; w++) { + nodeSetMorphWeight(nodeGetChild(node, c), w, morphValues[w]); + } + } + } } } } @@ -289,15 +314,25 @@ static void _buildNode(ModelT *model, InstanceT *instance, const cgltf_node *nod nodeSetRotation(handle, rotation); nodeSetScale(handle, scale); if (node->mesh != NULL) { + const float *weights = (node->weights_count > 0) ? node->weights : node->mesh->weights; + int32_t weightCount = (int32_t)((node->weights_count > 0) ? node->weights_count : node->mesh->weights_count); + int32_t w; + mesh = &model->meshes[node->mesh - model->data->meshes]; if (mesh->count == 1) { nodeSetMesh(handle, mesh->primitives[0].mesh, mesh->primitives[0].material); + for (w = 0; w < weightCount; w++) { + nodeSetMorphWeight(handle, w, weights[w]); + } } else { for (x = 0; x < mesh->count; x++) { int32_t child = nodeNew(handle); if (child != NO_HANDLE) { nodeSetMesh(child, mesh->primitives[x].mesh, mesh->primitives[x].material); + for (w = 0; w < weightCount; w++) { + nodeSetMorphWeight(child, w, weights[w]); + } } } } @@ -489,7 +524,7 @@ static bool _loadMeshes(ModelT *model) { utilTrace("Model: mesh %s primitive %d is not triangles; skipped.", data->meshes[x].name ? data->meshes[x].name : "(unnamed)", y); continue; } - mesh = _loadPrimitive(primitive); + mesh = _loadPrimitive(&data->meshes[x], primitive); if (mesh == NO_HANDLE) { return false; } @@ -502,9 +537,58 @@ static bool _loadMeshes(ModelT *model) { } +// The primitive's morph targets: position and normal deltas per target, named from the mesh's +// target names when the exporter wrote them. +static void _loadMorphs(int32_t mesh, const cgltf_mesh *source, const cgltf_primitive *primitive, int32_t vertexCount) { + int32_t targetCount = (int32_t)primitive->targets_count; + float *deltas; + float *values; + const char **names = NULL; + int32_t t; + int32_t x; + int32_t a; + + deltas = SDL_calloc((size_t)targetCount * (size_t)vertexCount * 6, sizeof(float)); + values = SDL_calloc((size_t)vertexCount * 3, sizeof(float)); + if ((deltas == NULL) || (values == NULL)) { + utilDie("Out of memory loading morph targets."); + } + for (t = 0; t < targetCount; t++) { + const cgltf_morph_target *target = &primitive->targets[t]; + + for (a = 0; a < (int32_t)target->attributes_count; a++) { + const cgltf_attribute *attribute = &target->attributes[a]; + int32_t offset; + + if ((attribute->type == cgltf_attribute_type_position) && (attribute->index == 0)) { + offset = 0; + } else if ((attribute->type == cgltf_attribute_type_normal) && (attribute->index == 0)) { + offset = 3; + } else { + continue; + } + if (!_readAttribute(attribute->data, values, 3, vertexCount)) { + continue; + } + for (x = 0; x < vertexCount; x++) { + deltas[((size_t)t * (size_t)vertexCount + (size_t)x) * 6 + (size_t)offset] = values[x * 3]; + deltas[((size_t)t * (size_t)vertexCount + (size_t)x) * 6 + (size_t)offset + 1] = values[x * 3 + 1]; + deltas[((size_t)t * (size_t)vertexCount + (size_t)x) * 6 + (size_t)offset + 2] = values[x * 3 + 2]; + } + } + } + if ((source->target_names != NULL) && ((int32_t)source->target_names_count >= targetCount)) { + names = (const char **)source->target_names; + } + meshSetMorphTargets(mesh, deltas, targetCount, names); + SDL_free(values); + SDL_free(deltas); +} + + // One primitive as a scene mesh: positions, normals (computed when absent), texture // coordinates, joints and weights when skinned, and indices (generated when absent). -static int32_t _loadPrimitive(const cgltf_primitive *primitive) { +static int32_t _loadPrimitive(const cgltf_mesh *source, const cgltf_primitive *primitive) { const cgltf_accessor *positions = NULL; const cgltf_accessor *normals = NULL; const cgltf_accessor *uvs = NULL; @@ -595,6 +679,8 @@ static int32_t _loadPrimitive(const cgltf_primitive *primitive) { mesh = meshNewVertices(vertices, vertexCount, indices, indexCount, (joints != NULL) && (weights != NULL)); if (mesh == NO_HANDLE) { _fail("Unable to upload a primitive."); + } else if (primitive->targets_count > 0) { + _loadMorphs(mesh, source, primitive, vertexCount); } SDL_free(values); SDL_free(indices); @@ -700,6 +786,48 @@ static void _sampleChannel(const cgltf_animation_channel *channel, double time, } +// A weights channel at a time: count scalars per keyframe, linear or step (cubic spline treated +// as linear between its values), clamped at both ends. +static void _sampleWeights(const cgltf_animation_channel *channel, double time, float *out, int32_t count) { + const cgltf_accessor *input = channel->sampler->input; + const cgltf_accessor *output = channel->sampler->output; + int32_t keys = (int32_t)input->count; + int32_t k; + int32_t next; + int32_t stride = (channel->sampler->interpolation == cgltf_interpolation_type_cubic_spline) ? 3 : 1; + float t0; + float t1; + float t; + float a; + float b; + int32_t w; + + if (keys == 0) { + return; + } + for (k = 0; k < keys - 1; k++) { + cgltf_accessor_read_float(input, (cgltf_size)(k + 1), &t1, 1); + if (time < t1) { + break; + } + } + next = SDL_min(k + 1, keys - 1); + cgltf_accessor_read_float(input, (cgltf_size)k, &t0, 1); + cgltf_accessor_read_float(input, (cgltf_size)next, &t1, 1); + t = (t1 > t0) ? (float)((time - t0) / (t1 - t0)) : 0.0f; + t = SDL_clamp(t, 0.0f, 1.0f); + if (channel->sampler->interpolation == cgltf_interpolation_type_step) { + t = 0.0f; + } + for (w = 0; w < count; w++) { + // Cubic spline keyframes hold in-tangent, value, out-tangent per weight; the value is the middle. + cgltf_accessor_read_float(output, (cgltf_size)((k * stride + (stride == 3 ? 1 : 0)) * count + w), &a, 1); + cgltf_accessor_read_float(output, (cgltf_size)((next * stride + (stride == 3 ? 1 : 0)) * count + w), &b, 1); + out[w] = a + (b - a) * t; + } +} + + // ===== Animation (on a model instance's root node) ===== double animationGetTime(int32_t root) { diff --git a/src/physics.h b/src/physics.h index d29468b79..ea28ff444 100644 --- a/src/physics.h +++ b/src/physics.h @@ -100,6 +100,7 @@ int32_t physicsGetEvents(PhysicsEventT *events, int32_t maximum); bool physicsInit(void); void physicsQuit(void); bool physicsRaycast(Vec3T origin, Vec3T direction, float maxDistance, int32_t *node, Vec3T *point, Vec3T *normal); +void physicsSet2D(bool planar); void physicsSetEnabled(bool enabled); void physicsSetGravity(Vec3T gravity); void physicsUpdate(bool advance); diff --git a/src/physicsJolt.cpp b/src/physicsJolt.cpp index 423d8f350..b6d643f52 100644 --- a/src/physicsJolt.cpp +++ b/src/physicsJolt.cpp @@ -203,6 +203,7 @@ namespace { double accumulator; // Seconds owed to the fixed step uint64_t lastTick; bool enabled; + bool planar; // New bodies keep to the XY plane (2D games) }; @@ -568,6 +569,10 @@ bool bodyNew(int32_t node, BodyTypeE type, ShapeTypeE shape, float a, float b, f settings.mFriction = DEFAULT_FRICTION; settings.mRestitution = DEFAULT_BOUNCE; settings.mUserData = (JPH::uint64)(uint32_t)node; + if (_world->planar) { + // Moves in X and Y, turns about Z, nothing else: a 2D game's world. + settings.mAllowedDOFs = JPH::EAllowedDOFs::Plane2D; + } record->id = _world->system->GetBodyInterface().CreateAndAddBody(settings, JPH::EActivation::Activate); } if (record->id.IsInvalid()) { @@ -929,6 +934,16 @@ bool physicsRaycast(Vec3T origin, Vec3T direction, float maxDistance, int32_t *n } +// 2D mode for bodies made from now on: they move in X and Y and turn about Z only, so a 2D game +// can run its world in overlay coordinates (gravity pointing +Y then, since the overlay's Y runs +// down) and draw sprites where the nodes are. Bodies already made keep their freedom. +void physicsSet2D(bool planar) { + if (_world != nullptr) { + _world->planar = planar; + } +} + + // Pauses the simulation (bodies hold still) without losing it. void physicsSetEnabled(bool enabled) { if (_world != nullptr) { diff --git a/src/scene.c b/src/scene.c index ad4e56f19..1b3439912 100644 --- a/src/scene.c +++ b/src/scene.c @@ -64,13 +64,18 @@ #define SHADOW_BIAS 0.0015f #define SHADOW_MARGIN 1.05f // The fitted light frustum, a little larger than the scene #define SKIN_BOUNDS_GROW 1.5f // A skinned mesh moves beyond its bind pose +#define MAX_MORPHS 8 // Active morph targets per draw (the shader's limit) +#define MORPH_FLOATS 8 // Per target per vertex: position delta xyz + pad, normal delta xyz + pad // Matches DrawUniforms in scene.hlsl. typedef struct DrawUniformsS { - Mat4T modelViewProjection; - Mat4T model; - Mat4T normalMatrix; + Mat4T modelViewProjection; + Mat4T model; + Mat4T normalMatrix; + float morphWeights[MAX_MORPHS]; + int32_t morphTargets[MAX_MORPHS]; + int32_t morphInfo[4]; } DrawUniformsT; // Matches Light and FragmentUniforms in scene.hlsl. @@ -114,6 +119,9 @@ typedef struct MeshS { float *positions; // A CPU copy of the geometry (x, y, z per vertex) for physics shapes uint32_t *indices; int32_t vertexCount; + SDL_GPUBuffer *morphBuffer; // Morph target deltas, MORPH_FLOATS per vertex per target + char **morphNames; + int32_t morphCount; bool skinned; bool used; } MeshT; @@ -162,6 +170,8 @@ typedef struct NodeS { int32_t material; LightT light; bool castsShadow; + float *morphWeights; // One per target of the node's mesh + int32_t morphCount; int32_t *skinJoints; // Nodes whose world matrices drive a skinned mesh Mat4T *skinInverseBind; int32_t skinCount; @@ -208,6 +218,7 @@ typedef struct SceneS { ShadowT shadows[MAX_SHADOWS]; int32_t shadowCount; SDL_GPUTexture *white; // 1x1 stand-in for untextured materials + SDL_GPUBuffer *noMorphs; // Stand-in delta buffer for meshes without morph targets SDL_FColor background; Vec3T ambient; NodeT *nodes; @@ -243,7 +254,7 @@ static bool _createPipeline(int32_t variant); static SDL_GPUTexture *_createShadowArray(SDL_GPUTextureType type, int32_t layers, int32_t size); static bool _createShadowMaps(int32_t layers); static bool _createShadowPipeline(int32_t variant); -static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms); +static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms, uint32_t storageBuffers); static bool _createShaders(void); static SDL_GPUTextureFormat _depthFormat(void); static SDL_GPUTextureFormat _shadowFormat(void); @@ -256,7 +267,10 @@ static void _fillLights(FragmentUniformsT *uniforms); static void _fitShadows(int32_t drawCount); static void _fillSkin(const NodeT *node, SkinUniformsT *uniforms); static void _freeFeed(FeedT *feed); +static void _freeMorphs(MeshT *mesh); +static void _freeMorphWeights(NodeT *node); static void _freeSkin(NodeT *node); +static void _matchMorphWeights(NodeT *node); static void _freeMaterialTexture(MaterialT *material); static void _lathe(SceneVertexT **vertices, int32_t *vertexCount, uint32_t **indices, int32_t *indexCount, float bottomRadius, float topRadius, float height, int32_t segments); static int32_t _lookupPipeline(int32_t node); @@ -531,7 +545,7 @@ static bool _createPipeline(int32_t variant) { // Picks the blob for the format the device accepts. -static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms) { +static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderStage stage, uint32_t samplers, uint32_t uniforms, uint32_t storageBuffers) { SDL_GPUShaderCreateInfo info; SDL_GPUShaderFormat formats = SDL_GetGPUShaderFormats(_scene.device); SDL_GPUShader *result; @@ -557,6 +571,7 @@ static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderSta info.stage = stage; info.num_samplers = samplers; info.num_uniform_buffers = uniforms; + info.num_storage_buffers = storageBuffers; result = SDL_CreateGPUShader(_scene.device, &info); if (result == NULL) { utilTrace("Scene: shader %s: %s", shader->entryPoint, SDL_GetError()); @@ -566,10 +581,10 @@ static SDL_GPUShader *_createShader(const SceneShaderT *shader, SDL_GPUShaderSta static bool _createShaders(void) { - _scene.vertexStatic = _createShader(&sceneShaderVertexStatic, SDL_GPU_SHADERSTAGE_VERTEX, 0, 1); - _scene.vertexSkinned = _createShader(&sceneShaderVertexSkinned, SDL_GPU_SHADERSTAGE_VERTEX, 0, 2); - _scene.fragment = _createShader(&sceneShaderFragmentMain, SDL_GPU_SHADERSTAGE_FRAGMENT, 2, 1); - _scene.depthFragment = _createShader(&sceneShaderDepthMain, SDL_GPU_SHADERSTAGE_FRAGMENT, 0, 0); + _scene.vertexStatic = _createShader(&sceneShaderVertexStatic, SDL_GPU_SHADERSTAGE_VERTEX, 0, 1, 1); + _scene.vertexSkinned = _createShader(&sceneShaderVertexSkinned, SDL_GPU_SHADERSTAGE_VERTEX, 0, 2, 1); + _scene.fragment = _createShader(&sceneShaderFragmentMain, SDL_GPU_SHADERSTAGE_FRAGMENT, 2, 1, 0); + _scene.depthFragment = _createShader(&sceneShaderDepthMain, SDL_GPU_SHADERSTAGE_FRAGMENT, 0, 0, 0); return (_scene.vertexStatic != NULL) && (_scene.vertexSkinned != NULL) && (_scene.fragment != NULL) && (_scene.depthFragment != NULL); } @@ -783,6 +798,7 @@ static void _drawList(SDL_GPUCommandBuffer *commands, SDL_GPURenderPass *pass, i SDL_BindGPUGraphicsPipeline(pass, pipeline); lastPipeline = variant; } + memset(&drawUniforms, 0, sizeof(drawUniforms)); drawUniforms.modelViewProjection = mat4Multiply(*viewProjection, node->world); drawUniforms.model = node->world; if (mat4Invert(node->world, &inverse)) { @@ -790,7 +806,40 @@ static void _drawList(SDL_GPUCommandBuffer *commands, SDL_GPURenderPass *pass, i } else { drawUniforms.normalMatrix = identity; } + // The strongest active morph targets, up to the shader's limit. + if ((mesh->morphBuffer != NULL) && (node->morphCount == mesh->morphCount)) { + int32_t active = 0; + int32_t t; + + for (t = 0; t < node->morphCount; t++) { + if (node->morphWeights[t] == 0.0f) { + continue; + } + if (active < MAX_MORPHS) { + drawUniforms.morphWeights[active] = node->morphWeights[t]; + drawUniforms.morphTargets[active] = t; + active++; + } else { + // Replace the weakest chosen one if this is stronger. + int32_t weakest = 0; + int32_t k; + + for (k = 1; k < MAX_MORPHS; k++) { + if (fabsf(drawUniforms.morphWeights[k]) < fabsf(drawUniforms.morphWeights[weakest])) { + weakest = k; + } + } + if (fabsf(node->morphWeights[t]) > fabsf(drawUniforms.morphWeights[weakest])) { + drawUniforms.morphWeights[weakest] = node->morphWeights[t]; + drawUniforms.morphTargets[weakest] = t; + } + } + } + drawUniforms.morphInfo[0] = active; + drawUniforms.morphInfo[1] = mesh->vertexCount; + } SDL_PushGPUVertexUniformData(commands, 0, &drawUniforms, sizeof(drawUniforms)); + SDL_BindGPUVertexStorageBuffers(pass, 0, (mesh->morphBuffer != NULL) ? &mesh->morphBuffer : &_scene.noMorphs, 1); if (mesh->skinned && (node->skinCount > 0)) { // 8 KB per skinned draw; allocated once per pass that needs it. if (skinUniforms == NULL) { @@ -1051,6 +1100,29 @@ static void _freeMaterialTexture(MaterialT *material) { } +static void _freeMorphs(MeshT *mesh) { + int32_t x; + + if (mesh->morphBuffer != NULL) { + SDL_ReleaseGPUBuffer(_scene.device, mesh->morphBuffer); + mesh->morphBuffer = NULL; + } + for (x = 0; x < mesh->morphCount; x++) { + SDL_free(mesh->morphNames[x]); + } + SDL_free(mesh->morphNames); + mesh->morphNames = NULL; + mesh->morphCount = 0; +} + + +static void _freeMorphWeights(NodeT *node) { + SDL_free(node->morphWeights); + node->morphWeights = NULL; + node->morphCount = 0; +} + + static void _freeSkin(NodeT *node) { SDL_free(node->skinJoints); SDL_free(node->skinInverseBind); @@ -1131,6 +1203,24 @@ static void _lathe(SceneVertexT **vertices, int32_t *vertexCount, uint32_t **ind } +// Sizes the node's weight list to its mesh's targets (weights start at 0). +static void _matchMorphWeights(NodeT *node) { + int32_t count = ((node->mesh != NO_HANDLE) && meshValid(node->mesh)) ? _scene.meshes[node->mesh].morphCount : 0; + + if (count == node->morphCount) { + return; + } + _freeMorphWeights(node); + if (count > 0) { + node->morphWeights = SDL_calloc((size_t)count, sizeof(float)); + if (node->morphWeights == NULL) { + utilDie("Out of memory allocating morph weights."); + } + node->morphCount = count; + } +} + + // What the fragment shader samples for a material: its video feed, its image, or NULL. static SDL_GPUTexture *_materialTexture(const MaterialT *material) { if ((material->feed != NO_HANDLE) && (material->feed < _scene.feedCount) && _scene.feeds[material->feed].used) { @@ -1680,16 +1770,34 @@ bool meshDelete(int32_t mesh) { } SDL_free(_scene.meshes[mesh].positions); SDL_free(_scene.meshes[mesh].indices); + _freeMorphs(&_scene.meshes[mesh]); memset(&_scene.meshes[mesh], 0, sizeof(MeshT)); for (x = 0; x < _scene.nodeCount; x++) { if (_scene.nodes[x].used && (_scene.nodes[x].mesh == mesh)) { _scene.nodes[x].mesh = NO_HANDLE; + _freeMorphWeights(&_scene.nodes[x]); } } return true; } +// A morph target by name, or -1. +int32_t meshFindMorph(int32_t mesh, const char *name) { + int32_t x; + + if (!meshValid(mesh) || (name == NULL)) { + return NO_HANDLE; + } + for (x = 0; x < _scene.meshes[mesh].morphCount; x++) { + if ((_scene.meshes[mesh].morphNames[x] != NULL) && (strcmp(_scene.meshes[mesh].morphNames[x], name) == 0)) { + return x; + } + } + return NO_HANDLE; +} + + // The mesh's geometry as kept on the CPU: x, y, z per vertex and triangle indices. bool meshGetGeometry(int32_t mesh, const float **positions, int32_t *vertexCount, const uint32_t **indices, int32_t *indexCount) { if (!meshValid(mesh)) { @@ -1703,6 +1811,22 @@ bool meshGetGeometry(int32_t mesh, const float **positions, int32_t *vertexCount } +int32_t meshGetMorphCount(int32_t mesh) { + if (!meshValid(mesh)) { + return 0; + } + return _scene.meshes[mesh].morphCount; +} + + +const char *meshGetMorphName(int32_t mesh, int32_t target) { + if (!meshValid(mesh) || (target < 0) || (target >= _scene.meshes[mesh].morphCount) || (_scene.meshes[mesh].morphNames[target] == NULL)) { + return ""; + } + return _scene.meshes[mesh].morphNames[target]; +} + + // Raw geometry from a script: positions (3 per vertex), normals (3, may be NULL for flat // shading computed here), uvs (2, may be NULL), and triangle indices. int32_t meshNew(const float *positions, const float *normals, const float *uvs, int32_t vertexCount, const uint32_t *indices, int32_t indexCount) { @@ -1770,6 +1894,56 @@ int32_t meshPlane(float width, float depth) { } +// Gives the mesh morph targets: deltas holds, per target, per vertex, a position delta (x, y, z) +// and a normal delta (x, y, z), six floats; names may be NULL or hold NULL entries. Nodes using +// the mesh get a weight per target, all 0. +bool meshSetMorphTargets(int32_t mesh, const float *deltas, int32_t targetCount, const char **names) { + MeshT *m; + float *packed; + int32_t count; + int32_t x; + + if (!meshValid(mesh) || (deltas == NULL) || (targetCount <= 0)) { + return false; + } + m = &_scene.meshes[mesh]; + count = targetCount * m->vertexCount; + packed = SDL_calloc((size_t)count * MORPH_FLOATS, sizeof(float)); + if (packed == NULL) { + utilDie("Out of memory packing morph targets."); + } + // float4 pairs for the shader: xyz0 position delta, xyz0 normal delta. + for (x = 0; x < count; x++) { + packed[x * MORPH_FLOATS] = deltas[x * 6]; + packed[x * MORPH_FLOATS + 1] = deltas[x * 6 + 1]; + packed[x * MORPH_FLOATS + 2] = deltas[x * 6 + 2]; + packed[x * MORPH_FLOATS + 4] = deltas[x * 6 + 3]; + packed[x * MORPH_FLOATS + 5] = deltas[x * 6 + 4]; + packed[x * MORPH_FLOATS + 6] = deltas[x * 6 + 5]; + } + _freeMorphs(m); + m->morphBuffer = _uploadBuffer(SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ, packed, (uint32_t)((size_t)count * MORPH_FLOATS * sizeof(float))); + SDL_free(packed); + if (m->morphBuffer == NULL) { + return false; + } + m->morphNames = SDL_calloc((size_t)targetCount, sizeof(char *)); + if (m->morphNames == NULL) { + utilDie("Out of memory naming morph targets."); + } + for (x = 0; x < targetCount; x++) { + m->morphNames[x] = ((names != NULL) && (names[x] != NULL)) ? SDL_strdup(names[x]) : NULL; + } + m->morphCount = targetCount; + for (x = 0; x < _scene.nodeCount; x++) { + if (_scene.nodes[x].used && (_scene.nodes[x].mesh == mesh)) { + _matchMorphWeights(&_scene.nodes[x]); + } + } + return true; +} + + // Latitude/longitude sphere; segments around, half as many from pole to pole. int32_t meshSphere(float radius, int32_t segments) { SceneVertexT *vertices; @@ -1905,6 +2079,7 @@ bool nodeDelete(int32_t node) { } SDL_free(_scene.nodes[node].name); _freeSkin(&_scene.nodes[node]); + _freeMorphWeights(&_scene.nodes[node]); _scene.nodes[node].name = NULL; _scene.nodes[node].used = false; return true; @@ -1979,6 +2154,22 @@ int32_t nodeGetMesh(int32_t node) { } +int32_t nodeGetMorphCount(int32_t node) { + if (!nodeValid(node)) { + return 0; + } + return _scene.nodes[node].morphCount; +} + + +float nodeGetMorphWeight(int32_t node, int32_t target) { + if (!nodeValid(node) || (target < 0) || (target >= _scene.nodes[node].morphCount)) { + return 0.0f; + } + return _scene.nodes[node].morphWeights[target]; +} + + const char *nodeGetName(int32_t node) { if (!nodeValid(node) || (_scene.nodes[node].name == NULL)) { return ""; @@ -2077,9 +2268,6 @@ bool nodeMove(int32_t node, Vec3T delta) { int32_t nodeNew(int32_t parent) { int32_t node; - if (_scene.device == NULL) { - return NO_HANDLE; - } if (parent == NO_HANDLE) { parent = SCENE_ROOT_NODE; } @@ -2115,6 +2303,17 @@ bool nodeSetMesh(int32_t node, int32_t mesh, int32_t material) { } _scene.nodes[node].mesh = mesh; _scene.nodes[node].material = material; + _matchMorphWeights(&_scene.nodes[node]); + return true; +} + + +// How much of a morph target the node's mesh shows (usually 0 to 1). +bool nodeSetMorphWeight(int32_t node, int32_t target, float weight) { + if (!nodeValid(node) || (target < 0) || (target >= _scene.nodes[node].morphCount)) { + return false; + } + _scene.nodes[node].morphWeights[target] = weight; return true; } @@ -2293,13 +2492,15 @@ bool sceneInit(SDL_GPUDevice *device, SDL_Renderer *renderer) { _scene.antialias = true; _scene.sampleCount = SDL_GPU_SAMPLECOUNT_1; _scene.shadowSize = SHADOW_SIZE; + // The node tree is plain data and exists on every machine (physics bodies live on nodes); + // everything from here on needs the GPU, and without one the layer cannot be enabled. + _allocNode(); + nodeSetName(SCENE_ROOT_NODE, "root"); if (device == NULL) { return false; } _scene.depthFormat = _depthFormat(); _scene.shadowFormat = _shadowFormat(); - _allocNode(); - nodeSetName(SCENE_ROOT_NODE, "root"); if (!_createShaders()) { sceneQuit(); return false; @@ -2326,8 +2527,13 @@ bool sceneInit(SDL_GPUDevice *device, SDL_Renderer *renderer) { _scene.white = _uploadTexture(pixel); SDL_DestroySurface(pixel); } + { + float zero[MORPH_FLOATS] = { 0.0f }; + + _scene.noMorphs = _uploadBuffer(SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ, zero, sizeof(zero)); + } _scene.shadowMapsNone = _createShadowArray(SDL_GPU_TEXTURETYPE_2D_ARRAY, 1, 1); - if ((_scene.sampler == NULL) || (_scene.shadowSampler == NULL) || (_scene.white == NULL) || (_scene.shadowMapsNone == NULL)) { + if ((_scene.sampler == NULL) || (_scene.shadowSampler == NULL) || (_scene.white == NULL) || (_scene.shadowMapsNone == NULL) || (_scene.noMorphs == NULL)) { utilTrace("Scene: %s", SDL_GetError()); sceneQuit(); return false; @@ -2398,11 +2604,15 @@ void sceneQuit(void) { if (_scene.white != NULL) { SDL_ReleaseGPUTexture(_scene.device, _scene.white); } + if (_scene.noMorphs != NULL) { + SDL_ReleaseGPUBuffer(_scene.device, _scene.noMorphs); + } _destroyTargets(); } for (x = 0; x < _scene.nodeCount; x++) { SDL_free(_scene.nodes[x].name); _freeSkin(&_scene.nodes[x]); + _freeMorphWeights(&_scene.nodes[x]); } SDL_free(_scene.nodes); SDL_free(_scene.meshes); @@ -2728,7 +2938,7 @@ Vec3T sceneUnproject(float x, float y, float distance) { void sceneUpdateTransforms(void) { Mat4T identity = mat4Identity(); - if (_scene.device != NULL) { + if (_scene.nodeCount > 0) { _updateWorld(SCENE_ROOT_NODE, &identity, true); } } diff --git a/src/scene.h b/src/scene.h index 7f250b811..f25ada4f6 100644 --- a/src/scene.h +++ b/src/scene.h @@ -98,11 +98,15 @@ int32_t meshCone(float radius, float height, int32_t segments); int32_t meshCylinder(float radius, float height, int32_t segments); bool meshDelete(int32_t mesh); bool meshGetGeometry(int32_t mesh, const float **positions, int32_t *vertexCount, const uint32_t **indices, int32_t *indexCount); +int32_t meshFindMorph(int32_t mesh, const char *name); +int32_t meshGetMorphCount(int32_t mesh); +const char *meshGetMorphName(int32_t mesh, int32_t target); int32_t meshNew(const float *positions, const float *normals, const float *uvs, int32_t vertexCount, const uint32_t *indices, int32_t indexCount); int32_t meshNewVertices(const SceneVertexT *vertices, int32_t vertexCount, const uint32_t *indices, int32_t indexCount, bool skinned); int32_t meshPlane(float width, float depth); int32_t meshSphere(float radius, int32_t segments); int32_t meshTorus(float radius, float tubeRadius, int32_t segments); +bool meshSetMorphTargets(int32_t mesh, const float *deltas, int32_t targetCount, const char **names); bool meshValid(int32_t mesh); bool nodeDelete(int32_t node); @@ -111,6 +115,8 @@ int32_t nodeGetChild(int32_t node, int32_t index); int32_t nodeGetChildCount(int32_t node); uint32_t nodeGetGeneration(int32_t node); int32_t nodeGetMesh(int32_t node); +int32_t nodeGetMorphCount(int32_t node); +float nodeGetMorphWeight(int32_t node, int32_t target); const char *nodeGetName(int32_t node); int32_t nodeGetParent(int32_t node); Vec3T nodeGetPosition(int32_t node); @@ -123,6 +129,7 @@ bool nodeMove(int32_t node, Vec3T delta); int32_t nodeNew(int32_t parent); bool nodeRotate(int32_t node, QuatT delta); bool nodeSetMesh(int32_t node, int32_t mesh, int32_t material); +bool nodeSetMorphWeight(int32_t node, int32_t target, float weight); bool nodeSetName(int32_t node, const char *name); bool nodeSetParent(int32_t node, int32_t parent); bool nodeSetPosition(int32_t node, Vec3T position); diff --git a/src/shaders/build.sh b/src/shaders/build.sh deleted file mode 100755 index 9c2538981..000000000 --- a/src/shaders/build.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -# -# Compiles scene.hlsl into sceneShaders.h: SPIR-V (Vulkan), DXIL (Direct3D 12) and MSL (Metal) -# for each entry point, as C arrays. Needs SDL_shadercross's command line tool (built from -# https://github.com/libsdl-org/SDL_shadercross with SDLSHADERCROSS_DXC=ON); pass its path as the -# first argument or put it on PATH. The engine build never runs this: the output is checked in. -# -# Usage: src/shaders/build.sh [path/to/shadercross] - -set -euo pipefail -cd "$(dirname "$0")" -SHADERCROSS=${1:-shadercross} -OUT=sceneShaders.h -TMP=$(mktemp -d) -trap 'rm -rf "$TMP"' EXIT - -ENTRIES="vertexStatic:vertex vertexSkinned:vertex fragmentMain:fragment depthMain:fragment" - -emit() { - # emit NAME FILE: a C array from a binary file - local name=$1 - local file=$2 - echo "static const unsigned char ${name}[] = {" - od -An -v -tx1 "$file" | sed 's/ \([0-9a-f][0-9a-f]\)/0x\1,/g; s/^/\t/' - echo "};" -} - -{ - echo "// Generated by build.sh from scene.hlsl with SDL_shadercross; do not edit." - echo "// SPIR-V for Vulkan, DXIL for Direct3D 12, MSL for Metal, one set per entry point." - echo - echo "#ifndef SCENE_SHADERS_H" - echo "#define SCENE_SHADERS_H" - echo - echo "#include " - echo - echo "typedef struct SceneShaderS {" - echo " const char *entryPoint;" - echo " const unsigned char *spirv;" - echo " size_t spirvSize;" - echo " const unsigned char *dxil;" - echo " size_t dxilSize;" - echo " const unsigned char *msl;" - echo " size_t mslSize;" - echo "} SceneShaderT;" - echo - for entry in $ENTRIES; do - name=${entry%%:*} - stage=${entry##*:} - for format in SPIRV DXIL MSL; do - ext=$(echo "$format" | tr '[:upper:]' '[:lower:]') - "$SHADERCROSS" scene.hlsl -s HLSL -d "$format" -t "$stage" -e "$name" -o "$TMP/$name.$ext" >&2 - emit "_${name}${format}" "$TMP/$name.$ext" - echo - done - echo "static const SceneShaderT sceneShader$(echo "${name:0:1}" | tr '[:lower:]' '[:upper:]')${name:1} = { \"$name\", _${name}SPIRV, sizeof(_${name}SPIRV), _${name}DXIL, sizeof(_${name}DXIL), _${name}MSL, sizeof(_${name}MSL) };" - echo - done - echo "#endif" -} > "$OUT" -echo "Wrote $OUT" diff --git a/src/shaders/scene.hlsl b/src/shaders/scene.hlsl index 25a39d2f9..1500800ce 100644 --- a/src/shaders/scene.hlsl +++ b/src/shaders/scene.hlsl @@ -9,6 +9,7 @@ #define MAX_LIGHTS 8 #define MAX_JOINTS 128 #define MAX_SHADOWS 8 +#define MAX_MORPHS 8 // Active morph targets per draw #define SHADOW_NONE 0 #define SHADOW_MAP 1 @@ -25,8 +26,14 @@ cbuffer DrawUniforms : register(b0, space1) { float4x4 modelViewProjection; float4x4 model; float4x4 normalMatrix; // Inverse transpose of model, for normals under non-uniform scale + float4 morphWeights[2]; // Up to MAX_MORPHS active targets ... + int4 morphTargets[2]; // ... and which targets they are + int4 morphInfo; // x = active count, y = vertices per target }; +// Morph target deltas: per target, per vertex, a position delta then a normal delta. +StructuredBuffer morphDeltas : register(t0, space0); + cbuffer SkinUniforms : register(b1, space1) { float4x4 joints[MAX_JOINTS]; }; @@ -47,32 +54,59 @@ struct VertexOutput { }; -VertexOutput vertexStatic(VertexInput input) { - VertexOutput output; +// Adds the active morph targets' deltas to a vertex. +void morph(uint vertex, inout float3 position, inout float3 normal) { + int x; - output.position = mul(modelViewProjection, float4(input.position, 1.0)); - output.worldPosition = mul(model, float4(input.position, 1.0)).xyz; - output.worldNormal = normalize(mul((float3x3)normalMatrix, input.normal)); + for (x = 0; x < MAX_MORPHS; x++) { + float weight; + uint base; + + if (x >= morphInfo.x) { + continue; + } + weight = morphWeights[x / 4][x % 4]; + base = ((uint)morphTargets[x / 4][x % 4] * (uint)morphInfo.y + vertex) * 2; + position += morphDeltas[base].xyz * weight; + normal += morphDeltas[base + 1].xyz * weight; + } +} + + +VertexOutput vertexStatic(VertexInput input, uint vertex : SV_VertexID) { + VertexOutput output; + float3 position = input.position; + float3 normal = input.normal; + + morph(vertex, position, normal); + output.position = mul(modelViewProjection, float4(position, 1.0)); + output.worldPosition = mul(model, float4(position, 1.0)).xyz; + output.worldNormal = normalize(mul((float3x3)normalMatrix, normal)); output.uv = input.uv; return output; } -VertexOutput vertexSkinned(VertexInput input) { +VertexOutput vertexSkinned(VertexInput input, uint vertex : SV_VertexID) { VertexOutput output; - float4 position = float4(input.position, 1.0); + float3 morphed = input.position; + float3 normal = input.normal; + float4 position; float4 skinned; float3 skinnedNormal; + morph(vertex, morphed, normal); + position = float4(morphed, 1.0); + // The weighted sum of the joint transforms, applied to the position and the normal. skinned = mul(joints[input.joints.x], position) * input.weights.x + mul(joints[input.joints.y], position) * input.weights.y + mul(joints[input.joints.z], position) * input.weights.z + mul(joints[input.joints.w], position) * input.weights.w; - skinnedNormal = mul((float3x3)joints[input.joints.x], input.normal) * input.weights.x - + mul((float3x3)joints[input.joints.y], input.normal) * input.weights.y - + mul((float3x3)joints[input.joints.z], input.normal) * input.weights.z - + mul((float3x3)joints[input.joints.w], input.normal) * input.weights.w; + skinnedNormal = mul((float3x3)joints[input.joints.x], normal) * input.weights.x + + mul((float3x3)joints[input.joints.y], normal) * input.weights.y + + mul((float3x3)joints[input.joints.z], normal) * input.weights.z + + mul((float3x3)joints[input.joints.w], normal) * input.weights.w; output.position = mul(modelViewProjection, skinned); output.worldPosition = mul(model, skinned).xyz; output.worldNormal = normalize(mul((float3x3)normalMatrix, skinnedNormal)); diff --git a/src/shaders/sceneShaders.h b/src/shaders/sceneShaders.h deleted file mode 100644 index 95d8d9a7c..000000000 --- a/src/shaders/sceneShaders.h +++ /dev/null @@ -1,4584 +0,0 @@ -// Generated by build.sh from scene.hlsl with SDL_shadercross; do not edit. -// SPIR-V for Vulkan, DXIL for Direct3D 12, MSL for Metal, one set per entry point. - -#ifndef SCENE_SHADERS_H -#define SCENE_SHADERS_H - -#include - -typedef struct SceneShaderS { - const char *entryPoint; - const unsigned char *spirv; - size_t spirvSize; - const unsigned char *dxil; - size_t dxilSize; - const unsigned char *msl; - size_t mslSize; -} SceneShaderT; - -static const unsigned char _vertexStaticSPIRV[] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0x52,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x01,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74, - 0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00, - 0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00,0x0a,0x00,0x00,0x00,0x74,0x79,0x70,0x65, - 0x2e,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x08,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, - 0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e,0x00, - 0x06,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, - 0x6c,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00, - 0x05,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, - 0x74,0x79,0x70,0x65,0x2e,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, - 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00, - 0x05,0x00,0x08,0x00,0x0e,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x07,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,0x6d,0x65, - 0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x06,0x00,0x05,0x00, - 0x0e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x00, - 0x06,0x00,0x06,0x00,0x0e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x62,0x61,0x73,0x65, - 0x43,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0e,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x65,0x6d,0x69,0x73,0x73,0x69,0x76,0x65,0x00,0x00,0x00,0x00, - 0x06,0x00,0x06,0x00,0x0e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x74,0x65, - 0x72,0x69,0x61,0x6c,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x0e,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x63,0x6f,0x75,0x6e,0x74,0x73,0x00,0x00,0x06,0x00,0x07,0x00, - 0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61, - 0x72,0x61,0x6d,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0e,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x74,0x72,0x69,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0e,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x00,0x00,0x06,0x00,0x05,0x00, - 0x0e,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x6c,0x69,0x67,0x68,0x74,0x73,0x00,0x00, - 0x05,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x4c,0x69,0x67,0x68,0x74,0x00,0x00,0x00, - 0x06,0x00,0x07,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x6f,0x73,0x69, - 0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00, - 0x0f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f, - 0x6e,0x52,0x61,0x6e,0x67,0x65,0x00,0x00,0x06,0x00,0x05,0x00,0x0f,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x05,0x00, - 0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x63,0x6f,0x6e,0x65,0x00,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x11,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67, - 0x65,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x62,0x61,0x73,0x65, - 0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00,0x05,0x00,0x06,0x00,0x13,0x00,0x00,0x00, - 0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x00, - 0x05,0x00,0x05,0x00,0x14,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x00,0x05,0x00,0x07,0x00,0x15,0x00,0x00,0x00,0x74,0x79,0x70,0x65, - 0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67,0x65,0x2e,0x61,0x72,0x72,0x61,0x79,0x00, - 0x05,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61, - 0x70,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x17,0x00,0x00,0x00,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x05,0x00,0x07,0x00, - 0x03,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x04,0x00,0x00,0x00, - 0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31, - 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x05,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76, - 0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x05,0x00,0x07,0x00, - 0x08,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x31,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x09,0x00,0x00,0x00, - 0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x32,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74, - 0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x03,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x04,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x09,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0b,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x17,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x17,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, - 0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x80,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0c,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0c,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0f,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0f,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0f,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0e,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x60,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x70,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0e,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0e,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0xf0,0x02,0x00,0x00, - 0x47,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x00,0x03,0x00, - 0x1e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x1e,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x2b,0x00,0x04,0x00,0x1c,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x1c,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x22,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x23,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x05,0x00,0x0a,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x24,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x25,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x25,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x1c,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x1e,0x00,0x03,0x00, - 0x0c,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x27,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x25,0x00,0x00,0x00, - 0x28,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x19,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x1a,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0f,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x1c,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x28,0x00,0x00,0x00, - 0x1e,0x00,0x0c,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, - 0x19,0x00,0x09,0x00,0x11,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x1a,0x00,0x02,0x00,0x13,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x2b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x19,0x00,0x09,0x00, - 0x15,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x2e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x2f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2f,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x34,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x35,0x00,0x00,0x00, - 0x34,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x36,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x37,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2b,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2b,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2e,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2e,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x34,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x38,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x2f,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x36,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x1d,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x3d,0x00,0x00,0x00, - 0x3c,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x1e,0x00,0x00,0x00,0x3e,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x1e,0x00,0x00,0x00, - 0x3f,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x1e,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x50,0x00,0x07,0x00,0x22,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x3e,0x00,0x00,0x00, - 0x3f,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x90,0x00,0x05,0x00, - 0x22,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x3d,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x36,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x44,0x00,0x00,0x00, - 0x43,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x22,0x00,0x00,0x00,0x45,0x00,0x00,0x00, - 0x41,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2d,0x00,0x00,0x00, - 0x46,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x36,0x00,0x00,0x00, - 0x47,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x23,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x22,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x4f,0x00,0x08,0x00,0x2d,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x49,0x00,0x00,0x00, - 0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x22,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x48,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2d,0x00,0x00,0x00,0x4c,0x00,0x00,0x00, - 0x4b,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x22,0x00,0x00,0x00,0x4d,0x00,0x00,0x00, - 0x48,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x2d,0x00,0x00,0x00, - 0x4e,0x00,0x00,0x00,0x4d,0x00,0x00,0x00,0x4d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x37,0x00,0x00,0x00, - 0x4f,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x4e,0x00,0x00,0x00, - 0x90,0x00,0x05,0x00,0x2d,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x3a,0x00,0x00,0x00, - 0x4f,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x2d,0x00,0x00,0x00,0x51,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x06,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x07,0x00,0x00,0x00, - 0x46,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x51,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, - 0x38,0x00,0x01,0x00, -}; - -static const unsigned char _vertexStaticDXIL[] = { - 0x44,0x58,0x42,0x43,0x0d,0x39,0xe0,0xb8,0x20,0x37,0x03,0xc3,0xcf,0xb8,0x25,0xa1, - 0xee,0x9e,0xbb,0x0b,0x01,0x00,0x00,0x00,0x08,0x16,0x00,0x00,0x07,0x00,0x00,0x00, - 0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x70,0x01,0x00,0x00, - 0xcc,0x02,0x00,0x00,0x48,0x0a,0x00,0x00,0x64,0x0a,0x00,0x00,0x53,0x46,0x49,0x30, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, - 0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x00,0x00,0x00,0x00,0x4f,0x53,0x47,0x31,0xa0,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x03,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x50,0x53,0x56,0x30,0x54,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x01,0x00,0x00,0x00,0x03,0x04,0x00,0x03,0x04,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x44,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x76,0x65,0x72,0x74,0x65, - 0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x02,0x00,0x00,0x25,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x02,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x03,0x44,0x03,0x03,0x04,0x00,0x00,0x07,0xf0,0x00,0x00, - 0x07,0xf0,0x00,0x00,0x07,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0x00,0x00, - 0x70,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, - 0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x54,0x41,0x54, - 0x74,0x07,0x00,0x00,0x60,0x00,0x01,0x00,0xdd,0x01,0x00,0x00,0x44,0x58,0x49,0x4c, - 0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x5c,0x07,0x00,0x00,0x42,0x43,0xc0,0xde, - 0x21,0x0c,0x00,0x00,0xd4,0x01,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39, - 0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02, - 0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x52,0x88, - 0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90, - 0x91,0x22,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x29,0x46,0x06, - 0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07, - 0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff, - 0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00, - 0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, - 0x28,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, - 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, - 0x10,0x78,0x23,0x00,0x25,0x00,0x14,0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00,0x29, - 0xc6,0x20,0x84,0x14,0x42,0xa6,0x18,0x80,0x10,0x52,0x06,0xa1,0x82,0x0c,0x32,0xc6, - 0x18,0x63,0x90,0x2a,0xc3,0x20,0x83,0xd8,0x51,0xc3,0xe5,0x4f,0xd8,0x43,0x48,0x3e, - 0xb7,0x51,0xc5,0x4a,0x4c,0x7e,0x71,0xdb,0x88,0x18,0x63,0x0c,0x2a,0xf7,0x0c,0x97, - 0x3f,0x61,0x0f,0x21,0xf9,0x21,0xd0,0x0c,0x0b,0x81,0x82,0x57,0x08,0x47,0x20,0xa1, - 0x58,0x8a,0x41,0xc6,0x18,0x34,0xe7,0x08,0x82,0x62,0x40,0x52,0x08,0xa9,0x64,0x07, - 0x02,0x86,0x11,0x88,0x21,0x09,0xf2,0x71,0x87,0x23,0x4d,0x0b,0x80,0x39,0xd4,0xe4, - 0x4f,0xd8,0x43,0xfc,0x5d,0x04,0x58,0x6e,0x83,0x14,0x4e,0xc4,0x48,0x68,0xd0,0x5a, - 0xd3,0x4e,0x06,0x02,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79, - 0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d, - 0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0, - 0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e, - 0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73, - 0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10, - 0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, - 0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78, - 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e, - 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28,0x40,0x00,0x04,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60, - 0xc8,0x13,0x01,0x01,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02, - 0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x20,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x32,0x1e,0x98,0x18,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22, - 0x25,0x30,0x02,0x50,0x0c,0x05,0x38,0x50,0x04,0x85,0x50,0x06,0xe5,0x50,0x12,0x05, - 0x18,0x50,0x80,0x02,0xe5,0x51,0x60,0xa5,0x55,0x2c,0x45,0x2e,0x50,0x10,0x85,0x41, - 0xa5,0x24,0x46,0x00,0xca,0xa0,0x08,0x0a,0x81,0x6a,0x0d,0xd0,0x9e,0x01,0x20,0x3e, - 0x03,0x40,0x7d,0x2c,0x46,0x81,0x4e,0x90,0x0f,0x4e,0x90,0x0f,0x4e,0x90,0x0f,0x00, - 0x78,0x80,0x07,0x78,0x00,0x40,0x04,0x84,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00, - 0xbc,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c, - 0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25, - 0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10, - 0x08,0x64,0x82,0x40,0x24,0x1b,0x84,0x81,0xd8,0x20,0x10,0x04,0x05,0xbb,0xb9,0x09, - 0x02,0xa1,0x6c,0x18,0x0e,0x84,0x98,0x20,0x70,0x61,0xc0,0x88,0x2e,0x0f,0xae,0xec, - 0x8b,0x48,0x2e,0xec,0xae,0xca,0x2d,0xcd,0xec,0x4d,0xae,0x6d,0x6e,0x82,0x40,0x2c, - 0x1b,0x10,0x42,0x59,0x06,0x62,0x60,0x80,0x0d,0x41,0xb3,0x81,0x00,0x00,0x07,0x98, - 0x20,0x6c,0x60,0x40,0x30,0x20,0x92,0x0b,0xbb,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b, - 0x9b,0xfb,0x6a,0x7b,0x23,0x2b,0x63,0xb3,0x4a,0x2b,0xbb,0x83,0x92,0x7b,0x53,0x2b, - 0x1b,0xa3,0x4b,0x7b,0x73,0x9b,0x20,0x10,0xcc,0x04,0x81,0x68,0x36,0x0c,0xd3,0x24, - 0x4d,0x10,0x08,0x67,0x82,0x40,0x3c,0x13,0x04,0x02,0x9a,0x20,0x54,0xdf,0x06,0x05, - 0x89,0x24,0xaa,0x22,0xac,0xeb,0xc2,0x28,0x11,0xc9,0x85,0xdd,0x55,0xb9,0xa5,0x99, - 0xbd,0xc9,0xb5,0xcd,0x7d,0xb5,0xbd,0x91,0x95,0xb1,0x4d,0x10,0x88,0x68,0x03,0x82, - 0x68,0x12,0x55,0x6d,0xd6,0xc5,0x8c,0x48,0x2e,0xec,0xae,0xca,0x2d,0xcd,0xec,0x4d, - 0xae,0x6d,0xee,0xcb,0xed,0x4d,0xae,0x2d,0x8c,0xad,0x29,0x8c,0x4e,0x2e,0x0d,0x6f, - 0x82,0x40,0x48,0x1b,0x10,0xa4,0x93,0xa8,0xca,0xb3,0xae,0x0d,0x04,0x93,0x71,0xdf, - 0x86,0x81,0x80,0xc0,0x60,0x82,0x20,0x00,0x1b,0x80,0x0d,0x03,0x31,0x06,0x63,0xb0, - 0x21,0x20,0x83,0x0d,0xc3,0x20,0x06,0x65,0x30,0x41,0xe8,0xc4,0x60,0x43,0x70,0x06, - 0x64,0xec,0xca,0xe4,0xe8,0xca,0xf0,0xa6,0xe8,0xc2,0xe8,0xd2,0xc6,0x88,0x50,0x15, - 0x61,0x0d,0x3d,0x3d,0x49,0x11,0x4d,0x10,0x0a,0x6d,0x82,0x50,0x6c,0x1b,0x02,0x62, - 0x82,0x50,0x70,0x1b,0x84,0xca,0xda,0xb0,0x10,0x6a,0xb0,0x06,0x6c,0xd0,0x06,0x6c, - 0x30,0xb8,0x01,0xc1,0x06,0x6f,0xb0,0x21,0x18,0x36,0x2c,0x83,0x1a,0xac,0x01,0x1b, - 0xc4,0x01,0x1b,0x0c,0x6e,0x30,0xb0,0xc1,0x1b,0x6c,0x08,0xa4,0x09,0x42,0xd1,0x6d, - 0x10,0xaa,0x6a,0xc3,0x22,0xa9,0xc1,0x1a,0xb0,0xc1,0x1c,0xb0,0xc1,0x40,0x07,0x12, - 0x1b,0xd4,0xc1,0x86,0x01,0x0e,0xe4,0xc0,0x0e,0x36,0x2c,0x84,0x1a,0xac,0x01,0x1b, - 0xb4,0x01,0x1d,0x0c,0x6e,0x40,0xb0,0xc1,0x1b,0x6c,0x58,0x06,0x35,0x58,0x03,0x36, - 0x88,0x03,0x3a,0x18,0xdc,0x60,0x60,0x83,0x37,0xd8,0xb0,0x48,0x6a,0xb0,0x06,0x6c, - 0x30,0x07,0x74,0x30,0xd0,0x81,0xc4,0x06,0x75,0xc0,0x65,0xca,0xea,0x0b,0xea,0x6d, - 0x2e,0x8d,0x2e,0xed,0xcd,0x6d,0x82,0x50,0x78,0x13,0x04,0x62,0xda,0x20,0x54,0x7d, - 0xb0,0x61,0xa9,0xf6,0x60,0x0d,0xdc,0xa0,0x0d,0xf8,0x60,0xe0,0x83,0x8a,0x0d,0xfc, - 0x60,0x03,0x81,0x07,0x79,0xa0,0x07,0x7f,0xb0,0x61,0xb8,0x03,0x50,0x00,0x36,0x14, - 0x62,0x90,0x06,0xa1,0xf0,0x00,0x8c,0xc2,0xe4,0xe4,0xc2,0xf2,0xbe,0xd8,0xde,0xc6, - 0xc2,0xd8,0xbe,0xc4,0xf2,0xe8,0xca,0xe6,0x26,0x08,0x04,0x45,0x28,0x4c,0x4e,0x2e, - 0x2c,0xef,0x8b,0xed,0x6d,0x2c,0x8c,0xed,0x8b,0x8d,0x6c,0x8e,0x6e,0x82,0x40,0x54, - 0x34,0xcc,0xd8,0xde,0xc2,0xe8,0xe6,0x26,0x08,0x84,0xc5,0x22,0xcd,0x6d,0x8e,0x6e, - 0x6e,0x82,0x40,0x5c,0x34,0xe6,0xd2,0xce,0xbe,0xd8,0xc8,0x26,0x08,0x04,0x46,0x63, - 0x2e,0xed,0xec,0x6b,0x8e,0x6e,0x82,0x40,0x64,0x1b,0x98,0x51,0x20,0x85,0x52,0x30, - 0x85,0x53,0x40,0x85,0x54,0x50,0x85,0x55,0x60,0x85,0x56,0x70,0x85,0x2a,0x6c,0x6c, - 0x76,0x6d,0x2e,0x69,0x64,0x65,0x6e,0x74,0x53,0x82,0xa0,0x0a,0x19,0x9e,0x8b,0x5d, - 0x99,0xdc,0x5c,0xda,0x9b,0xdb,0x94,0x80,0x68,0x42,0x86,0xe7,0x62,0x17,0xc6,0x66, - 0x57,0x26,0x37,0x25,0x28,0xea,0x90,0xe1,0xb9,0xcc,0xa1,0x85,0x91,0x95,0xc9,0x35, - 0xbd,0x91,0x95,0xb1,0x4d,0x09,0x90,0x32,0x64,0x78,0x2e,0x72,0x65,0x73,0x6f,0x75, - 0x72,0x63,0x65,0x73,0x53,0x02,0xa7,0x12,0x19,0x9e,0x0b,0x5d,0x1e,0x5c,0x59,0x90, - 0x9b,0xdb,0x1b,0x5d,0x18,0x5d,0xda,0x9b,0xdb,0xdc,0x14,0x01,0x0c,0xca,0xa0,0x0e, - 0x19,0x9e,0x8b,0x5d,0x5a,0xd9,0x5d,0x12,0xd9,0x14,0x5d,0x18,0x5d,0xd9,0x94,0xe0, - 0x0c,0xea,0x90,0xe1,0xb9,0x94,0xb9,0xd1,0xc9,0xe5,0x41,0xbd,0xa5,0xb9,0xd1,0xcd, - 0x4d,0x09,0x42,0xa1,0x0b,0x19,0x9e,0xcb,0xd8,0x5b,0x9d,0x1b,0x5d,0x99,0xdc,0xdc, - 0x94,0xc0,0x15,0x00,0x79,0x18,0x00,0x00,0x4c,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, - 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, - 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, - 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, - 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, - 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, - 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, - 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, - 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, - 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, - 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, - 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, - 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, - 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, - 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, - 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, - 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, - 0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b, - 0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4, - 0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e,0xf0,0x00,0x00,0x00,0x71,0x20,0x00,0x00, - 0x20,0x00,0x00,0x00,0x56,0xb0,0x0d,0x97,0xef,0x3c,0xbe,0x10,0x50,0x45,0x41,0x44, - 0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6,0x17,0xb7,0x6d,0x07,0xd2,0x70,0xf9,0xce, - 0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34,0xc3,0x42,0xd8,0x80,0x33,0x5c,0xbe,0xf3, - 0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16,0x30,0x0d,0x97,0xef,0x3c,0xfe,0xe2,0x00, - 0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d,0x02,0xd5,0x70,0xf9,0xce,0xe3,0x4b,0x93, - 0x13,0x11,0x28,0x35,0x3d,0xd4,0xe4,0x17,0xb7,0x6d,0x06,0xd2,0x70,0xf9,0xce,0xe3, - 0x4f,0x44,0x34,0x21,0x40,0x84,0xf9,0xc5,0x6d,0x1b,0xc1,0x33,0x5c,0xbe,0xf3,0xf8, - 0x54,0x03,0x44,0x98,0x5f,0xdc,0xb6,0x01,0x30,0x15,0x11,0x4d,0xc4,0xc5,0x4e,0xc0, - 0x84,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x96,0x06,0xb3,0xb4,0xf8,0xc3,0xba,0x4b,0xc2,0x7c,0x62,0x4c, - 0xf4,0xab,0x45,0x92,0x44,0x58,0x49,0x4c,0x9c,0x0b,0x00,0x00,0x60,0x00,0x01,0x00, - 0xe7,0x02,0x00,0x00,0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00, - 0x84,0x0b,0x00,0x00,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xde,0x02,0x00,0x00, - 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91, - 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, - 0x1e,0x04,0x8b,0x62,0x80,0x18,0x45,0x02,0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14, - 0x38,0x08,0x18,0x4b,0x0a,0x32,0x62,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5, - 0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90,0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c, - 0xe1,0x83,0xe5,0x8a,0x04,0x31,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00, - 0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07,0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff, - 0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff,0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00, - 0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06, - 0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x2c,0x00,0x00,0x00,0x32,0x22,0x88,0x09, - 0x20,0x64,0x85,0x04,0x13,0x23,0xa4,0x84,0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14, - 0x12,0x4c,0x8c,0x8c,0x0b,0x84,0xc4,0x4c,0x10,0x84,0xc1,0x08,0x40,0x09,0x00,0x0a, - 0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00,0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51, - 0x0c,0x80,0x20,0x88,0x62,0x20,0xa4,0x20,0x03,0x31,0x0c,0xc3,0x30,0x0c,0xa4,0x94, - 0x61,0x20,0x06,0x62,0x8e,0x1a,0x2e,0x7f,0xc2,0x1e,0x42,0xf2,0xb9,0x8d,0x2a,0x56, - 0x62,0xf2,0x8b,0xdb,0x46,0xc4,0x30,0x0c,0x03,0x15,0xf7,0x0c,0x97,0x3f,0x61,0x0f, - 0x21,0xf9,0x21,0xd0,0x0c,0x0b,0x81,0x82,0xa7,0x10,0x0e,0x01,0x11,0x14,0x95,0x62, - 0x20,0x86,0x61,0xa0,0x69,0x8e,0x20,0x28,0x06,0x44,0x14,0x04,0x51,0x91,0x35,0x10, - 0x30,0x8c,0x40,0x0c,0x49,0x90,0x1d,0x77,0x38,0xd2,0xb4,0x00,0x98,0x43,0x4d,0xfe, - 0x84,0x3d,0xc4,0xdf,0x45,0x80,0xe5,0x36,0x48,0xe1,0x44,0x8c,0x84,0x06,0x4d,0xd3, - 0x68,0x4b,0x06,0x22,0x09,0x06,0xf2,0x68,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0, - 0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50, - 0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07, - 0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78, - 0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0, - 0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07, - 0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d, - 0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07, - 0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28, - 0x40,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xc8,0x13,0x01,0x01,0x30,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02,0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59, - 0x20,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x32,0x1e,0x98,0x14,0x19,0x11,0x4c,0x90, - 0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22,0x4a,0x60,0x04,0xa0,0x24,0x8a,0xa1,0x00, - 0x07,0xca,0xa0,0x1c,0x8a,0xa0,0x3c,0xa8,0x28,0x89,0x11,0x80,0x32,0x28,0x82,0x42, - 0x20,0x6e,0x06,0x80,0xba,0xb1,0x18,0x05,0x3a,0x41,0x3e,0x38,0x41,0x3e,0x38,0x41, - 0x3e,0x00,0xe0,0x01,0x1e,0xe0,0x01,0x00,0x11,0x10,0x02,0x00,0x79,0x18,0x00,0x00, - 0x66,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c, - 0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25, - 0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10, - 0x08,0x64,0x82,0x40,0x24,0x1b,0x84,0x81,0x98,0x20,0x10,0xca,0x06,0x61,0x30,0x28, - 0xd8,0xcd,0x4d,0x10,0x88,0x65,0xc3,0x80,0x24,0xc4,0x04,0x81,0xb3,0x08,0x4c,0x10, - 0x08,0x66,0x03,0x42,0x2c,0xcc,0x40,0x0c,0x0d,0xb0,0x21,0x70,0x36,0x10,0x00,0xf0, - 0x00,0x13,0x84,0xee,0xda,0x10,0x44,0x13,0x04,0x01,0x20,0x63,0x57,0x26,0x47,0x57, - 0x86,0x37,0x45,0x17,0x46,0x97,0x36,0x46,0x84,0xaa,0x08,0x6b,0xe8,0xe9,0x49,0x8a, - 0x68,0x82,0x50,0x44,0x13,0x84,0x42,0xda,0x10,0x10,0x13,0x84,0x62,0x9a,0x20,0x10, - 0xcd,0x04,0x81,0x70,0x36,0x08,0xda,0xb6,0x61,0x21,0x2a,0xeb,0xc2,0xae,0x21,0x23, - 0x2e,0x6e,0x43,0x30,0x6c,0x58,0x86,0xca,0xba,0xbc,0x6b,0xc8,0x86,0x8b,0x9b,0x20, - 0x10,0xcf,0x86,0x00,0x0c,0x26,0x08,0x05,0xb5,0x41,0xd0,0xb4,0x0d,0x0b,0x18,0x54, - 0xd6,0x15,0x06,0xd7,0x20,0x06,0x60,0x70,0x8d,0xc1,0x86,0xa1,0xfb,0xc8,0x60,0xc3, - 0x42,0x54,0xd6,0x85,0x89,0xc1,0x90,0x11,0x17,0xb7,0x61,0x19,0x2a,0xeb,0xf2,0xc4, - 0x60,0xc8,0x86,0x8b,0xdb,0xb0,0x80,0x41,0x65,0x5d,0x61,0x20,0x06,0x83,0x18,0x80, - 0xc1,0x35,0x06,0x5c,0xa6,0xac,0xbe,0xa0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x26, - 0x08,0x45,0x35,0x41,0x20,0xa0,0x0d,0x82,0xb6,0x06,0x1b,0x16,0x2d,0x0d,0xac,0x0c, - 0x53,0x83,0x41,0x0d,0xb4,0x8b,0x0d,0x36,0x10,0x66,0x70,0x06,0x68,0xd0,0x06,0x1b, - 0x86,0x32,0x70,0x03,0x60,0x43,0x31,0x51,0x6f,0x00,0x01,0x55,0xd8,0xd8,0xec,0xda, - 0x5c,0xd2,0xc8,0xca,0xdc,0xe8,0xa6,0x04,0x41,0x15,0x32,0x3c,0x17,0xbb,0x32,0xb9, - 0xb9,0xb4,0x37,0xb7,0x29,0x01,0xd1,0x84,0x0c,0xcf,0xc5,0x2e,0x8c,0xcd,0xae,0x4c, - 0x6e,0x4a,0x60,0xd4,0x21,0xc3,0x73,0x99,0x43,0x0b,0x23,0x2b,0x93,0x6b,0x7a,0x23, - 0x2b,0x63,0x9b,0x12,0x24,0x65,0xc8,0xf0,0x5c,0xe4,0xca,0xe6,0xde,0xea,0xe4,0xc6, - 0xca,0xe6,0xa6,0x04,0x4f,0x1d,0x32,0x3c,0x17,0xbb,0xb4,0xb2,0xbb,0x24,0xb2,0x29, - 0xba,0x30,0xba,0xb2,0x29,0x41,0x54,0x87,0x0c,0xcf,0xa5,0xcc,0x8d,0x4e,0x2e,0x0f, - 0xea,0x2d,0xcd,0x8d,0x6e,0x6e,0x4a,0xf0,0x06,0x00,0x00,0x00,0x79,0x18,0x00,0x00, - 0x4c,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, - 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, - 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, - 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, - 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, - 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, - 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, - 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, - 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, - 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, - 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, - 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, - 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, - 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, - 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, - 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, - 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70, - 0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87, - 0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e, - 0xf0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x56,0xb0,0x0d,0x97, - 0xef,0x3c,0xbe,0x10,0x50,0x45,0x41,0x44,0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6, - 0x17,0xb7,0x6d,0x07,0xd2,0x70,0xf9,0xce,0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34, - 0xc3,0x42,0xd8,0x80,0x33,0x5c,0xbe,0xf3,0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16, - 0x30,0x0d,0x97,0xef,0x3c,0xfe,0xe2,0x00,0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d, - 0x02,0xd5,0x70,0xf9,0xce,0xe3,0x4b,0x93,0x13,0x11,0x28,0x35,0x3d,0xd4,0xe4,0x17, - 0xb7,0x6d,0x06,0xd2,0x70,0xf9,0xce,0xe3,0x4f,0x44,0x34,0x21,0x40,0x84,0xf9,0xc5, - 0x6d,0x1b,0xc1,0x33,0x5c,0xbe,0xf3,0xf8,0x54,0x03,0x44,0x98,0x5f,0xdc,0xb6,0x01, - 0x30,0x15,0x11,0x4d,0xc4,0xc5,0x4e,0xc0,0x84,0x08,0x00,0x00,0x61,0x20,0x00,0x00, - 0x60,0x01,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x44,0x14,0x57,0x29,0x94,0x5d,0x21,0xcc,0x00,0x8c,0x32,0x00,0x09,0x32,0xca,0x00, - 0x74,0xc8,0x28,0x03,0x90,0x6c,0xa3,0x0c,0xc0,0x87,0x8c,0x32,0x00,0x60,0x80,0x8c, - 0x32,0x00,0xc9,0x19,0x8c,0x32,0x00,0x62,0x80,0x94,0xdc,0x28,0x03,0xe0,0x21,0xa3, - 0x0c,0x40,0x18,0x20,0x05,0x31,0xca,0x00,0x24,0xc9,0x28,0x03,0x30,0x06,0xc8,0x28, - 0x03,0x90,0x3c,0xe5,0x56,0x32,0x54,0x94,0x00,0x55,0x23,0x00,0x00,0x00,0x00,0x00, - 0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xa1,0xc1,0xb2,0x99,0x81,0x19,0x04,0x33,0x11, - 0x1e,0x81,0x8c,0xc2,0x4c,0x84,0x47,0x20,0xa3,0x30,0x13,0xe1,0x11,0xc8,0x28,0x8c, - 0x18,0x24,0x00,0x08,0x82,0x81,0x21,0x07,0x18,0x19,0xa8,0x41,0x18,0x5c,0x23,0x06, - 0x09,0x00,0x82,0x60,0x60,0xcc,0x41,0x56,0x06,0x6b,0x70,0x60,0x23,0x06,0x09,0x00, - 0x82,0x60,0x60,0xd0,0x81,0xd6,0x06,0x6c,0x30,0x06,0xd9,0x88,0x41,0x02,0x80,0x20, - 0x18,0x18,0x75,0xb0,0xb9,0x41,0x1b,0x24,0xda,0x88,0x41,0x02,0x80,0x20,0x18,0x18, - 0x76,0xc0,0xbd,0x81,0x1b,0x8c,0xc1,0x36,0x62,0x90,0x00,0x20,0x08,0x06,0xc6,0x1d, - 0x74,0x6f,0xf0,0x06,0x66,0xc0,0x8d,0x18,0x24,0x00,0x08,0x82,0x81,0x81,0x07,0x1e, - 0x1c,0xc0,0x01,0xd3,0x8d,0x18,0x24,0x00,0x08,0x82,0x81,0x91,0x07,0x5f,0x1c,0xc4, - 0x81,0x19,0x78,0x23,0x06,0x07,0x00,0x82,0x60,0x10,0xd9,0x41,0x18,0x30,0x72,0x30, - 0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0x30,0x62,0x70, - 0x00,0x20,0x08,0x06,0xd1,0x1e,0x98,0x41,0x34,0x06,0xa3,0x09,0x01,0x30,0x9a,0x20, - 0x04,0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x23,0x06,0x07,0x00,0x82,0x60,0x10,0x81, - 0xc2,0x1a,0x58,0x67,0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3, - 0x09,0xc4,0x30,0x62,0x70,0x00,0x20,0x08,0x06,0x51,0x29,0xc0,0xc1,0x56,0x06,0xa3, - 0x09,0x01,0x30,0x9a,0x20,0x04,0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x36,0x5d,0xf2, - 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x49,0x15,0xf0,0xe0,0xb9,0x82,0x11,0x03,0x04, - 0x00,0x41,0x30,0x98,0x56,0x21,0x0f,0x96,0x2b,0xb0,0xe0,0x80,0x8e,0x59,0x9b,0x7c, - 0x46,0x0c,0x10,0x00,0x04,0xc1,0x60,0x72,0x05,0x3e,0x90,0xb6,0x60,0xc4,0x00,0x01, - 0x40,0x10,0x0c,0xa6,0x57,0xe8,0x03,0x67,0x0b,0x2c,0x50,0xa0,0x63,0xd9,0x27,0x9f, - 0x11,0x03,0x04,0x00,0x41,0x30,0x98,0x64,0x01,0x14,0xaa,0x2f,0x18,0x31,0x40,0x00, - 0x10,0x04,0x83,0x69,0x16,0x42,0x21,0xfa,0x02,0x0b,0x1a,0xe8,0x18,0x37,0x06,0xf2, - 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0xc9,0x16,0x48,0x01,0x1b,0x83,0x60,0xc4,0x00, - 0x01,0x40,0x10,0x0c,0xa6,0x5b,0x28,0x05,0x6a,0x0c,0x02,0x0b,0x20,0xe8,0x8c,0x18, - 0x1c,0x00,0x08,0x82,0x41,0xa4,0x0b,0xa5,0x00,0x07,0xa0,0x30,0x9a,0x10,0x00,0xa3, - 0x09,0x42,0x30,0x9a,0x30,0x08,0x23,0x06,0x07,0x00,0x82,0x60,0x10,0xf9,0x42,0x2a, - 0xd0,0xc1,0x28,0x8c,0x26,0x04,0xc0,0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x88,0xc1, - 0x01,0x80,0x20,0x18,0x44,0xe2,0xd0,0x0a,0x78,0x60,0x0a,0xa3,0x09,0x01,0x30,0x9a, - 0x20,0x04,0xa3,0x09,0x83,0x30,0x62,0x70,0x00,0x20,0x08,0x06,0x91,0x39,0xc4,0x02, - 0x1f,0x94,0xc2,0x68,0x42,0x00,0x8c,0x26,0x08,0xc1,0x68,0xc2,0x20,0xd8,0x73,0x07, - 0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x49,0x1d,0x70,0x81,0xb9,0x83,0x60,0xc4, - 0x00,0x01,0x40,0x10,0x0c,0xa6,0x75,0xc8,0x85,0xe4,0x0e,0x02,0x0b,0x0c,0xe8,0x98, - 0xb4,0x07,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0xc9,0x1d,0x78,0xe1,0xd9,0x83, - 0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xa6,0x77,0xe8,0x05,0x66,0x0f,0x02,0x0b,0x12, - 0xe8,0x58,0xf5,0x07,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x49,0x1e,0xc0,0x41, - 0xfa,0x83,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xa6,0x79,0x08,0x87,0xe7,0x0f,0x02, - 0x0b,0x18,0xe8,0x8c,0x18,0x1c,0x00,0x08,0x82,0x41,0x64,0x0f,0xe1,0xc0,0x0a,0xb9, - 0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0xb0,0xbd, - 0x01,0x16,0xee,0xe1,0x1e,0x86,0x0d,0x88,0xa0,0x18,0x80,0xed,0x0d,0xb1,0x80,0x0f, - 0xf9,0x30,0x6c,0x40,0x04,0xc5,0x00,0x6c,0x6f,0x90,0x85,0x7c,0x98,0x87,0x61,0x03, - 0x22,0x28,0x06,0x60,0x7b,0xc3,0x2c,0xe8,0x83,0x3d,0x0c,0x1b,0x10,0x41,0x31,0x00, - 0xdb,0x1b,0x68,0x61,0x1f,0x78,0x81,0x82,0x61,0x8c,0x18,0x1c,0x00,0x08,0x82,0x41, - 0x14,0x12,0xec,0x70,0x0b,0xe3,0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30, - 0x08,0xa3,0x09,0xc4,0x30,0x6c,0x40,0x2c,0xc4,0x00,0x0c,0x1b,0x10,0xca,0x30,0x00, - 0xc3,0x06,0x44,0x22,0x0c,0xc0,0xb0,0x01,0x81,0x04,0x03,0x40,0xc7,0x30,0x46,0x0c, - 0x0e,0x00,0x04,0xc1,0x20,0x3a,0x09,0x79,0xe8,0x85,0x71,0x18,0x4d,0x08,0x80,0xd1, - 0x04,0x21,0x18,0x4d,0x18,0x84,0xd1,0x04,0x62,0x18,0x36,0x20,0x22,0x62,0x00,0x86, - 0x0d,0x08,0x68,0x18,0x80,0x61,0x03,0xe2,0x11,0x06,0x60,0xd8,0x80,0x70,0x82,0x01, - 0xa0,0x66,0x18,0xdb,0x1b,0xc2,0x21,0x25,0x52,0x62,0xd8,0x80,0x08,0xae,0x01,0xd8, - 0xde,0x20,0x0e,0x2a,0xb1,0x12,0xc3,0x06,0x44,0x70,0x0d,0xc0,0xf6,0x86,0x71,0x58, - 0x89,0x92,0x18,0x36,0x20,0x82,0x6b,0x00,0xb6,0x37,0x90,0x03,0x4b,0xa0,0xc4,0xb0, - 0x01,0x11,0x5c,0x03,0xb0,0xbd,0xa1,0x1c,0x5a,0x22,0x1f,0x28,0x18,0xc6,0xb0,0x01, - 0x61,0x44,0x03,0x30,0x6c,0x40,0x14,0xd0,0x00,0x0c,0x1b,0x10,0xc4,0x33,0x00,0xc3, - 0x06,0xc4,0xe0,0x0c,0x00,0x09,0xc3,0x18,0x36,0x20,0x0e,0x66,0x00,0x86,0x0d,0x08, - 0x63,0x19,0x80,0x61,0x03,0xa2,0x50,0x06,0x60,0xd8,0x80,0x20,0x92,0x01,0xa0,0x61, - 0x18,0xdb,0x1b,0xd0,0x21,0x26,0x62,0x62,0xd8,0x80,0x08,0xbe,0x01,0xd8,0xde,0x90, - 0x0e,0x32,0x31,0x13,0xc3,0x06,0x44,0xf0,0x0d,0xc0,0xf6,0x06,0x75,0x98,0x89,0x96, - 0x18,0x36,0x20,0x82,0x6f,0x00,0xb6,0x37,0xac,0x03,0x4d,0xc0,0xc4,0xb0,0x01,0x11, - 0x7c,0x03,0xb0,0xbd,0x81,0x1d,0x6a,0x82,0x1e,0x28,0x18,0xc6,0xb0,0x01,0x61,0x64, - 0x03,0x30,0x6c,0x40,0x14,0xd8,0x00,0x0c,0x1b,0x10,0xc4,0x35,0x00,0xc3,0x06,0xc4, - 0x60,0x0d,0x00,0x09,0xc3,0x18,0x36,0x20,0x0e,0x6a,0x00,0x86,0x0d,0x08,0x63,0x1a, - 0x80,0x61,0x03,0xa2,0x90,0x06,0x60,0xd8,0x80,0x20,0xa2,0x01,0xa0,0x61,0x18,0xd6, - 0xb5,0x83,0x7c,0x46,0x0c,0x10,0x00,0x04,0xc1,0x60,0xea,0x89,0x95,0xc0,0xda,0x21, - 0x18,0x31,0x40,0x00,0x10,0x04,0x83,0xc9,0x27,0x58,0x62,0x6a,0x87,0xc0,0x1c,0x78, - 0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x4c,0x60,0xe1,0x12,0x0e,0x3c,0x04,0x23, - 0x06,0x08,0x00,0x82,0x60,0x30,0x85,0xc5,0x4b,0x38,0xf0,0x10,0x58,0x32,0x0f,0xf2, - 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x69,0x2c,0x62,0x22,0x99,0x87,0x60,0xc4,0x00, - 0x01,0x40,0x10,0x0c,0x26,0xb2,0x90,0x89,0x64,0x1e,0x82,0x11,0x83,0x05,0x00,0x41, - 0x30,0x50,0xd0,0x02,0x24,0x0e,0x22,0x38,0x88,0x60,0xc4,0xc0,0x00,0x40,0x10,0x0c, - 0x18,0xb4,0x00,0x89,0xc0,0x82,0x44,0x3e,0x26,0x1c,0xf2,0xb1,0xa1,0x90,0xcf,0x88, - 0x41,0x02,0x80,0x20,0x18,0x20,0x6e,0x71,0x13,0x67,0x71,0x16,0x3e,0xe1,0x07,0x23, - 0x06,0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0x9c,0xc5,0x59,0x8c,0x84,0x1e,0x8c, - 0x18,0x24,0x00,0x08,0x82,0x01,0xe2,0x16,0x37,0x71,0x16,0x67,0xc1,0x13,0x76,0x30, - 0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x5b,0xdc,0x04,0x5a,0x9c,0x85,0x4f,0x0c,0x23, - 0x06,0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0xa0,0xc5,0x59,0x8c,0x84,0x30,0x62, - 0x90,0x00,0x20,0x08,0x06,0x88,0x5b,0xdc,0x04,0x5a,0x9c,0x05,0x4f,0x04,0x23,0x06, - 0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0x84,0xc5,0x59,0xf8,0x44,0x3f,0x8c,0x18, - 0x24,0x00,0x08,0x82,0x01,0xe2,0x16,0x37,0x11,0x16,0x67,0x31,0x12,0xfc,0x30,0x62, - 0x90,0x00,0x20,0x08,0x06,0x88,0x5b,0xdc,0xc4,0x58,0x9c,0x85,0x4f,0xf8,0xc2,0x88, - 0x41,0x02,0x80,0x20,0x18,0x20,0x6e,0x71,0x13,0x63,0x71,0x16,0x23,0xa1,0x0b,0x23, - 0x06,0x09,0x00,0x82,0x60,0x80,0xb8,0xc5,0x4d,0x8c,0xc5,0x59,0xf0,0x84,0x2d,0x8c, - 0x18,0x24,0x00,0x08,0x82,0x01,0xe2,0x16,0x37,0x31,0x16,0x67,0xd1,0x13,0xb2,0x80, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; - -static const unsigned char _vertexStaticMSL[] = { - 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, - 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, - 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, - 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, - 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x78,0x34,0x20,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, - 0x69,0x78,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x31,0x32,0x38,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, - 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x4c,0x69,0x67,0x68,0x74,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, - 0x67,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, - 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x20,0x63,0x6f,0x6e,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, - 0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x34,0x20,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, - 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x65,0x6d,0x69,0x73,0x73,0x69, - 0x76,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6d, - 0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x63,0x6f,0x75,0x6e,0x74,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72, - 0x61,0x6d,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x38, - 0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x38,0x5d,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x4c,0x69,0x67,0x68,0x74,0x20,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x38,0x5d, - 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72, - 0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f,0x76, - 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b,0x75, - 0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72, - 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x75,0x73,0x65, - 0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54, - 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28, - 0x6c,0x6f,0x63,0x6e,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, - 0x20,0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d, - 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72,0x74,0x65,0x78, - 0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62, - 0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, - 0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x32,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x32,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28, - 0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,0x78, - 0x20,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x6f,0x75, - 0x74,0x20,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x28,0x76, - 0x65,0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x69,0x6e,0x20,0x69, - 0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20, - 0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x44,0x72, - 0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x26,0x20,0x44,0x72,0x61,0x77, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65, - 0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x76,0x65, - 0x72,0x74,0x65,0x78,0x53,0x74,0x61,0x74,0x69,0x63,0x5f,0x6f,0x75,0x74,0x20,0x6f, - 0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x5f,0x36,0x35,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20, - 0x3d,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d, - 0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69, - 0x6f,0x6e,0x20,0x2a,0x20,0x5f,0x36,0x35,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75, - 0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x30,0x20,0x3d,0x20,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x6d,0x6f,0x64,0x65,0x6c,0x20,0x2a,0x20,0x5f,0x36,0x35,0x29, - 0x2e,0x78,0x79,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x6f,0x75, - 0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20, - 0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x69,0x7a, - 0x65,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x31,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33, - 0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69, - 0x78,0x5b,0x30,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, - 0x69,0x78,0x5b,0x31,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74, - 0x72,0x69,0x78,0x5b,0x32,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61, - 0x74,0x72,0x69,0x78,0x5b,0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69, - 0x78,0x5b,0x30,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, - 0x69,0x78,0x5b,0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74, - 0x72,0x69,0x78,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61, - 0x74,0x72,0x69,0x78,0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69, - 0x78,0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, - 0x69,0x78,0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74, - 0x72,0x69,0x78,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61, - 0x74,0x72,0x69,0x78,0x5b,0x33,0x5d,0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29, - 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76, - 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x3d,0x20,0x69, - 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f, - 0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a, -}; - -static const SceneShaderT sceneShaderVertexStatic = { "vertexStatic", _vertexStaticSPIRV, sizeof(_vertexStaticSPIRV), _vertexStaticDXIL, sizeof(_vertexStaticDXIL), _vertexStaticMSL, sizeof(_vertexStaticMSL) }; - -static const unsigned char _vertexSkinnedSPIRV[] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0xa4,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x01,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74, - 0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x08,0x00,0x0c,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f, - 0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65,0x6c,0x00,0x00,0x00,0x06,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61, - 0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x0d,0x00,0x00,0x00, - 0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x0e,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x53,0x6b,0x69, - 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x05,0x00, - 0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x00,0x00, - 0x05,0x00,0x06,0x00,0x0f,0x00,0x00,0x00,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x08,0x00,0x10,0x00,0x00,0x00, - 0x74,0x79,0x70,0x65,0x2e,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x10,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69, - 0x6f,0x6e,0x00,0x00,0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x65,0x6d,0x69,0x73, - 0x73,0x69,0x76,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x00,0x00,0x00,0x00, - 0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x63,0x6f,0x75,0x6e, - 0x74,0x73,0x00,0x00,0x06,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00,0x00, - 0x06,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x4d,0x61,0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00, - 0x10,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e, - 0x66,0x6f,0x00,0x00,0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x6c,0x69,0x67,0x68,0x74,0x73,0x00,0x00,0x05,0x00,0x04,0x00,0x11,0x00,0x00,0x00, - 0x4c,0x69,0x67,0x68,0x74,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x11,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65, - 0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e,0x67,0x65,0x00,0x00, - 0x06,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f, - 0x72,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x63,0x6f,0x6e,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x12,0x00,0x00,0x00, - 0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x13,0x00,0x00,0x00,0x74,0x79,0x70,0x65, - 0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x14,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00, - 0x05,0x00,0x06,0x00,0x15,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x16,0x00,0x00,0x00, - 0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x05,0x00,0x07,0x00, - 0x17,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67, - 0x65,0x2e,0x61,0x72,0x72,0x61,0x79,0x00,0x05,0x00,0x05,0x00,0x18,0x00,0x00,0x00, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x00,0x00,0x05,0x00,0x06,0x00, - 0x19,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x03,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76, - 0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x04,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54, - 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, - 0x05,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x06,0x00,0x00,0x00, - 0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, - 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76, - 0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34,0x00,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x09,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x05,0x00,0x07,0x00, - 0x0a,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x31,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x0b,0x00,0x00,0x00, - 0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x32,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x02,0x00,0x00,0x00,0x76,0x65,0x72,0x74, - 0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x03,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x04,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x09,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x18,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x19,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x19,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, - 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x80,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1c,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x11,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x60,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x70,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x10,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x48,0x00,0x05,0x00, - 0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0xf0,0x02,0x00,0x00, - 0x47,0x00,0x03,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x1e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x1e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x00,0x03,0x00, - 0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x20,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x2b,0x00,0x04,0x00,0x1e,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x1e,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x1e,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x25,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x26,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x27,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x1c,0x00,0x04,0x00, - 0x1a,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x1e,0x00,0x03,0x00, - 0x0e,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2a,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x28,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x1b,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x1c,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x11,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x1c,0x00,0x04,0x00,0x1d,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, - 0x1e,0x00,0x0c,0x00,0x10,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x19,0x00,0x09,0x00,0x13,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x1a,0x00,0x02,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x2e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x19,0x00,0x09,0x00, - 0x17,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x2f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x34,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x34,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x36,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x37,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x32,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x3a,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x3b,0x00,0x00,0x00, - 0x3a,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x3d,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x27,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2a,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2e,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2f,0x00,0x00,0x00,0x18,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x2e,0x00,0x00,0x00,0x19,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x36,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x37,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x3e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x3f,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x28,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x3a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x40,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x41,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00,0x45,0x00,0x00,0x00, - 0x41,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x20,0x00,0x00,0x00, - 0x46,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00, - 0x25,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x45,0x00,0x00,0x00, - 0x46,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3f,0x00,0x00,0x00, - 0x48,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x41,0x00,0x06,0x00, - 0x3c,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x49,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x4b,0x00,0x00,0x00, - 0x4a,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x4c,0x00,0x00,0x00, - 0x47,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3e,0x00,0x00,0x00, - 0x4d,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x20,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x4d,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, - 0x25,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x4e,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x3f,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x51,0x00,0x00,0x00, - 0x50,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x3c,0x00,0x00,0x00,0x52,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x26,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x90,0x00,0x05,0x00, - 0x25,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x53,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x3e,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x56,0x00,0x00,0x00, - 0x55,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x57,0x00,0x00,0x00, - 0x54,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x25,0x00,0x00,0x00, - 0x58,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x3f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x28,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0x59,0x00,0x00,0x00, - 0x41,0x00,0x06,0x00,0x3c,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x26,0x00,0x00,0x00, - 0x5c,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x25,0x00,0x00,0x00, - 0x5d,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x3e,0x00,0x00,0x00,0x5e,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x5e,0x00,0x00,0x00, - 0x8e,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x5d,0x00,0x00,0x00, - 0x5f,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x61,0x00,0x00,0x00, - 0x58,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3f,0x00,0x00,0x00, - 0x62,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x41,0x00,0x06,0x00, - 0x3c,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x63,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x65,0x00,0x00,0x00, - 0x64,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x66,0x00,0x00,0x00, - 0x47,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3e,0x00,0x00,0x00, - 0x67,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x20,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, - 0x25,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x68,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x6a,0x00,0x00,0x00,0x61,0x00,0x00,0x00, - 0x69,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x6b,0x00,0x00,0x00, - 0x4b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00, - 0x6c,0x00,0x00,0x00,0x6b,0x00,0x00,0x00,0x6b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00, - 0x6d,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, - 0x30,0x00,0x00,0x00,0x6e,0x00,0x00,0x00,0x6d,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x25,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x4b,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x6f,0x00,0x00,0x00, - 0x6f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x50,0x00,0x06,0x00,0x3d,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x6c,0x00,0x00,0x00, - 0x6e,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x30,0x00,0x00,0x00, - 0x72,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, - 0x30,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x4e,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x53,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x75,0x00,0x00,0x00, - 0x74,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x76,0x00,0x00,0x00, - 0x53,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00, - 0x77,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00, - 0x78,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, - 0x30,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x78,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x3d,0x00,0x00,0x00,0x7a,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x77,0x00,0x00,0x00, - 0x79,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x7b,0x00,0x00,0x00, - 0x42,0x00,0x00,0x00,0x7a,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x30,0x00,0x00,0x00, - 0x7c,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x81,0x00,0x05,0x00, - 0x30,0x00,0x00,0x00,0x7d,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x7c,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x5c,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x7f,0x00,0x00,0x00, - 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x80,0x00,0x00,0x00, - 0x5c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00, - 0x81,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00, - 0x82,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, - 0x30,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x82,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x3d,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x81,0x00,0x00,0x00, - 0x83,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x85,0x00,0x00,0x00, - 0x42,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x30,0x00,0x00,0x00, - 0x86,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x5f,0x00,0x00,0x00,0x81,0x00,0x05,0x00, - 0x30,0x00,0x00,0x00,0x87,0x00,0x00,0x00,0x7d,0x00,0x00,0x00,0x86,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x65,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x89,0x00,0x00,0x00, - 0x88,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x8a,0x00,0x00,0x00, - 0x65,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00, - 0x8b,0x00,0x00,0x00,0x8a,0x00,0x00,0x00,0x8a,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00, - 0x8c,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, - 0x30,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0x8c,0x00,0x00,0x00,0x8c,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x3d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x8b,0x00,0x00,0x00, - 0x8d,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x8f,0x00,0x00,0x00, - 0x42,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x30,0x00,0x00,0x00, - 0x90,0x00,0x00,0x00,0x8f,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x81,0x00,0x05,0x00, - 0x30,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x87,0x00,0x00,0x00,0x90,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x3c,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x93,0x00,0x00,0x00, - 0x92,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x94,0x00,0x00,0x00, - 0x6a,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3c,0x00,0x00,0x00, - 0x95,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x26,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x95,0x00,0x00,0x00,0x90,0x00,0x05,0x00, - 0x25,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x6a,0x00,0x00,0x00,0x96,0x00,0x00,0x00, - 0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x97,0x00,0x00,0x00, - 0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x3c,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x9a,0x00,0x00,0x00, - 0x99,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x9b,0x00,0x00,0x00, - 0x9a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00, - 0x9c,0x00,0x00,0x00,0x9b,0x00,0x00,0x00,0x9b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x25,0x00,0x00,0x00, - 0x9d,0x00,0x00,0x00,0x9a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x4f,0x00,0x08,0x00, - 0x30,0x00,0x00,0x00,0x9e,0x00,0x00,0x00,0x9d,0x00,0x00,0x00,0x9d,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x25,0x00,0x00,0x00,0x9f,0x00,0x00,0x00,0x9a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x4f,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x9f,0x00,0x00,0x00, - 0x9f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x50,0x00,0x06,0x00,0x3d,0x00,0x00,0x00,0xa1,0x00,0x00,0x00,0x9c,0x00,0x00,0x00, - 0x9e,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x90,0x00,0x05,0x00,0x30,0x00,0x00,0x00, - 0xa2,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0xa1,0x00,0x00,0x00,0x0c,0x00,0x06,0x00, - 0x30,0x00,0x00,0x00,0xa3,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00, - 0xa2,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x94,0x00,0x00,0x00, - 0x3e,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, - 0x0a,0x00,0x00,0x00,0xa3,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x0b,0x00,0x00,0x00, - 0x43,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, -}; - -static const unsigned char _vertexSkinnedDXIL[] = { - 0x44,0x58,0x42,0x43,0x27,0xb4,0xc6,0xd9,0x69,0x41,0xbc,0x07,0x91,0x11,0x7e,0x85, - 0x11,0x6e,0x35,0x08,0x01,0x00,0x00,0x00,0x70,0x25,0x00,0x00,0x07,0x00,0x00,0x00, - 0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0xb0,0x01,0x00,0x00, - 0x80,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x1c,0x0c,0x00,0x00,0x53,0x46,0x49,0x30, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, - 0xb4,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xa8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xa8,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xa8,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xa8,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xa8,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x00,0x00,0x00,0x00,0x4f,0x53,0x47,0x31,0xa0,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x03,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x91,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, - 0x50,0x53,0x56,0x30,0xc8,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xff,0xff,0xff,0xff,0x01,0x00,0x00,0x00,0x05,0x04,0x00,0x05,0x04,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x54,0x45,0x58, - 0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54, - 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x00,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e, - 0x65,0x64,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x00,0x00,0x00, - 0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x03,0x44,0x00,0x01,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x04,0x44,0x00,0x03,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x43,0x00,0x03,0x02,0x00,0x00, - 0x37,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x01,0x43,0x00,0x03,0x02,0x00,0x00, - 0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x02,0x42,0x00,0x03,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x44,0x03,0x03,0x04,0x00,0x00, - 0x07,0xf0,0x00,0x00,0x07,0xf0,0x00,0x00,0x07,0xf0,0x00,0x00,0x00,0x00,0x00,0x00, - 0x70,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00, - 0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00,0x77,0xf0,0x00,0x00, - 0x53,0x54,0x41,0x54,0x78,0x08,0x00,0x00,0x60,0x00,0x01,0x00,0x1e,0x02,0x00,0x00, - 0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x60,0x08,0x00,0x00, - 0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x15,0x02,0x00,0x00,0x0b,0x82,0x20,0x00, - 0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49, - 0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62, - 0x80,0x18,0x45,0x02,0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b, - 0x0a,0x32,0x62,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42, - 0xe4,0x48,0x0e,0x90,0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a, - 0x04,0x31,0x46,0x06,0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff, - 0xff,0xff,0xff,0x07,0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20, - 0x6d,0x30,0x86,0xff,0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00, - 0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00, - 0x89,0x20,0x00,0x00,0x34,0x00,0x00,0x00,0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04, - 0x13,0x23,0xa4,0x84,0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c, - 0x0b,0x84,0xc4,0x4c,0x10,0x8c,0xc1,0x08,0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08, - 0xc0,0x60,0x8e,0x00,0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51,0x0c,0x82,0x20,0x88, - 0x82,0x20,0xa4,0x18,0x00,0x41,0x10,0xc5,0x40,0x4a,0x41,0x06,0x62,0x18,0x86,0x61, - 0x18,0x88,0x29,0xc3,0x40,0x0c,0xe4,0x1c,0x35,0x5c,0xfe,0x84,0x3d,0x84,0xe4,0x73, - 0x1b,0x55,0xac,0xc4,0xe4,0x17,0xb7,0x8d,0x88,0x61,0x18,0x06,0x2a,0xee,0x19,0x2e, - 0x7f,0xc2,0x1e,0x42,0xf2,0x43,0xa0,0x19,0x16,0x02,0x05,0x51,0x21,0x20,0x42,0x22, - 0x68,0x2a,0xc5,0x40,0x0c,0xc3,0x40,0xd5,0x1c,0x41,0x50,0x0c,0x89,0x28,0x08,0xe2, - 0x22,0x6c,0x20,0x60,0x18,0x81,0x18,0x92,0x60,0x3b,0xee,0x70,0xa4,0x69,0x01,0x30, - 0x87,0x9a,0xfc,0x09,0x7b,0x88,0xbf,0x8b,0x00,0xcb,0x6d,0x90,0xc2,0x89,0x18,0x09, - 0x0d,0x1c,0xc7,0x51,0x97,0x80,0x01,0xdc,0x71,0x87,0x23,0x4d,0x0b,0x80,0x39,0xd4, - 0xe4,0x4f,0xd8,0x43,0xfc,0xac,0x82,0x34,0x6e,0x83,0x14,0x4e,0xc4,0x48,0x28,0xf8, - 0x08,0x4c,0x0b,0x02,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87, - 0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e, - 0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, - 0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20, - 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07, - 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, - 0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20, - 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, - 0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76, - 0x40,0x07,0x43,0x9e,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86, - 0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20, - 0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28,0x40,0x00,0x08,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x60,0xc8,0xe3,0x00,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0x90,0x67,0x02,0x02,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x4f, - 0x05,0x04,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x1e,0x0c,0x08,0x80, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb2,0x40,0x00,0x00,0x21,0x00,0x00,0x00, - 0x32,0x1e,0x98,0x18,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22, - 0x4a,0x60,0x04,0xa0,0x18,0x0a,0x70,0xa0,0x00,0x01,0x03,0x8a,0xa0,0x10,0xca,0xa0, - 0x1c,0x4a,0xa2,0x00,0x03,0x0a,0x50,0xa0,0x3c,0x0a,0x7c,0xa0,0x84,0x07,0x0a,0x3b, - 0xa0,0xb0,0x15,0x0a,0xa4,0x30,0xa8,0x28,0x89,0x11,0x80,0x32,0x28,0x82,0x52,0x28, - 0x04,0xba,0x6a,0x80,0xba,0x19,0x00,0xf2,0x66,0x00,0x08,0x9c,0x01,0xa0,0x70,0x06, - 0x80,0xc4,0xb1,0x2c,0x0a,0x3a,0x41,0x3e,0x38,0x41,0x3e,0x38,0x41,0x3e,0x00,0xe0, - 0x01,0x1e,0xe0,0x01,0x00,0x11,0x10,0x02,0x80,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b, - 0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4,0x83,0x7b,0xe4, - 0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0xe0,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90, - 0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c, - 0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c, - 0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x88,0x64,0x82,0x40,0x28,0x1b,0x84,0x81, - 0xd8,0x20,0x10,0x04,0x05,0xbb,0xb9,0x09,0x02,0xb1,0x6c,0x18,0x0e,0x84,0x98,0x20, - 0x78,0x64,0xc0,0x88,0x2e,0x0f,0xae,0xec,0x8b,0x48,0x2e,0xec,0xae,0xca,0x2d,0xcd, - 0xec,0x4d,0xae,0x6d,0x6e,0x82,0x40,0x30,0x1b,0x10,0x42,0x59,0x06,0x62,0x60,0x80, - 0x09,0x42,0x18,0x98,0x01,0x23,0xba,0x3c,0xb8,0xb2,0xaf,0xa9,0xb5,0x34,0xb7,0x2a, - 0xb7,0x34,0xb3,0x37,0xb9,0xb6,0xb9,0x09,0x02,0xd1,0x6c,0x40,0x06,0xe7,0x19,0x86, - 0x01,0x02,0x36,0x08,0x4d,0xb4,0x81,0x00,0x00,0x09,0x98,0x20,0x74,0x63,0x40,0x30, - 0x20,0x92,0x0b,0xbb,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b,0x9b,0xfb,0x6a,0x7b,0x23, - 0x2b,0x63,0xb3,0x4a,0x2b,0xbb,0x83,0x92,0x7b,0x53,0x2b,0x1b,0xa3,0x4b,0x7b,0x73, - 0x9b,0x20,0x10,0xce,0x04,0x81,0x78,0x36,0x0c,0xd7,0x65,0x4d,0x10,0x08,0x68,0x82, - 0x40,0x44,0x13,0x04,0x42,0x9a,0x20,0x5c,0x62,0xb0,0x41,0x41,0x2a,0x0b,0xcb,0x08, - 0x6d,0xdb,0x38,0x4a,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x5f,0x6d,0x6f,0x64,0x65,0x6c,0x13,0x04,0x62,0xda,0x80,0x20,0x9e,0x85,0x65,0x9f, - 0xb6,0x31,0x23,0x92,0x0b,0xbb,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b,0x9b,0xfb,0x72, - 0x7b,0x93,0x6b,0x0b,0x63,0x6b,0x0a,0xa3,0x93,0x4b,0xc3,0x9b,0x20,0x10,0xd4,0x06, - 0x04,0x09,0x03,0x0b,0xcb,0xc4,0x40,0xdb,0x36,0x10,0x4c,0x07,0x06,0x63,0x30,0x41, - 0x00,0x83,0x32,0xe0,0x34,0xb5,0x96,0xe6,0x56,0xe5,0x96,0x66,0xf6,0x26,0xd7,0x36, - 0xf7,0xa5,0xf6,0x96,0xe6,0x46,0x37,0xb7,0x01,0x41,0xcc,0xc0,0xc2,0x32,0x42,0xdb, - 0x36,0x08,0xd0,0x19,0x6c,0x28,0x08,0x8a,0x0c,0xca,0x00,0x0d,0x26,0x08,0x02,0xb0, - 0x01,0xd8,0x30,0x10,0x6b,0xb0,0x06,0x1b,0x02,0x36,0xd8,0x30,0x0c,0x6a,0xd0,0x06, - 0x13,0x04,0x31,0x38,0x83,0x0d,0xc1,0x1b,0xb0,0xb1,0x2b,0x93,0xa3,0x2b,0xc3,0x9b, - 0x5a,0x4b,0x73,0x73,0x2b,0x23,0x23,0x42,0x55,0x84,0x35,0xf4,0xf4,0x24,0x45,0x34, - 0x41,0x28,0xb8,0x09,0x42,0xd1,0x6d,0x08,0x88,0x09,0x42,0xe1,0x6d,0x10,0x32,0x6d, - 0xc3,0x42,0xc8,0xc1,0x1c,0xd0,0x41,0x1d,0xd0,0xc1,0x60,0x07,0x04,0x1d,0xdc,0xc1, - 0x86,0x60,0xd8,0xb0,0x0c,0x72,0x30,0x07,0x74,0x90,0x07,0x74,0x30,0xd8,0xc1,0x40, - 0x07,0x77,0xb0,0x21,0xb0,0x26,0x08,0xc5,0xb7,0x41,0xc8,0xb2,0x0d,0x8b,0x25,0x07, - 0x73,0x40,0x07,0x7b,0x40,0x07,0x03,0x1f,0x58,0x74,0xd0,0x07,0x13,0x84,0x02,0x0c, - 0x36,0x04,0xd9,0x04,0xa1,0x08,0x83,0x09,0x02,0x51,0x6d,0x10,0x32,0x51,0xd8,0xb0, - 0x64,0x72,0xf0,0x07,0x74,0x00,0x0a,0x74,0x30,0x84,0x42,0x46,0x07,0xa3,0xb0,0x21, - 0xb8,0x36,0x2c,0x97,0x1c,0xcc,0x01,0x1d,0x94,0x02,0x1d,0x0c,0xa1,0x70,0xd1,0xc1, - 0x28,0x6c,0x28,0xf0,0x40,0x0f,0xfc,0x80,0x14,0x4c,0x61,0xc3,0x42,0xc8,0xc1,0x1c, - 0xd0,0x41,0x1d,0xf0,0xc1,0x60,0x07,0x04,0x1d,0xdc,0xc1,0x86,0x65,0x90,0x83,0x39, - 0xa0,0x83,0x3c,0xe0,0x83,0xc1,0x0e,0x06,0x3a,0xb8,0x83,0x0d,0x8b,0x25,0x07,0x73, - 0x40,0x07,0x7b,0xc0,0x07,0x03,0x1f,0x58,0x74,0xd0,0x07,0x5c,0xa6,0xac,0xbe,0xa0, - 0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x36,0x2c,0xd9,0x2a,0xcc,0x81,0x1d,0xd4,0x41, - 0x28,0x0c,0xa1,0x90,0xd1,0xc1,0x28,0x6c,0x20,0x50,0x21,0x15,0x54,0x81,0x15,0x36, - 0x0c,0xa7,0xd0,0x0a,0xc0,0x86,0x42,0x0d,0xe2,0xc0,0x15,0x26,0x80,0x51,0x98,0x9c, - 0x5c,0x58,0xde,0x17,0xdb,0xdb,0x58,0x18,0xdb,0x97,0x58,0x1e,0x5d,0xd9,0xdc,0x04, - 0x81,0xb0,0x08,0x85,0xc9,0xc9,0x85,0xe5,0x7d,0xb1,0xbd,0x8d,0x85,0xb1,0x7d,0xb1, - 0x91,0xcd,0xd1,0x4d,0x10,0x88,0x8b,0x86,0x19,0xdb,0x5b,0x18,0xdd,0xdc,0x04,0x81, - 0xc0,0x58,0xa4,0xb9,0xcd,0xd1,0xcd,0x4d,0x10,0x88,0x8c,0x44,0x9a,0x1b,0xdd,0x1c, - 0x8d,0xb9,0xb4,0xb3,0x2f,0x36,0xb2,0x09,0x02,0xa1,0xd1,0x98,0x4b,0x3b,0xfb,0x9a, - 0xa3,0x9b,0x20,0x10,0x1b,0x8b,0xba,0x34,0x37,0xba,0xb9,0x0d,0x10,0x2c,0xc4,0x82, - 0x2c,0xcc,0x02,0x2d,0xd4,0x82,0x2d,0xdc,0x02,0x2e,0x5c,0xb9,0xa0,0x0b,0xbb,0xc0, - 0x0b,0xbd,0xc0,0x0b,0x55,0xd8,0xd8,0xec,0xda,0x5c,0xd2,0xc8,0xca,0xdc,0xe8,0xa6, - 0x04,0x41,0x15,0x32,0x3c,0x17,0xbb,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x29,0x01,0xd1, - 0x84,0x0c,0xcf,0xc5,0x2e,0x8c,0xcd,0xae,0x4c,0x6e,0x4a,0x50,0xd4,0x21,0xc3,0x73, - 0x99,0x43,0x0b,0x23,0x2b,0x93,0x6b,0x7a,0x23,0x2b,0x63,0x9b,0x12,0x20,0x65,0xc8, - 0xf0,0x5c,0xe4,0xca,0xe6,0xde,0xea,0xe4,0xc6,0xca,0xe6,0xa6,0x04,0x52,0x25,0x32, - 0x3c,0x17,0xba,0x3c,0xb8,0xb2,0x20,0x37,0xb7,0x37,0xba,0x30,0xba,0xb4,0x37,0xb7, - 0xb9,0x29,0x02,0x1a,0xb4,0x41,0x1d,0x32,0x3c,0x17,0xbb,0xb4,0xb2,0xbb,0x24,0xb2, - 0x29,0xba,0x30,0xba,0xb2,0x29,0xc1,0x1b,0xd4,0x21,0xc3,0x73,0x29,0x73,0xa3,0x93, - 0xcb,0x83,0x7a,0x4b,0x73,0xa3,0x9b,0x9b,0x12,0xb8,0x42,0x17,0x32,0x3c,0x97,0xb1, - 0xb7,0x3a,0x37,0xba,0x32,0xb9,0xb9,0x29,0x41,0x2f,0x00,0x00,0x79,0x18,0x00,0x00, - 0x4c,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, - 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, - 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, - 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, - 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, - 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, - 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, - 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, - 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, - 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, - 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, - 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, - 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, - 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, - 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, - 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, - 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70, - 0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87, - 0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e, - 0xf0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x24,0x00,0x00,0x00,0x66,0xb0,0x0d,0x97, - 0xef,0x3c,0xbe,0x10,0x50,0x45,0x41,0x44,0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6, - 0x17,0xb7,0x6d,0x08,0xd2,0x70,0xf9,0xce,0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34, - 0xc3,0x42,0x18,0x81,0x33,0x5c,0xbe,0xf3,0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16, - 0x30,0x0d,0x97,0xef,0x3c,0xfe,0xe2,0x00,0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d, - 0x02,0xd3,0x70,0xf9,0xce,0xe3,0x2f,0x0e,0x30,0x88,0xcd,0x43,0x4d,0x3e,0x72,0xdb, - 0x36,0x50,0x0d,0x97,0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52,0xd3,0x43,0x4d,0x7e, - 0x71,0xdb,0x76,0x20,0x0d,0x97,0xef,0x3c,0xfe,0x44,0x44,0x13,0x02,0x44,0x98,0x5f, - 0xdc,0xb6,0x15,0x3c,0xc3,0xe5,0x3b,0x8f,0x4f,0x35,0x40,0x84,0xf9,0xc5,0x6d,0x1b, - 0x40,0x53,0x11,0xd1,0x44,0x5c,0xac,0x82,0x34,0x0d,0x31,0x00,0x00,0x00,0x00,0x00, - 0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0xf2,0x97,0xd6, - 0xad,0x07,0x8f,0xd0,0x8b,0x74,0xf8,0xde,0xae,0x5e,0x77,0x85,0x44,0x58,0x49,0x4c, - 0x4c,0x19,0x00,0x00,0x60,0x00,0x01,0x00,0x53,0x06,0x00,0x00,0x44,0x58,0x49,0x4c, - 0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x34,0x19,0x00,0x00,0x42,0x43,0xc0,0xde, - 0x21,0x0c,0x00,0x00,0x4a,0x06,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39, - 0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x18,0x45,0x02, - 0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x62,0x88, - 0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90, - 0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x31,0x46,0x06, - 0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07, - 0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff, - 0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00, - 0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, - 0x35,0x00,0x00,0x00,0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04,0x13,0x23,0xa4,0x84, - 0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c,0x0b,0x84,0xc4,0x4c, - 0x10,0x98,0xc1,0x08,0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00, - 0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51,0x0c,0x82,0x20,0x88,0x82,0x20,0xa4,0x18, - 0x00,0x41,0x10,0xc5,0x40,0x4a,0x41,0x06,0x62,0x18,0x86,0x61,0x18,0x88,0x29,0xc3, - 0x40,0x0c,0xe4,0x1c,0x35,0x5c,0xfe,0x84,0x3d,0x84,0xe4,0x73,0x1b,0x55,0xac,0xc4, - 0xe4,0x17,0xb7,0x8d,0x88,0x61,0x18,0x06,0x2a,0xee,0x19,0x2e,0x7f,0xc2,0x1e,0x42, - 0xf2,0x43,0xa0,0x19,0x16,0x02,0x05,0x51,0x21,0x20,0x42,0x22,0x68,0x2a,0xc5,0x40, - 0x0c,0xc3,0x40,0xd5,0x1c,0x41,0x50,0x0c,0x89,0x28,0x08,0xe2,0x22,0x6c,0x20,0x60, - 0x18,0x81,0x18,0x92,0x60,0x3b,0xee,0x70,0xa4,0x69,0x01,0x30,0x87,0x9a,0xfc,0x09, - 0x7b,0x88,0xbf,0x8b,0x00,0xcb,0x6d,0x90,0xc2,0x89,0x18,0x09,0x0d,0x1c,0xc7,0x51, - 0x97,0x80,0x01,0xdc,0x71,0x87,0x23,0x4d,0x0b,0x80,0x39,0xd4,0xe4,0x4f,0xd8,0x43, - 0xfc,0xac,0x82,0x34,0x6e,0x83,0x14,0x4e,0xc4,0x48,0x28,0xf8,0x08,0x4c,0x0b,0x22, - 0x09,0x06,0x1a,0x69,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87, - 0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e, - 0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, - 0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20, - 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07, - 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, - 0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20, - 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, - 0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76, - 0x40,0x07,0x43,0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86, - 0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20, - 0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x28,0x40,0x00,0x08,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x61,0x80,0x00,0x08,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x60,0xc8,0xe3,0x00,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xc0,0x90,0x67,0x02,0x02,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x4f, - 0x05,0x04,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x1e,0x0c,0x08,0x80, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb2,0x40,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x32,0x1e,0x98,0x14,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22, - 0x4a,0x60,0x04,0xa0,0x24,0x8a,0xa1,0x00,0x07,0x0a,0x10,0x30,0xa0,0x0c,0xca,0xa1, - 0x08,0xca,0xa3,0x10,0xa8,0x28,0x89,0x11,0x80,0x32,0x28,0x82,0x52,0x28,0x04,0xf2, - 0x66,0x00,0x28,0x9c,0x01,0x20,0x71,0x2c,0x8b,0x82,0x4e,0x90,0x0f,0x4e,0x90,0x0f, - 0x4e,0x90,0x0f,0x00,0x78,0x80,0x07,0x78,0x00,0x40,0x04,0x84,0x00,0xe0,0x1e,0xf9, - 0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0,0x1e,0xf9,0xe0, - 0x1e,0xf9,0xe0,0x1e,0xf9,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x76,0x00,0x00,0x00, - 0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd, - 0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66, - 0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x88,0x64,0x82,0x40, - 0x28,0x1b,0x84,0x81,0x98,0x20,0x10,0xcb,0x06,0x61,0x30,0x28,0xd8,0xcd,0x4d,0x10, - 0x08,0x66,0xc3,0x80,0x24,0xc4,0x04,0xc1,0xd3,0x08,0x4c,0x10,0x88,0x66,0x03,0x42, - 0x2c,0xcc,0x40,0x0c,0x0d,0x30,0x41,0x08,0x83,0x6d,0x82,0x40,0x38,0x1b,0x90,0xe1, - 0x61,0x86,0x61,0x80,0x80,0x0d,0x82,0x13,0x6d,0x20,0x00,0x40,0x02,0x26,0x08,0x62, - 0xc0,0x6d,0x08,0xa8,0x09,0x82,0x00,0xb0,0xb1,0x2b,0x93,0xa3,0x2b,0xc3,0x9b,0x5a, - 0x4b,0x73,0x73,0x2b,0x23,0x23,0x42,0x55,0x84,0x35,0xf4,0xf4,0x24,0x45,0x34,0x41, - 0x28,0xa8,0x09,0x42,0x51,0x6d,0x08,0x88,0x09,0x42,0x61,0x4d,0x10,0x88,0x67,0x82, - 0x40,0x40,0x1b,0x84,0xce,0xdb,0xb0,0x10,0x58,0xa6,0x6d,0xda,0xc0,0x11,0xda,0xb7, - 0x21,0x18,0x36,0x2c,0x03,0x96,0x69,0x61,0xa0,0x0d,0xdc,0xa0,0x7d,0x13,0x04,0x22, - 0xda,0x10,0x8c,0xc1,0x04,0xa1,0xb8,0x36,0x08,0x5d,0xb7,0x61,0x19,0x03,0x2c,0xd3, - 0xc8,0x40,0x1b,0xca,0x60,0x0c,0x34,0x33,0x98,0x20,0x14,0xd8,0x86,0xa0,0x9b,0x20, - 0x14,0xd9,0x04,0x81,0x90,0x36,0x08,0xdd,0x1a,0x6c,0x58,0x3a,0x0c,0x0d,0xb4,0x34, - 0xd0,0x06,0x35,0xe8,0x34,0x36,0x98,0x20,0x10,0xd3,0x86,0xc0,0x0d,0x36,0x2c,0x6e, - 0x80,0x65,0xda,0x1b,0x68,0x83,0x1a,0xb8,0x81,0xc6,0x06,0x1b,0x0a,0x30,0x10,0x83, - 0x33,0x68,0x03,0x38,0xd8,0xb0,0x10,0x58,0xa6,0x6d,0x65,0x30,0x70,0x84,0xf6,0x6d, - 0x58,0x06,0x2c,0xd3,0xc2,0xa0,0x0c,0x06,0x6e,0xd0,0xbe,0x0d,0xcb,0x18,0x60,0x99, - 0x46,0x06,0x65,0x30,0x94,0xc1,0x18,0x68,0x66,0xc0,0x65,0xca,0xea,0x0b,0xea,0x6d, - 0x2e,0x8d,0x2e,0xed,0xcd,0x6d,0xc3,0xd2,0xd5,0x41,0xc6,0x6d,0x6a,0x30,0xa8,0x41, - 0xa7,0xb1,0xc1,0x06,0x42,0x0e,0xe6,0x80,0x0e,0xec,0x60,0xc3,0x10,0x07,0x77,0x00, - 0x6c,0x28,0xac,0x0b,0x0f,0x26,0xa0,0x0a,0x1b,0x9b,0x5d,0x9b,0x4b,0x1a,0x59,0x99, - 0x1b,0xdd,0x94,0x20,0xa8,0x42,0x86,0xe7,0x62,0x57,0x26,0x37,0x97,0xf6,0xe6,0x36, - 0x25,0x20,0x9a,0x90,0xe1,0xb9,0xd8,0x85,0xb1,0xd9,0x95,0xc9,0x4d,0x09,0x8c,0x3a, - 0x64,0x78,0x2e,0x73,0x68,0x61,0x64,0x65,0x72,0x4d,0x6f,0x64,0x65,0x6c,0x53,0x82, - 0xa4,0x0c,0x19,0x9e,0x8b,0x5c,0xd9,0xdc,0x5b,0x9d,0xdc,0x58,0xd9,0xdc,0x94,0x40, - 0xaa,0x43,0x86,0xe7,0x62,0x97,0x56,0x76,0x97,0x44,0x36,0x45,0x17,0x46,0x57,0x36, - 0x25,0xa0,0xea,0x90,0xe1,0xb9,0x94,0xb9,0xd1,0xc9,0xe5,0x41,0xbd,0xa5,0xb9,0xd1, - 0xcd,0x4d,0x09,0xf0,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x4c,0x00,0x00,0x00, - 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, - 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, - 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, - 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, - 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, - 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, - 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, - 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, - 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, - 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, - 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, - 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, - 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, - 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, - 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, - 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, - 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87, - 0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a, - 0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e,0xf0,0x00,0x00,0x00, - 0x71,0x20,0x00,0x00,0x24,0x00,0x00,0x00,0x66,0xb0,0x0d,0x97,0xef,0x3c,0xbe,0x10, - 0x50,0x45,0x41,0x44,0xa5,0x03,0x0c,0x25,0x61,0x00,0x02,0xe6,0x17,0xb7,0x6d,0x08, - 0xd2,0x70,0xf9,0xce,0xe3,0x0b,0x11,0x01,0x4c,0x44,0x08,0x34,0xc3,0x42,0x18,0x81, - 0x33,0x5c,0xbe,0xf3,0xf8,0x83,0x33,0xdd,0x7e,0x71,0xdb,0x16,0x30,0x0d,0x97,0xef, - 0x3c,0xfe,0xe2,0x00,0x83,0xd8,0x3c,0xd4,0xe4,0x17,0xb7,0x6d,0x02,0xd3,0x70,0xf9, - 0xce,0xe3,0x2f,0x0e,0x30,0x88,0xcd,0x43,0x4d,0x3e,0x72,0xdb,0x36,0x50,0x0d,0x97, - 0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52,0xd3,0x43,0x4d,0x7e,0x71,0xdb,0x76,0x20, - 0x0d,0x97,0xef,0x3c,0xfe,0x44,0x44,0x13,0x02,0x44,0x98,0x5f,0xdc,0xb6,0x15,0x3c, - 0xc3,0xe5,0x3b,0x8f,0x4f,0x35,0x40,0x84,0xf9,0xc5,0x6d,0x1b,0x40,0x53,0x11,0xd1, - 0x44,0x5c,0xac,0x82,0x34,0x0d,0x31,0x00,0x61,0x20,0x00,0x00,0xa3,0x04,0x00,0x00, - 0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x44,0x14,0x57,0xd9, - 0xcd,0x00,0x94,0xc2,0x28,0x03,0xa0,0x24,0xa3,0x0c,0x80,0xa2,0x8c,0x32,0x00,0x4a, - 0x34,0xca,0xb0,0xb0,0x41,0x52,0x72,0xa3,0x0c,0x80,0xd2,0x06,0xa3,0x0c,0x80,0x19, - 0x24,0xa3,0x0c,0x40,0x18,0x24,0xa3,0x0c,0x80,0x32,0x8d,0x32,0x00,0x69,0x90,0x8c, - 0x32,0x00,0x6a,0x90,0x8c,0x32,0x00,0x49,0x52,0x10,0xa3,0x0c,0xc0,0x19,0x24,0xe5, - 0x56,0x32,0xa3,0x0c,0x00,0x1a,0x24,0xa3,0x0c,0xc0,0x1a,0x24,0x54,0x94,0x00,0x5d, - 0x23,0x00,0x00,0x00,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0xb5,0x01,0xe4,0xb1,0x01, - 0x1b,0x04,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0xb9,0x41,0xf4,0xb1,0x01,0x1b,0x08, - 0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63, - 0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24,0xa3, - 0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31, - 0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32, - 0x0a,0x33,0x11,0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11, - 0x63,0x40,0x24,0xa3,0x30,0x13,0x31,0x06,0x44,0x32,0x0a,0x33,0x11,0x63,0x40,0x24, - 0xa3,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x46,0x29,0xcc,0xc1,0x1c,0xf0,0x41,0x1c, - 0x9c,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xa6,0x40,0x07,0x74,0xd0,0x07,0x14, - 0x1a,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0x71,0x0a,0x75,0x50,0x07,0x7e,0x10,0x07, - 0x69,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x06,0x2a,0xd8,0x81,0x1d,0xfc,0xc1,0x1c, - 0xa8,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x20,0xa8,0x70,0x07,0x7b,0x00,0x0a,0x75, - 0xb0,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0xa4,0x02,0x1e,0xf0,0x41,0x28,0x60, - 0x6c,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x2a,0xe4,0x41,0x1f,0x88,0x42,0x1d, - 0xb4,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x20,0xab,0xa0,0x07,0x7e,0x30,0x0a,0x77, - 0xe0,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xb4,0xc2,0x1e,0xf4,0x01,0x29,0xe4, - 0xc1,0x1b,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0xe1,0x0a,0x7c,0xe0,0x07,0xa5,0xc0, - 0xc1,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xaf,0xd0,0x07,0xa7,0x60,0x0a,0x7b, - 0x10,0x07,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xc0,0x82,0x1f,0xa0,0xc2,0x29,0x78, - 0x72,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x46,0x2c,0xfc,0x41,0x2a,0xa0,0xc2,0x1e, - 0xcc,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xb2,0x00,0x0a,0xa9,0x90,0x0a,0x7e, - 0x40,0x07,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xcc,0x42,0x28,0xa8,0x82,0x2a,0x84, - 0x41,0x1d,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0x41,0x0b,0xa2,0xb0,0x0a,0xab,0xe0, - 0x07,0x76,0x50,0x4c,0x29,0xdc,0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0xb1,0x90,0x07, - 0x62,0x10,0x8c,0x26,0x04,0xc0,0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31, - 0x94,0x31,0x0b,0x3b,0x62,0x70,0x00,0x20,0x08,0x06,0xd3,0x2d,0xfc,0x01,0x1a,0x04, - 0xa3,0x09,0x01,0x30,0x9a,0x20,0x04,0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x66,0xe4, - 0x02,0x08,0x46,0x0c,0x0e,0x00,0x04,0xc1,0x60,0xea,0x85,0x52,0x70,0x83,0x60,0x34, - 0x21,0x00,0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0xa1,0x24,0x52,0xd8, - 0x11,0x83,0x03,0x00,0x41,0x30,0x98,0xc6,0x61,0x15,0xe8,0x20,0x18,0x4d,0x08,0x80, - 0xd1,0x04,0x21,0x18,0x4d,0x18,0x84,0xd1,0x04,0x62,0x30,0x6b,0x93,0xcf,0x88,0x01, - 0x02,0x80,0x20,0x18,0x54,0xe8,0x20,0x0b,0xd1,0x16,0x8c,0x18,0x20,0x00,0x08,0x82, - 0x41,0x95,0x0e,0xb3,0xc0,0x6c,0x81,0x05,0x07,0x74,0x2c,0xfb,0xe4,0x33,0x62,0x80, - 0x00,0x20,0x08,0x06,0x15,0x3b,0xd8,0x02,0xf5,0x05,0x23,0x06,0x08,0x00,0x82,0x60, - 0x50,0xb5,0xc3,0x2d,0x3c,0x5f,0x60,0x81,0x02,0x1d,0xe3,0xc6,0x40,0x3e,0x23,0x06, - 0x08,0x00,0x82,0x60,0x50,0xc1,0x83,0x2e,0x5c,0x63,0x10,0x8c,0x18,0x20,0x00,0x08, - 0x82,0x41,0x15,0x0f,0xbb,0x20,0x8d,0x41,0x60,0x41,0x03,0x1d,0xfb,0xce,0x40,0x3e, - 0x23,0x06,0x08,0x00,0x82,0x60,0x50,0xd1,0x83,0x2f,0x68,0x67,0x10,0x8c,0x18,0x20, - 0x00,0x08,0x82,0x41,0x55,0x0f,0xbf,0x50,0x9d,0x41,0x60,0x01,0x04,0x1d,0x6b,0xf0, - 0x40,0x3e,0xa6,0xe4,0x81,0x7c,0xec,0xd0,0x03,0xf9,0x18,0xb1,0x07,0xf2,0xa9,0x3b, - 0x88,0x87,0x1b,0x31,0x38,0x00,0x10,0x04,0x83,0xa9,0x1f,0xca,0xc1,0x15,0x82,0xd1, - 0x84,0x00,0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x18,0x4d,0x20,0x86,0x32,0xfe,0x61, - 0x47,0x0c,0x0e,0x00,0x04,0xc1,0x60,0x1a,0x89,0x75,0xa0,0x85,0x60,0x34,0x21,0x00, - 0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0xc1,0x8c,0x92,0x00,0xc1,0x88, - 0xc1,0x01,0x80,0x20,0x18,0x4c,0x29,0x11,0x0f,0xba,0x10,0x8c,0x26,0x04,0xc0,0x68, - 0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0x94,0x04,0x0f,0x3b,0x62,0x70,0x00, - 0x20,0x08,0x06,0xd3,0x4b,0xdc,0x03,0x38,0x04,0xa3,0x09,0x01,0x30,0x9a,0x20,0x04, - 0xa3,0x09,0x83,0x30,0x9a,0x40,0x0c,0x66,0x9d,0x82,0x7c,0x46,0x0c,0x10,0x00,0x04, - 0xc1,0xa0,0xa2,0x09,0x7f,0x88,0x4e,0x21,0x18,0x31,0x40,0x00,0x10,0x04,0x83,0xaa, - 0x26,0xfe,0x81,0x39,0x85,0xc0,0x82,0x03,0x3a,0x96,0xad,0x82,0x7c,0x46,0x0c,0x10, - 0x00,0x04,0xc1,0xa0,0xc2,0x09,0x91,0xa0,0x56,0x21,0x18,0x31,0x40,0x00,0x10,0x04, - 0x83,0x2a,0x27,0x46,0xe2,0x59,0x85,0xc0,0x02,0x05,0x3a,0xc6,0xbd,0x82,0x7c,0x46, - 0x0c,0x10,0x00,0x04,0xc1,0xa0,0xe2,0x09,0x93,0xb8,0x5e,0x21,0x18,0x31,0x40,0x00, - 0x10,0x04,0x83,0xaa,0x27,0x4e,0x42,0x7a,0x85,0xc0,0x82,0x06,0x3a,0xf6,0xcd,0x82, - 0x7c,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0x02,0x0b,0x95,0xd0,0x66,0x21,0x18,0x31, - 0x40,0x00,0x10,0x04,0x83,0x2a,0x2c,0x56,0xa2,0x9a,0x85,0xc0,0x02,0x08,0x3a,0xd6, - 0x8c,0x83,0x7c,0x4c,0x21,0x07,0xf9,0xd8,0x51,0x0e,0xf2,0x31,0xc2,0x1c,0xe4,0x63, - 0x04,0x1c,0xc0,0xc7,0x08,0x38,0x80,0x8f,0x11,0x70,0x00,0x1f,0x23,0xe0,0x00,0x3e, - 0x65,0x0e,0x61,0x71,0x23,0x06,0x07,0x00,0x82,0x60,0x30,0xb5,0x45,0x4d,0xf8,0x43, - 0x30,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0x50,0xc6, - 0x5b,0xec,0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0x73,0xb1,0x13,0x24,0x11,0x8c,0x26, - 0x04,0xc0,0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0x98,0x51,0x17,0x20, - 0x18,0x31,0x38,0x00,0x10,0x04,0x83,0x29,0x2f,0xc2,0x42,0x25,0x82,0xd1,0x84,0x00, - 0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x18,0x4d,0x20,0x86,0x92,0xc0,0x62,0x47,0x0c, - 0x0e,0x00,0x04,0xc1,0x60,0xfa,0x8b,0xb3,0x80,0x89,0x60,0x34,0x21,0x00,0x46,0x13, - 0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0xc1,0xac,0x7b,0x90,0xcf,0x88,0x01,0x02, - 0x80,0x20,0x18,0x54,0xa4,0xe1,0x16,0xd1,0x3d,0x04,0x23,0x06,0x08,0x00,0x82,0x60, - 0x50,0x95,0xc6,0x5b,0x30,0xf7,0x10,0x58,0x70,0x40,0xc7,0xb2,0x7d,0x90,0xcf,0x88, - 0x01,0x02,0x80,0x20,0x18,0x54,0xa8,0x21,0x17,0xd4,0x3e,0x04,0x23,0x06,0x08,0x00, - 0x82,0x60,0x50,0xa5,0xc6,0x5c,0x3c,0xfb,0x10,0x58,0xa0,0x40,0xc7,0xb8,0x7f,0x90, - 0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xac,0x61,0x17,0xd7,0x3f,0x04,0x23,0x06, - 0x08,0x00,0x82,0x60,0x50,0xb5,0xc6,0x5d,0x48,0xff,0x10,0x58,0xd0,0x40,0xc7,0xbe, - 0x91,0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xb0,0xa1,0x17,0xda,0x48,0x04, - 0x23,0x06,0x08,0x00,0x82,0x60,0x50,0xc5,0xc6,0x5e,0x54,0x23,0x11,0x58,0x00,0x41, - 0xc7,0x1a,0x99,0x90,0x8f,0x29,0x33,0x21,0x1f,0x3b,0x68,0x42,0x3e,0x46,0xd4,0x84, - 0x7c,0x0c,0x0e,0x08,0xf8,0x18,0x1c,0x10,0xf0,0x31,0x38,0x20,0xe0,0x63,0x70,0x40, - 0xc0,0xa7,0x6a,0x22,0x36,0x6e,0xc4,0xe0,0x00,0x40,0x10,0x0c,0xa6,0xde,0x28,0x0d, - 0xb7,0x08,0x46,0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06,0x61,0x34,0x81,0x18, - 0xca,0xf8,0x8d,0x1d,0x31,0x38,0x00,0x10,0x04,0x83,0x69,0x3c,0x56,0x83,0x2e,0x82, - 0xd1,0x84,0x00,0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x18,0x4d,0x20,0x06,0x33,0xca, - 0x03,0x04,0x23,0x06,0x07,0x00,0x82,0x60,0x30,0xa5,0x47,0x6c,0xe8,0x45,0x30,0x9a, - 0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0x50,0x12,0x6c,0xec, - 0x88,0xc1,0x01,0x80,0x20,0x18,0x4c,0xef,0x71,0x1b,0xa0,0x11,0x8c,0x26,0x04,0xc0, - 0x68,0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0x98,0x75,0x16,0xf2,0x19,0x31, - 0x40,0x00,0x10,0x04,0x83,0x8a,0x3e,0x7c,0x23,0x3a,0x8b,0x60,0xc4,0x00,0x01,0x40, - 0x10,0x0c,0xaa,0xfa,0xf8,0x0d,0xe6,0x2c,0x02,0x0b,0x0e,0xe8,0x58,0xb6,0x16,0xf2, - 0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x0a,0x3f,0xc4,0x83,0x5a,0x8b,0x60,0xc4,0x00, - 0x01,0x40,0x10,0x0c,0xaa,0xfc,0x18,0x8f,0x67,0x2d,0x02,0x0b,0x14,0xe8,0x18,0xf7, - 0x16,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x8a,0x3f,0xcc,0xe3,0x7a,0x8b,0x60, - 0xc4,0x00,0x01,0x40,0x10,0x0c,0xaa,0xfe,0x38,0x0f,0xe9,0x2d,0x02,0x0b,0x1a,0xe8, - 0xd8,0x37,0x17,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x0a,0x44,0xd4,0x43,0x9b, - 0x8b,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xaa,0x10,0x59,0x8f,0x6a,0x2e,0x02,0x0b, - 0x20,0xe8,0x58,0x13,0x1a,0xf2,0x31,0x45,0x34,0xe4,0x63,0xc7,0x68,0xc8,0xc7,0x08, - 0xd2,0x90,0x8f,0xc1,0x01,0x01,0x1f,0x83,0x03,0x02,0x3e,0x06,0x07,0x04,0x7c,0x0c, - 0x0e,0x08,0xf8,0xec,0x71,0xd8,0x0d,0x14,0x41,0x91,0x61,0x03,0x22,0xd8,0x8b,0x01, - 0xd8,0xe3,0xc0,0x1b,0x29,0xa2,0x22,0xc3,0x06,0x44,0xb0,0x17,0x03,0xb0,0xc7,0xa1, - 0x37,0x54,0x64,0x44,0x86,0x0d,0x88,0x60,0x2f,0x06,0x60,0x8f,0x83,0x6f,0xac,0x88, - 0x89,0x0c,0x1b,0x10,0xc1,0x5e,0x0c,0xc0,0x1e,0x87,0xdf,0x60,0x11,0xfa,0xa0,0x60, - 0x18,0xc3,0x06,0x84,0xa1,0x17,0x03,0x30,0x6c,0x40,0x14,0x79,0x31,0x00,0xc3,0x06, - 0x04,0x81,0x17,0x03,0x30,0x6c,0x40,0x0c,0x77,0x31,0x00,0x24,0x0c,0x63,0xd8,0x80, - 0x38,0xea,0x62,0x00,0x86,0x0d,0x08,0x83,0x2e,0x06,0x60,0xd8,0x80,0x28,0xe6,0x62, - 0x00,0x86,0x0d,0x08,0x42,0x2e,0x06,0x80,0x86,0x61,0xec,0x71,0x10,0x0f,0x18,0x81, - 0x91,0x61,0x03,0x22,0x18,0x8d,0x01,0xd8,0xe3,0x30,0x1e,0x31,0x22,0x23,0xc3,0x06, - 0x44,0x30,0x1a,0x03,0xb0,0xc7,0x81,0x3c,0x64,0x64,0x45,0x86,0x0d,0x88,0x60,0x34, - 0x06,0x60,0x8f,0x43,0x79,0xcc,0x88,0x8b,0x0c,0x1b,0x10,0xc1,0x68,0x0c,0xc0,0x1e, - 0x07,0xf3,0xa0,0x91,0xfe,0xa0,0x60,0x18,0xc3,0x06,0x84,0x21,0x1a,0x03,0x30,0x6c, - 0x40,0x14,0xa1,0x31,0x00,0xc3,0x06,0x04,0x01,0x1a,0x03,0x30,0x6c,0x40,0x0c,0x7f, - 0x31,0x00,0x24,0x0c,0x63,0xd8,0x80,0x38,0xfa,0x62,0x00,0x86,0x0d,0x08,0x83,0x2f, - 0x06,0x60,0xd8,0x80,0x28,0xf6,0x62,0x00,0x86,0x0d,0x08,0x42,0x2f,0x06,0x80,0x86, - 0x61,0xec,0x71,0x48,0x0f,0x1c,0xc1,0x91,0x61,0x03,0x22,0x58,0x8d,0x01,0xd8,0xe3, - 0xa0,0x1e,0x39,0xa2,0x23,0xc3,0x06,0x44,0xb0,0x1a,0x03,0xb0,0xc7,0x61,0x3d,0x74, - 0x64,0x46,0x86,0x0d,0x88,0x60,0x35,0x06,0x60,0x8f,0x03,0x7b,0xec,0x88,0x8d,0x0c, - 0x1b,0x10,0xc1,0x6a,0x0c,0xc0,0x1e,0x87,0xf6,0xe0,0x91,0x11,0xa1,0x60,0x18,0xc3, - 0x06,0x84,0xa1,0x1a,0x03,0x30,0x6c,0x40,0x14,0xa9,0x31,0x00,0xc3,0x06,0x04,0x81, - 0x1a,0x03,0x30,0x6c,0x40,0x0c,0xa7,0x31,0x00,0x24,0x0c,0x63,0xd8,0x80,0x38,0x4a, - 0x63,0x00,0x86,0x0d,0x08,0x83,0x34,0x06,0x60,0xd8,0x80,0x28,0x46,0x63,0x00,0x86, - 0x0d,0x08,0x42,0x34,0x06,0x80,0x86,0x61,0xd8,0xa4,0x1b,0xf2,0x19,0x31,0x40,0x00, - 0x10,0x04,0x83,0x8a,0x4c,0x5c,0x64,0xd2,0x8d,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c, - 0xaa,0x32,0x79,0x91,0x49,0x37,0x02,0x73,0x7a,0x43,0x3e,0x23,0x06,0x08,0x00,0x82, - 0x60,0x50,0x9d,0x49,0x8c,0x38,0xbd,0x11,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0x85, - 0x26,0x32,0xe2,0xf4,0x46,0x60,0x09,0x78,0xc8,0x67,0xc4,0x00,0x01,0x40,0x10,0x0c, - 0x2a,0x35,0xa1,0x91,0x04,0x3c,0x82,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0xd6,0xa4, - 0x46,0x12,0xf0,0x08,0xec,0x68,0x0f,0xf9,0x58,0xe1,0x1e,0xf2,0xb1,0xe1,0x3d,0xe4, - 0xb3,0xc7,0x81,0x3f,0xd8,0x84,0x4d,0x86,0x0d,0x88,0x60,0x2e,0x06,0x60,0x8f,0x43, - 0x7f,0xb4,0x89,0x9b,0x0c,0x1b,0x10,0xc1,0x5c,0x0c,0xc0,0x1e,0x07,0xff,0x70,0x93, - 0x33,0x19,0x36,0x20,0x82,0xb9,0x18,0x80,0x3d,0x0e,0xff,0xf1,0x26,0x6a,0x32,0x6c, - 0x40,0x04,0x73,0x31,0x00,0x7b,0x1c,0x40,0x04,0x4e,0x70,0x84,0x82,0x61,0x0c,0x1b, - 0x10,0x86,0x5c,0x0c,0xc0,0xb0,0x01,0x51,0xc4,0xc5,0x00,0x0c,0x1b,0x10,0x04,0x5c, - 0x0c,0xc0,0xb0,0x01,0x31,0xbc,0xc5,0x00,0x90,0x30,0x8c,0x61,0x03,0xe2,0x68,0x8b, - 0x01,0x18,0x36,0x20,0x0c,0xb6,0x18,0x80,0x61,0x03,0xa2,0x58,0x8b,0x01,0x18,0x36, - 0x20,0x08,0xb5,0x18,0x00,0x1a,0x86,0xb1,0xc7,0x61,0x44,0xe8,0x84,0x4e,0x86,0x0d, - 0x88,0x60,0x2f,0x06,0x60,0x8f,0x03,0x89,0xd4,0x89,0x9d,0x0c,0x1b,0x10,0xc1,0x5e, - 0x0c,0xc0,0x1e,0x87,0x12,0xb1,0x93,0x37,0x19,0x36,0x20,0x82,0xbd,0x18,0x80,0x3d, - 0x0e,0x26,0x72,0x27,0x72,0x32,0x6c,0x40,0x04,0x7b,0x31,0x00,0x7b,0x1c,0x4e,0x04, - 0x4f,0xc2,0x84,0x82,0x61,0x0c,0x1b,0x10,0x86,0x5e,0x0c,0xc0,0xb0,0x01,0x51,0xe4, - 0xc5,0x00,0x0c,0x1b,0x10,0x04,0x5e,0x0c,0xc0,0xb0,0x01,0x31,0xdc,0xc5,0x00,0x90, - 0x30,0x8c,0x61,0x03,0xe2,0xa8,0x8b,0x01,0x18,0x36,0x20,0x0c,0xba,0x18,0x80,0x61, - 0x03,0xa2,0x98,0x8b,0x01,0x18,0x36,0x20,0x08,0xb9,0x18,0x00,0x1a,0x86,0xb1,0xc7, - 0x41,0x45,0xf8,0x84,0x4f,0x86,0x0d,0x88,0x60,0x34,0x06,0x60,0x8f,0xc3,0x8a,0xf4, - 0x89,0x9f,0x0c,0x1b,0x10,0xc1,0x68,0x0c,0xc0,0x1e,0x07,0x16,0xf1,0x93,0x3b,0x19, - 0x36,0x20,0x82,0xd1,0x18,0x80,0x3d,0x0e,0x2d,0xf2,0x27,0x7a,0x32,0x6c,0x40,0x04, - 0xa3,0x31,0x00,0x7b,0x1c,0x5c,0x04,0x54,0xce,0x84,0x82,0x61,0x0c,0x1b,0x10,0x86, - 0x68,0x0c,0xc0,0xb0,0x01,0x51,0x84,0xc6,0x00,0x0c,0x1b,0x10,0x04,0x68,0x0c,0xc0, - 0xb0,0x01,0x31,0xfc,0xc5,0x00,0x90,0x30,0x8c,0x61,0x03,0xe2,0xe8,0x8b,0x01,0x18, - 0x36,0x20,0x0c,0xbe,0x18,0x80,0x61,0x03,0xa2,0xd8,0x8b,0x01,0x18,0x36,0x20,0x08, - 0xbd,0x18,0x00,0x1a,0x86,0x61,0x93,0x7f,0xc8,0x67,0xc4,0x00,0x01,0x40,0x10,0x0c, - 0x2a,0x54,0x91,0x93,0xc9,0x3f,0x82,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0x52,0x65, - 0x4e,0x26,0xff,0x08,0xcc,0x09,0x11,0xf9,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0xb5, - 0x2a,0x75,0xe2,0x84,0x48,0x30,0x62,0x80,0x00,0x20,0x08,0x06,0x15,0xab,0xd8,0x89, - 0x13,0x22,0x81,0x25,0x24,0x22,0x9f,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0x5c,0x05, - 0x4f,0x12,0x12,0x09,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0x7a,0x95,0x3c,0x49,0x48, - 0x24,0xb0,0x03,0x46,0xe4,0x63,0x45,0x8c,0xc8,0xc7,0x06,0x19,0x91,0x8f,0x0d,0x67, - 0x00,0x1f,0x1b,0xce,0x00,0x3e,0x36,0x9c,0x01,0x7c,0xf6,0x38,0x80,0xc9,0xac,0xcc, - 0xca,0xb0,0x01,0x11,0xa8,0xc5,0x00,0xec,0x71,0x08,0x13,0x5a,0xa9,0x95,0x61,0x03, - 0x22,0x50,0x8b,0x01,0xd8,0xe3,0x20,0x26,0xb5,0xe2,0x2a,0xc3,0x06,0x44,0xa0,0x16, - 0x03,0xb0,0xc7,0x61,0x4c,0x6c,0x25,0x56,0x86,0x0d,0x88,0x40,0x2d,0x06,0x60,0x8f, - 0x03,0x99,0xdc,0xca,0x9f,0x50,0x30,0x8c,0x61,0x03,0xc2,0x48,0x8b,0x01,0x18,0x36, - 0x20,0x0a,0xb4,0x18,0x80,0x61,0x03,0x82,0x38,0x8b,0x01,0x18,0x36,0x20,0x06,0xb3, - 0x18,0x00,0x12,0x86,0x31,0x6c,0x40,0x1c,0x64,0x31,0x00,0xc3,0x06,0x84,0x31,0x16, - 0x03,0x30,0x6c,0x40,0x14,0x62,0x31,0x00,0xc3,0x06,0x04,0x11,0x16,0x03,0x40,0xc3, - 0x30,0xf6,0x38,0x9c,0xc9,0xae,0xec,0xca,0xb0,0x01,0x11,0xc8,0xc5,0x00,0xec,0x71, - 0x40,0x13,0x5e,0xe9,0x95,0x61,0x03,0x22,0x90,0x8b,0x01,0xd8,0xe3,0x90,0x26,0xbd, - 0x62,0x2b,0xc3,0x06,0x44,0x20,0x17,0x03,0xb0,0xc7,0x41,0x4d,0x7c,0x25,0x57,0x86, - 0x0d,0x88,0x40,0x2e,0x06,0x60,0x8f,0xc3,0x9a,0xfc,0x0a,0xaa,0x50,0x30,0x8c,0x61, - 0x03,0xc2,0x88,0x8b,0x01,0x18,0x36,0x20,0x0a,0xb8,0x18,0x80,0x61,0x03,0x82,0x78, - 0x8b,0x01,0x18,0x36,0x20,0x06,0xb7,0x18,0x00,0x12,0x86,0x31,0x6c,0x40,0x1c,0x6c, - 0x31,0x00,0xc3,0x06,0x84,0xb1,0x16,0x03,0x30,0x6c,0x40,0x14,0x6a,0x31,0x00,0xc3, - 0x06,0x04,0x91,0x16,0x03,0x40,0xc3,0x30,0xf6,0x38,0xb8,0xc9,0xb8,0x8c,0xcb,0xb0, - 0x01,0x11,0xe8,0xc5,0x00,0xec,0x71,0x78,0x13,0x72,0x29,0x97,0x61,0x03,0x22,0xd0, - 0x8b,0x01,0xd8,0xe3,0x00,0x27,0xe5,0xe2,0x2b,0xc3,0x06,0x44,0xa0,0x17,0x03,0xb0, - 0xc7,0x21,0x4e,0xcc,0x25,0x5c,0x86,0x0d,0x88,0x40,0x2f,0x06,0x60,0x8f,0x83,0x9c, - 0x9c,0x8b,0xab,0x50,0x30,0x8c,0x61,0x03,0xc2,0xc8,0x8b,0x01,0x18,0x36,0x20,0x0a, - 0xbc,0x18,0x80,0x61,0x03,0x82,0xb8,0x8b,0x01,0x18,0x36,0x20,0x06,0xbb,0x18,0x00, - 0x12,0x86,0x31,0x6c,0x40,0x1c,0x74,0x31,0x00,0xc3,0x06,0x84,0x31,0x17,0x03,0x30, - 0x6c,0x40,0x14,0x72,0x31,0x00,0xc3,0x06,0x04,0x11,0x17,0x03,0x40,0xc3,0x30,0x6c, - 0x2a,0x13,0xf9,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0xf5,0x2e,0xb9,0x32,0x95,0x49, - 0x30,0x62,0x80,0x00,0x20,0x08,0x06,0x15,0xbc,0xe8,0xca,0x54,0x26,0x81,0x39,0x68, - 0x22,0x9f,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0xe4,0x85,0x57,0x1c,0x34,0x09,0x46, - 0x0c,0x10,0x00,0x04,0xc1,0xa0,0x9a,0x97,0x5e,0x71,0xd0,0x24,0xb0,0x64,0x4d,0xe4, - 0x33,0x62,0x80,0x00,0x20,0x08,0x06,0x55,0xbd,0xfc,0x4a,0xb2,0x26,0xc1,0x88,0x01, - 0x02,0x80,0x20,0x18,0x54,0xf6,0x02,0x2e,0xc9,0x9a,0x04,0x76,0xd8,0x89,0x7c,0xac, - 0xb8,0x13,0xf9,0xd8,0x80,0x27,0xf2,0xb1,0x33,0x18,0xe0,0x63,0x67,0x30,0xc0,0xc7, - 0xce,0x60,0x80,0xcf,0x1e,0x07,0x52,0xd1,0x17,0x7d,0x19,0x36,0x20,0x82,0xb0,0x18, - 0x80,0x3d,0x0e,0xa5,0xb2,0x2f,0xfc,0x32,0x6c,0x40,0x04,0x61,0x31,0x00,0x7b,0x1c, - 0x4c,0x85,0x5f,0xea,0x65,0xd8,0x80,0x08,0xc2,0x62,0x00,0xf6,0x38,0x9c,0x4a,0xbf, - 0xe0,0xcb,0xb0,0x01,0x11,0x84,0xc5,0x00,0xec,0x71,0x40,0x15,0x7f,0x31,0x17,0x0a, - 0x86,0x31,0x6c,0x40,0x18,0x60,0x31,0x00,0xc3,0x06,0x44,0xf1,0x13,0x03,0x30,0x6c, - 0x40,0x10,0x3e,0x31,0x00,0xc3,0x06,0xc4,0xd0,0x13,0x03,0x40,0xc2,0x30,0x86,0x0d, - 0x88,0x63,0x27,0x06,0x60,0xd8,0x80,0x30,0x74,0x62,0x00,0x86,0x0d,0x88,0x22,0x27, - 0x06,0x60,0xd8,0x80,0x20,0x70,0x62,0x00,0x68,0x18,0xc6,0x1e,0x87,0x55,0x11,0x19, - 0x91,0x19,0x36,0x20,0x82,0xb4,0x18,0x80,0x3d,0x0e,0xac,0x32,0x32,0x24,0x33,0x6c, - 0x40,0x04,0x69,0x31,0x00,0x7b,0x1c,0x5a,0x85,0x64,0xfa,0x65,0xd8,0x80,0x08,0xd2, - 0x62,0x00,0xf6,0x38,0xb8,0x4a,0xc9,0x80,0xcc,0xb0,0x01,0x11,0xa4,0xc5,0x00,0xec, - 0x71,0x78,0x15,0x93,0x79,0x17,0x0a,0x86,0x31,0x6c,0x40,0x18,0x68,0x31,0x00,0xc3, - 0x06,0x44,0x71,0x16,0x03,0x30,0x6c,0x40,0x10,0x66,0x31,0x00,0xc3,0x06,0xc4,0x50, - 0x16,0x03,0x40,0xc2,0x30,0x86,0x0d,0x88,0x63,0x2c,0x06,0x60,0xd8,0x80,0x30,0xc4, - 0x62,0x00,0x86,0x0d,0x88,0x22,0x2c,0x06,0x60,0xd8,0x80,0x20,0xc0,0x62,0x00,0x68, - 0x18,0xc6,0x1e,0x07,0x59,0x51,0x19,0x95,0x19,0x36,0x20,0x82,0xb8,0x18,0x80,0x3d, - 0x0e,0xb3,0xb2,0x32,0x2c,0x33,0x6c,0x40,0x04,0x71,0x31,0x00,0x7b,0x1c,0x68,0x85, - 0x65,0x4a,0x66,0xd8,0x80,0x08,0xe2,0x62,0x00,0xf6,0x38,0xd4,0x4a,0xcb,0xa0,0xcc, - 0xb0,0x01,0x11,0xc4,0xc5,0x00,0xec,0x71,0xb0,0x15,0x97,0xa9,0x17,0x0a,0x86,0x31, - 0x6c,0x40,0x18,0x70,0x31,0x00,0xc3,0x06,0x44,0xf1,0x16,0x03,0x30,0x6c,0x40,0x10, - 0x6e,0x31,0x00,0xc3,0x06,0xc4,0xd0,0x16,0x03,0x40,0xc2,0x30,0x86,0x0d,0x88,0x63, - 0x2d,0x06,0x60,0xd8,0x80,0x30,0xd4,0x62,0x00,0x86,0x0d,0x88,0x22,0x2d,0x06,0x60, - 0xd8,0x80,0x20,0xd0,0x62,0x00,0x68,0x18,0x86,0x4d,0xac,0x22,0x9f,0x11,0x03,0x04, - 0x00,0x41,0x30,0xa8,0x6c,0x06,0x64,0x26,0x56,0x09,0x46,0x0c,0x10,0x00,0x04,0xc1, - 0xa0,0xba,0x99,0x90,0x99,0x58,0x25,0x30,0xe7,0x55,0xe4,0x33,0x62,0x80,0x00,0x20, - 0x08,0x06,0x55,0xce,0x8c,0x8c,0xf3,0x2a,0xc1,0x88,0x01,0x02,0x80,0x20,0x18,0x54, - 0x3a,0x43,0x32,0xce,0xab,0x04,0x96,0xc8,0x8a,0x7c,0x46,0x0c,0x10,0x00,0x04,0xc1, - 0xa0,0xe2,0x19,0x93,0x49,0x64,0x25,0x18,0x31,0x40,0x00,0x10,0x04,0x83,0xaa,0x67, - 0x4e,0x26,0x91,0x95,0xc0,0x0e,0x5e,0x91,0x8f,0x15,0xbd,0x22,0x1f,0x1b,0x7c,0x45, - 0x3e,0x76,0x06,0x03,0x7c,0xec,0x0c,0x06,0xf8,0xd8,0x19,0x0c,0xf0,0x19,0x31,0x38, - 0x00,0x10,0x04,0x83,0xa9,0x6c,0x5a,0xa6,0x5e,0x4a,0x66,0x34,0x21,0x00,0x46,0x13, - 0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0x61,0x8f,0x43,0xbb,0x98,0x8d,0xd9,0x0c, - 0x1b,0x10,0x41,0x31,0x00,0x7b,0x1c,0xdc,0xe5,0x6c,0xd0,0x66,0xd8,0x80,0x08,0x8a, - 0x01,0xd8,0xe3,0xf0,0x2e,0x68,0x13,0x36,0xc3,0x06,0x44,0x50,0x0c,0xc0,0x1e,0x07, - 0x78,0x49,0x1b,0xb2,0x19,0x36,0x20,0x82,0x62,0x00,0xf6,0x38,0xc4,0x8b,0xda,0xc8, - 0x0c,0x05,0xc3,0x18,0x31,0x38,0x00,0x10,0x04,0x83,0x09,0x6e,0x70,0x06,0x64,0x5e, - 0x66,0x34,0x21,0x00,0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0x61,0xd8, - 0x80,0x58,0x88,0x01,0x18,0x36,0x20,0x94,0x61,0x00,0x86,0x0d,0x88,0x44,0x18,0x80, - 0x61,0x03,0x02,0x09,0x06,0x80,0x8e,0x61,0x8c,0x18,0x1c,0x00,0x08,0x82,0xc1,0x64, - 0x37,0x3e,0x63,0x32,0x2e,0x33,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08, - 0xa3,0x09,0xc4,0x30,0x6c,0x40,0x44,0xc4,0x00,0x0c,0x1b,0x10,0xd0,0x30,0x00,0xc3, - 0x06,0xc4,0x23,0x0c,0xc0,0xb0,0x01,0xe1,0x04,0x03,0x40,0xcd,0x30,0xf6,0x38,0xf8, - 0x0b,0xde,0xe0,0xcd,0xb0,0x01,0x11,0x5c,0x03,0xb0,0xc7,0xe1,0x5f,0xf2,0x46,0x6f, - 0x86,0x0d,0x88,0xe0,0x1a,0x80,0x3d,0x0e,0x20,0xa3,0x37,0x73,0x33,0x6c,0x40,0x04, - 0xd7,0x00,0xec,0x71,0x08,0x99,0xbd,0xb1,0x9b,0x61,0x03,0x22,0xb8,0x06,0x60,0x8f, - 0x83,0xc8,0xf0,0x4d,0xd9,0x50,0x30,0x8c,0x61,0x03,0xc2,0x88,0x06,0x60,0xd8,0x80, - 0x28,0xa0,0x01,0x18,0x36,0x20,0x88,0x67,0x00,0x86,0x0d,0x88,0xc1,0x19,0x00,0x12, - 0x86,0x31,0x6c,0x40,0x1c,0xcc,0x00,0x0c,0x1b,0x10,0xc6,0x32,0x00,0xc3,0x06,0x44, - 0xa1,0x0c,0xc0,0xb0,0x01,0x41,0x24,0x03,0x40,0xc3,0x30,0xf6,0x38,0x94,0x0c,0xe8, - 0x80,0xce,0xb0,0x01,0x11,0x7c,0x03,0xb0,0xc7,0xc1,0x64,0x42,0x47,0x74,0x86,0x0d, - 0x88,0xe0,0x1b,0x80,0x3d,0x0e,0x27,0x23,0x3a,0x7b,0x33,0x6c,0x40,0x04,0xdf,0x00, - 0xec,0x71,0x40,0x99,0xd1,0xf1,0x9b,0x61,0x03,0x22,0xf8,0x06,0x60,0x8f,0x43,0xca, - 0x90,0xce,0xda,0x50,0x30,0x8c,0x61,0x03,0xc2,0xc8,0x06,0x60,0xd8,0x80,0x28,0xb0, - 0x01,0x18,0x36,0x20,0x88,0x6b,0x00,0x86,0x0d,0x88,0xc1,0x1a,0x00,0x12,0x86,0x31, - 0x6c,0x40,0x1c,0xd4,0x00,0x0c,0x1b,0x10,0xc6,0x34,0x00,0xc3,0x06,0x44,0x21,0x0d, - 0xc0,0xb0,0x01,0x41,0x44,0x03,0x40,0xc3,0x30,0xac,0x53,0x03,0xf9,0x8c,0x18,0x20, - 0x00,0x08,0x82,0x41,0xc5,0x3a,0x76,0x83,0xa9,0x41,0x30,0x62,0x80,0x00,0x20,0x08, - 0x06,0x55,0xeb,0xdc,0xcd,0xa4,0x06,0x81,0x39,0x6d,0x20,0x9f,0x11,0x03,0x04,0x00, - 0x41,0x30,0xa8,0x5e,0x27,0x6f,0x9c,0x36,0x08,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0, - 0x82,0x1d,0xbd,0x71,0xda,0x20,0xb0,0x04,0x0e,0xe4,0x33,0x62,0x80,0x00,0x20,0x08, - 0x06,0x95,0xec,0xf0,0x4d,0x02,0x07,0xc1,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xb3, - 0xd3,0x37,0x09,0x1c,0x04,0x23,0x06,0x0b,0x00,0x82,0x60,0xc0,0xdc,0x0e,0xdb,0x1c, - 0x44,0x70,0x10,0xc1,0x88,0x81,0x01,0x80,0x20,0x18,0x38,0xb7,0xc3,0x36,0x81,0x05, - 0x89,0x7c,0x4c,0x38,0xe4,0x63,0x43,0x21,0x9f,0x11,0x83,0x03,0x00,0x41,0x30,0x98, - 0x74,0x47,0x74,0xd4,0xc6,0x76,0x46,0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06, - 0x61,0x34,0x81,0x18,0x46,0x0c,0x0e,0x00,0x04,0xc1,0x60,0xfa,0x9d,0xd3,0x79,0x1b, - 0xd2,0x19,0x4d,0x08,0x80,0xd1,0x04,0x21,0x18,0x4d,0x18,0x84,0xd1,0x04,0x62,0x18, - 0x31,0x38,0x00,0x10,0x04,0x83,0x89,0x7c,0x58,0x87,0x6e,0x44,0x67,0x34,0x21,0x00, - 0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x13,0x88,0x61,0xc4,0xe0,0x00,0x40,0x10, - 0x0c,0xa6,0xf4,0x89,0x9d,0xbc,0xf1,0x9b,0xd1,0x84,0x00,0x18,0x4d,0x10,0x82,0xd1, - 0x84,0x41,0x18,0x4d,0x20,0x06,0x9b,0xcc,0x43,0x3e,0x23,0x06,0x08,0x00,0x82,0x60, - 0x50,0xb9,0x0f,0xee,0x3c,0xe6,0x11,0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0xf5,0x3e, - 0xb9,0xb3,0x98,0x47,0x30,0x62,0x80,0x00,0x20,0x08,0x06,0x15,0xfc,0xe8,0xce,0x61, - 0x1e,0x81,0x59,0xea,0x21,0x9f,0x11,0x03,0x04,0x00,0x41,0x30,0xa8,0xe4,0x87,0x77, - 0x24,0xf5,0x08,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0x9a,0x9f,0xde,0x71,0xd4,0x23, - 0x18,0x31,0x40,0x00,0x10,0x04,0x83,0x8a,0x7e,0x7c,0x47,0x51,0x8f,0xc0,0x32,0xf7, - 0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0xf6,0x03,0x3e,0x95,0x7b,0x04,0x23, - 0x06,0x08,0x00,0x82,0x60,0x50,0xdd,0x4f,0xf8,0x44,0xee,0x11,0x8c,0x18,0x20,0x00, - 0x08,0x82,0x41,0x85,0x3f,0xe2,0xd3,0xb8,0x47,0x60,0x9c,0x7c,0xc8,0x67,0xc4,0x00, - 0x01,0x40,0x10,0x0c,0x2a,0xfd,0x21,0x1f,0x4c,0x3e,0x82,0x11,0x03,0x04,0x00,0x41, - 0x30,0xa8,0xf6,0xa7,0x7c,0x28,0xf9,0x08,0x46,0x0c,0x10,0x00,0x04,0xc1,0xa0,0xe2, - 0x1f,0xf3,0x81,0xe4,0x23,0x18,0x31,0x38,0x00,0x10,0x04,0x83,0xc9,0x7f,0xcc,0xc7, - 0x75,0x76,0x67,0x34,0x21,0x00,0x46,0x13,0x84,0x60,0x34,0x61,0x10,0x46,0x0c,0x0e, - 0x00,0x04,0xc1,0x60,0x12,0x21,0xf5,0x91,0x1d,0xdf,0x19,0x4d,0x08,0x80,0xd1,0x04, - 0x21,0x18,0x4d,0x18,0x84,0x11,0x83,0x03,0x00,0x41,0x30,0x98,0x4c,0xc8,0x7d,0x6c, - 0x27,0x7c,0x46,0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06,0x61,0xc4,0xe0,0x00, - 0x40,0x10,0x0c,0x26,0x15,0x92,0x1f,0xdd,0xf1,0x9d,0xd1,0x84,0x00,0x18,0x4d,0x10, - 0x82,0xd1,0x84,0x41,0xb0,0xc7,0x44,0xe4,0x33,0x62,0x80,0x00,0x20,0x08,0x06,0x95, - 0x0b,0xe1,0x0f,0x63,0x22,0xc1,0x88,0x01,0x02,0x80,0x20,0x18,0x54,0x2f,0x94,0x3f, - 0x89,0x89,0x04,0x23,0x06,0x08,0x00,0x82,0x60,0x50,0xc1,0x90,0xfe,0x18,0x26,0x12, - 0x98,0xa4,0x22,0xf2,0x19,0x31,0x40,0x00,0x10,0x04,0x83,0x4a,0x86,0xf8,0xe7,0x51, - 0x91,0x60,0xc4,0x00,0x01,0x40,0x10,0x0c,0xaa,0x19,0xea,0x1f,0x46,0x45,0x82,0x11, - 0x03,0x04,0x00,0x41,0x30,0xa8,0x68,0xc8,0x7f,0x12,0x15,0x09,0xac,0x72,0x11,0xf9, - 0x8c,0x18,0x20,0x00,0x08,0x82,0x41,0x65,0x43,0x20,0x24,0xb9,0x48,0x30,0x62,0x80, - 0x00,0x20,0x08,0x06,0xd5,0x0d,0x85,0xd0,0xe3,0x22,0xc1,0x88,0x01,0x02,0x80,0x20, - 0x18,0x54,0x38,0x24,0x42,0x8c,0x8b,0x04,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4, - 0x10,0x08,0xd9,0x90,0x0d,0xad,0x50,0x32,0x62,0x90,0x00,0x20,0x08,0x06,0x4a,0x0f, - 0x81,0x90,0x0d,0xd9,0x50,0xfb,0x14,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4,0x10, - 0x08,0xd9,0x90,0x0d,0xa5,0x50,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x4a,0x0f,0x81, - 0xd0,0x0d,0xd9,0xd0,0x0a,0x8d,0xc2,0x88,0x41,0x02,0x80,0x20,0x18,0x28,0x3d,0x04, - 0x42,0x37,0x64,0x43,0xed,0x23,0x0a,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4,0x10, - 0x08,0xdd,0x90,0x0d,0xa5,0x50,0x28,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0xd2,0x43, - 0x20,0xf4,0x42,0x36,0xb4,0x42,0xb2,0x33,0x62,0x90,0x00,0x20,0x08,0x06,0x4a,0x0f, - 0x81,0xd0,0x0b,0xd9,0x50,0xfb,0xc4,0xce,0x88,0x41,0x02,0x80,0x20,0x18,0x28,0x3d, - 0x04,0x42,0x31,0x64,0x43,0x2b,0x94,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0xa0,0xf4, - 0x10,0x08,0xc5,0x90,0x0d,0xb5,0x4f,0x19,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0xd2, - 0x43,0x20,0x14,0x43,0x36,0x94,0x42,0x61,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x4a, - 0x0f,0x81,0x50,0x0c,0xd9,0x90,0x0a,0x75,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -}; - -static const unsigned char _vertexSkinnedMSL[] = { - 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, - 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, - 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, - 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, - 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x78,0x34,0x20,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, - 0x69,0x78,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x31,0x32,0x38,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, - 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x4c,0x69,0x67,0x68,0x74,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, - 0x67,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, - 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x20,0x63,0x6f,0x6e,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, - 0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x34,0x20,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, - 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x65,0x6d,0x69,0x73,0x73,0x69, - 0x76,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6d, - 0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x63,0x6f,0x75,0x6e,0x74,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72, - 0x61,0x6d,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x38, - 0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x38,0x5d,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x4c,0x69,0x67,0x68,0x74,0x20,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x38,0x5d, - 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72, - 0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x6f,0x75,0x74,0x0a,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b, - 0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x75,0x73, - 0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72, - 0x28,0x6c,0x6f,0x63,0x6e,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, - 0x6e,0x20,0x5b,0x5b,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a, - 0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x72,0x74,0x65, - 0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72, - 0x69,0x62,0x75,0x74,0x65,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62, - 0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x32,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, - 0x65,0x28,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x69,0x6e,0x74, - 0x34,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x33,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x33, - 0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, - 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34, - 0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x34,0x29,0x5d, - 0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x76,0x65, - 0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x6f,0x75,0x74,0x20, - 0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x28,0x76,0x65, - 0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e,0x6e,0x65,0x64,0x5f,0x69,0x6e,0x20,0x69, - 0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20, - 0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x44,0x72, - 0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x26,0x20,0x44,0x72,0x61,0x77, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65, - 0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74, - 0x20,0x74,0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x26,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x31,0x29,0x5d,0x5d,0x29,0x0a, - 0x7b,0x0a,0x20,0x20,0x20,0x20,0x76,0x65,0x72,0x74,0x65,0x78,0x53,0x6b,0x69,0x6e, - 0x6e,0x65,0x64,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x5f,0x37,0x31, - 0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x2c,0x20,0x31, - 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, - 0x5f,0x31,0x30,0x36,0x20,0x3d,0x20,0x28,0x28,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69, - 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x33,0x2e,0x78,0x5d,0x20,0x2a,0x20,0x5f,0x37,0x31,0x29,0x20,0x2a,0x20,0x69, - 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x34,0x2e,0x78,0x29,0x20,0x2b,0x20,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x79,0x5d,0x20,0x2a,0x20,0x5f,0x37,0x31,0x29,0x20,0x2a,0x20,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x34,0x2e,0x79,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x7a,0x5d,0x20,0x2a,0x20,0x5f,0x37,0x31,0x29,0x20,0x2a,0x20,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x34,0x2e,0x7a,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x77,0x5d,0x20,0x2a,0x20,0x5f,0x37,0x31,0x29,0x20,0x2a,0x20,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x34,0x2e,0x77,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33, - 0x20,0x5f,0x31,0x36,0x33,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f, - 0x72,0x6d,0x61,0x6c,0x69,0x7a,0x65,0x28,0x28,0x28,0x28,0x28,0x28,0x69,0x6e,0x2e, - 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31, - 0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61, - 0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, - 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, - 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x30,0x5d, - 0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76, - 0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b, - 0x31,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e, - 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78, - 0x5d,0x5b,0x32,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, - 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, - 0x2e,0x78,0x5d,0x5b,0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e, - 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78, - 0x5d,0x5b,0x30,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e, - 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33, - 0x2e,0x78,0x5d,0x5b,0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69, - 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x33,0x2e,0x78,0x5d,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69, - 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73, - 0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78, - 0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69, - 0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x33,0x2e,0x78,0x5d,0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69, - 0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73, - 0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53, - 0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e, - 0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58, - 0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c, - 0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f, - 0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54, - 0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x78,0x5d,0x5b,0x33,0x5d,0x5b,0x32, - 0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x69,0x6e, - 0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34,0x2e,0x78, - 0x29,0x20,0x2b,0x20,0x28,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b, - 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x30,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b, - 0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74, - 0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x31,0x5d,0x5b,0x30,0x5d,0x2c,0x20, - 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69, - 0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x32,0x5d,0x5b,0x30,0x5d, - 0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a, - 0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x33,0x5d,0x5b, - 0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28, - 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69, - 0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x30,0x5d,0x5b,0x31,0x5d, - 0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a, - 0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x31,0x5d,0x5b, - 0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x32, - 0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d, - 0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d,0x5b,0x30, - 0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x79,0x5d, - 0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69, - 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e, - 0x79,0x5d,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x79,0x5d,0x5b,0x33,0x5d,0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29, - 0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x34,0x2e,0x79,0x29,0x29,0x20,0x2b,0x20,0x28,0x28, - 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x31,0x20,0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66, - 0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d, - 0x5b,0x30,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69, - 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e, - 0x7a,0x5d,0x5b,0x31,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x7a,0x5d,0x5b,0x32,0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b, - 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79, - 0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x7a,0x5d,0x5b,0x30,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b, - 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b, - 0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74, - 0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20, - 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69, - 0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x33,0x5d,0x5b,0x31,0x5d, - 0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b, - 0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74, - 0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20, - 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69, - 0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x31,0x5d,0x5b,0x32,0x5d, - 0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a, - 0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x32,0x5d,0x5b, - 0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x7a,0x5d,0x5b,0x33, - 0x5d,0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29,0x29,0x20,0x2a,0x20,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x34,0x2e,0x7a,0x29,0x29,0x20,0x2b,0x20,0x28,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x20,0x2a,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28, - 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69, - 0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45, - 0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x30,0x5d,0x5b,0x30,0x5d, - 0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a, - 0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x31,0x5d,0x5b, - 0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x32, - 0x5d,0x5b,0x30,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d, - 0x5b,0x33,0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x30, - 0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d, - 0x5b,0x31,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69, - 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e, - 0x77,0x5d,0x5b,0x32,0x5d,0x5b,0x31,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x77,0x5d,0x5b,0x33,0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e,0x2e,0x69, - 0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x33,0x2e, - 0x77,0x5d,0x5b,0x30,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x69,0x6e, - 0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44, - 0x33,0x2e,0x77,0x5d,0x5b,0x31,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b,0x69,0x6e, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b, - 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x32,0x5d,0x5b,0x32,0x5d,0x2c,0x20,0x53,0x6b, - 0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6a,0x6f,0x69,0x6e,0x74, - 0x73,0x5b,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x33,0x2e,0x77,0x5d,0x5b,0x33,0x5d,0x5b,0x32,0x5d,0x29,0x2e, - 0x78,0x79,0x7a,0x29,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x34,0x2e,0x77,0x29,0x29,0x20, - 0x2a,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x78,0x33,0x28,0x66,0x6c,0x6f,0x61,0x74, - 0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e, - 0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x30,0x5d,0x5b,0x30, - 0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, - 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x31,0x5d,0x5b, - 0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x32,0x5d, - 0x5b,0x30,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x33, - 0x5d,0x5b,0x30,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e, - 0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x30,0x5d,0x5b,0x31, - 0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, - 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x31,0x5d,0x5b, - 0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x32,0x5d, - 0x5b,0x31,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x33, - 0x5d,0x5b,0x31,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x34,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6e, - 0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x30,0x5d,0x5b,0x32, - 0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, - 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x31,0x5d,0x5b, - 0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x32,0x5d, - 0x5b,0x32,0x5d,0x2c,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x33, - 0x5d,0x5b,0x32,0x5d,0x29,0x2e,0x78,0x79,0x7a,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, - 0x20,0x3d,0x20,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, - 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, - 0x69,0x6f,0x6e,0x20,0x2a,0x20,0x5f,0x31,0x30,0x36,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x6f,0x75,0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x30,0x20,0x3d,0x20,0x28,0x44,0x72,0x61,0x77,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x6f,0x64,0x65,0x6c,0x20,0x2a,0x20,0x5f,0x31, - 0x30,0x36,0x29,0x2e,0x78,0x79,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, - 0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x31,0x20,0x3d,0x20,0x5f,0x31,0x36,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6f, - 0x75,0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x32,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, - 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a, -}; - -static const SceneShaderT sceneShaderVertexSkinned = { "vertexSkinned", _vertexSkinnedSPIRV, sizeof(_vertexSkinnedSPIRV), _vertexSkinnedDXIL, sizeof(_vertexSkinnedDXIL), _vertexSkinnedMSL, sizeof(_vertexSkinnedMSL) }; - -static const unsigned char _fragmentMainSPIRV[] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0x31,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, - 0x01,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0f,0x00,0x0b,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x66,0x72,0x61,0x67, - 0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x10,0x00,0x03,0x00, - 0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x05,0x00,0x00,0x00, - 0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x74,0x79,0x70,0x65, - 0x2e,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, - 0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74,0x69,0x6f,0x6e,0x00, - 0x06,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x6f,0x64,0x65, - 0x6c,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00, - 0x05,0x00,0x06,0x00,0x08,0x00,0x00,0x00,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x09,0x00,0x00,0x00, - 0x74,0x79,0x70,0x65,0x2e,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x0a,0x00,0x00,0x00, - 0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00, - 0x05,0x00,0x08,0x00,0x0b,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00, - 0x06,0x00,0x07,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x61,0x6d,0x65, - 0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x06,0x00,0x05,0x00, - 0x0b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x00, - 0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x62,0x61,0x73,0x65, - 0x43,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x65,0x6d,0x69,0x73,0x73,0x69,0x76,0x65,0x00,0x00,0x00,0x00, - 0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6d,0x61,0x74,0x65, - 0x72,0x69,0x61,0x6c,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x63,0x6f,0x75,0x6e,0x74,0x73,0x00,0x00,0x06,0x00,0x07,0x00, - 0x0b,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61, - 0x72,0x61,0x6d,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0b,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x74,0x72,0x69,0x78, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0b,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x00,0x00,0x06,0x00,0x05,0x00, - 0x0b,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x6c,0x69,0x67,0x68,0x74,0x73,0x00,0x00, - 0x05,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x4c,0x69,0x67,0x68,0x74,0x00,0x00,0x00, - 0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x6f,0x73,0x69, - 0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00, - 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f, - 0x6e,0x52,0x61,0x6e,0x67,0x65,0x00,0x00,0x06,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x63,0x6f,0x6e,0x65,0x00,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x0d,0x00,0x00,0x00,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0e,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67, - 0x65,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0f,0x00,0x00,0x00,0x62,0x61,0x73,0x65, - 0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00,0x05,0x00,0x06,0x00,0x10,0x00,0x00,0x00, - 0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x00, - 0x05,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x00,0x05,0x00,0x07,0x00,0x12,0x00,0x00,0x00,0x74,0x79,0x70,0x65, - 0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67,0x65,0x2e,0x61,0x72,0x72,0x61,0x79,0x00, - 0x05,0x00,0x05,0x00,0x13,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61, - 0x70,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x14,0x00,0x00,0x00,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x00,0x00,0x05,0x00,0x07,0x00, - 0x03,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x30,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x04,0x00,0x00,0x00, - 0x69,0x6e,0x2e,0x76,0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31, - 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x05,0x00,0x00,0x00,0x69,0x6e,0x2e,0x76, - 0x61,0x72,0x2e,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x00,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x6f,0x75,0x74,0x2e,0x76,0x61,0x72,0x2e, - 0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x02,0x00,0x00,0x00,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x15,0x00,0x00,0x00,0x74,0x79,0x70,0x65, - 0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x64,0x2e,0x69,0x6d,0x61,0x67,0x65,0x00,0x00, - 0x05,0x00,0x07,0x00,0x16,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x64,0x2e,0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x47,0x00,0x04,0x00, - 0x03,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x04,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x05,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0a,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0a,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0d,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x80,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x09,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x09,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, - 0x05,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0b,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x60,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x70,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x0b,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0b,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x48,0x00,0x05,0x00, - 0x0b,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0xf0,0x02,0x00,0x00, - 0x47,0x00,0x03,0x00,0x0b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x1b,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x1b,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x1b,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x1b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x1b,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x1b,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00, - 0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x9a,0x99,0x19,0x3f,0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x0a,0xd7,0x23,0x3d,0x17,0x00,0x04,0x00,0x26,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2c,0x00,0x06,0x00,0x26,0x00,0x00,0x00, - 0x27,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x40, - 0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x00,0x00,0x80,0x43, - 0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x00,0x00,0x80,0x40, - 0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x17,0xb7,0xd1,0x38, - 0x2b,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x41, - 0x14,0x00,0x02,0x00,0x32,0x00,0x00,0x00,0x2a,0x00,0x03,0x00,0x32,0x00,0x00,0x00, - 0x33,0x00,0x00,0x00,0x2c,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0x34,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x2b,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0xa0,0x40,0x2b,0x00,0x04,0x00, - 0x1b,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x29,0x00,0x03,0x00, - 0x32,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x39,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x3a,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x3a,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x3b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x3c,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x3c,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x1c,0x00,0x04,0x00, - 0x17,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x3d,0x00,0x00,0x00,0x1e,0x00,0x03,0x00, - 0x09,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x3e,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x3c,0x00,0x00,0x00, - 0x3f,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x18,0x00,0x00,0x00, - 0x3a,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x19,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x3f,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00, - 0x1c,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3f,0x00,0x00,0x00, - 0x1e,0x00,0x0c,0x00,0x0b,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x39,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x19,0x00,0x09,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0e,0x00,0x00,0x00,0x1a,0x00,0x02,0x00,0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x19,0x00,0x09,0x00, - 0x12,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x44,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x26,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x45,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x46,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x47,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x39,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x48,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x49,0x00,0x00,0x00, - 0x48,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x4a,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x4b,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x15,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, - 0x1b,0x00,0x03,0x00,0x16,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x4c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3a,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x3b,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x3e,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x40,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x41,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x42,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x43,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x42,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x44,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x44,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x46,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x47,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x3c,0x00,0x00,0x00,0x4d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x00,0x00,0x80,0xbf,0x2b,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x4f,0x00,0x00,0x00,0x39,0x8e,0xe3,0x3d,0x2b,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0xcd,0xcc,0x4c,0x3e,0x36,0x00,0x05,0x00, - 0x48,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x51,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x26,0x00,0x00,0x00, - 0x52,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x26,0x00,0x00,0x00, - 0x53,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x45,0x00,0x00,0x00, - 0x54,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x55,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xfb,0x00,0x03,0x00,0x4d,0x00,0x00,0x00,0x56,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x56,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x4a,0x00,0x00,0x00, - 0x57,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x39,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x41,0x00,0x06,0x00, - 0x4b,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x5a,0x00,0x00,0x00, - 0x59,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x4b,0x00,0x00,0x00,0x5b,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x5b,0x00,0x00,0x00,0x41,0x00,0x06,0x00, - 0x4b,0x00,0x00,0x00,0x5d,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x5e,0x00,0x00,0x00, - 0x5d,0x00,0x00,0x00,0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x5f,0x00,0x00,0x00, - 0x5e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x60,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0x5f,0x00,0x00,0x00,0x61,0x00,0x00,0x00, - 0x60,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x61,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x0e,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x56,0x00,0x05,0x00, - 0x15,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x63,0x00,0x00,0x00, - 0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x64,0x00,0x00,0x00, - 0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x39,0x00,0x00,0x00, - 0x66,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, - 0x60,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x60,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x39,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x56,0x00,0x00,0x00, - 0x66,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x4b,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x68,0x00,0x00,0x00, - 0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x6a,0x00,0x00,0x00,0x69,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x6b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfa,0x00,0x04,0x00,0x6a,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x6b,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x6c,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x55,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x6b,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x26,0x00,0x00,0x00, - 0x6d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x53,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x4a,0x00,0x00,0x00,0x6e,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x6f,0x00,0x00,0x00, - 0x6e,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0x70,0x00,0x00,0x00, - 0x6f,0x00,0x00,0x00,0x6f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x71,0x00,0x00,0x00, - 0x70,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x26,0x00,0x00,0x00, - 0x72,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x71,0x00,0x00,0x00, - 0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x67,0x00,0x00,0x00, - 0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x5a,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x75,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x26,0x00,0x00,0x00, - 0x76,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0x5a,0x00,0x00,0x00, - 0x5a,0x00,0x00,0x00,0x0c,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0x78,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x73,0x00,0x00,0x00, - 0x77,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x79,0x00,0x00,0x00, - 0x28,0x00,0x00,0x00,0x5a,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x7a,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, - 0x26,0x00,0x00,0x00,0x7b,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x7a,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x7c,0x00,0x00,0x00,0x5c,0x00,0x00,0x00, - 0x5c,0x00,0x00,0x00,0x0c,0x00,0x08,0x00,0x21,0x00,0x00,0x00,0x7d,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x2a,0x00,0x00,0x00, - 0x7c,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x4a,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00, - 0x7f,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00, - 0x80,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x26,0x00,0x00,0x00, - 0x81,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x8e,0x00,0x05,0x00, - 0x26,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x7a,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x4a,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x84,0x00,0x00,0x00, - 0x83,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0x85,0x00,0x00,0x00, - 0x84,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x86,0x00,0x00,0x00, - 0x82,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x4b,0x00,0x00,0x00, - 0x87,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x87,0x00,0x00,0x00, - 0x6e,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x88,0x00,0x00,0x00, - 0xf9,0x00,0x02,0x00,0x8a,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x8a,0x00,0x00,0x00, - 0xf5,0x00,0x07,0x00,0x26,0x00,0x00,0x00,0x8b,0x00,0x00,0x00,0x86,0x00,0x00,0x00, - 0x6b,0x00,0x00,0x00,0x8c,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x1b,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x6b,0x00,0x00,0x00, - 0x8f,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0xb1,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x90,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0xf6,0x00,0x04,0x00, - 0x91,0x00,0x00,0x00,0x8d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00, - 0x90,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0x92,0x00,0x00,0x00,0xaf,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x93,0x00,0x00,0x00, - 0x8e,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x94,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0x93,0x00,0x00,0x00,0x95,0x00,0x00,0x00, - 0x94,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x95,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, - 0x8d,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x94,0x00,0x00,0x00,0x41,0x00,0x08,0x00, - 0x4b,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00, - 0x8e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0xb4,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0xf7,0x00,0x03,0x00,0x99,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00, - 0x98,0x00,0x00,0x00,0x9a,0x00,0x00,0x00,0x9b,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0x9a,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x4a,0x00,0x00,0x00,0x9c,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x9d,0x00,0x00,0x00,0x9c,0x00,0x00,0x00, - 0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0x9e,0x00,0x00,0x00,0x9d,0x00,0x00,0x00, - 0x9d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x7f,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x9f,0x00,0x00,0x00,0x9e,0x00,0x00,0x00, - 0x0c,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x45,0x00,0x00,0x00,0x9f,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x99,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x9b,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x4a,0x00,0x00,0x00, - 0xa1,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0xa2,0x00,0x00,0x00, - 0xa1,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0xa3,0x00,0x00,0x00, - 0xa2,0x00,0x00,0x00,0xa2,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0xa4,0x00,0x00,0x00, - 0xa3,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0xa5,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0xa4,0x00,0x00,0x00, - 0x0c,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0xa6,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x00,0x00,0xa5,0x00,0x00,0x00,0x2f,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0xa7,0x00,0x00,0x00,0xa6,0x00,0x00,0x00,0xa6,0x00,0x00,0x00, - 0xa6,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0xa8,0x00,0x00,0x00, - 0xa4,0x00,0x00,0x00,0xa7,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xa9,0x00,0x00,0x00,0xa5,0x00,0x00,0x00,0xa5,0x00,0x00,0x00,0x81,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0xaa,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0xa9,0x00,0x00,0x00, - 0x88,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xab,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0xaa,0x00,0x00,0x00,0x41,0x00,0x08,0x00,0x4b,0x00,0x00,0x00,0xac,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0xad,0x00,0x00,0x00, - 0xac,0x00,0x00,0x00,0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xae,0x00,0x00,0x00, - 0xad,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0xaf,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0xae,0x00,0x00,0x00,0xb0,0x00,0x00,0x00, - 0xaf,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xb0,0x00,0x00,0x00,0x88,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0xb1,0x00,0x00,0x00,0xa5,0x00,0x00,0x00,0xad,0x00,0x00,0x00, - 0x0c,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0xb2,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x1a,0x00,0x00,0x00,0xb1,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x83,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0xb3,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0xb2,0x00,0x00,0x00, - 0x0c,0x00,0x08,0x00,0x21,0x00,0x00,0x00,0xb4,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2b,0x00,0x00,0x00,0xb3,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xb5,0x00,0x00,0x00,0xab,0x00,0x00,0x00, - 0xb4,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0xaf,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0xaf,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0xb6,0x00,0x00,0x00, - 0xab,0x00,0x00,0x00,0x9b,0x00,0x00,0x00,0xb5,0x00,0x00,0x00,0xb0,0x00,0x00,0x00, - 0xb4,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xb7,0x00,0x00,0x00,0x97,0x00,0x00,0x00, - 0x28,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfa,0x00,0x04,0x00,0xb7,0x00,0x00,0x00,0xb9,0x00,0x00,0x00,0xb8,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0xb9,0x00,0x00,0x00,0x7f,0x00,0x04,0x00,0x26,0x00,0x00,0x00, - 0xba,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x4a,0x00,0x00,0x00, - 0xbb,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0xbc,0x00,0x00,0x00, - 0xbb,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0xbd,0x00,0x00,0x00, - 0xbc,0x00,0x00,0x00,0xbc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x0c,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0xbe,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0xbd,0x00,0x00,0x00,0x94,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0xba,0x00,0x00,0x00,0xbe,0x00,0x00,0x00, - 0x41,0x00,0x08,0x00,0x4b,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0xc1,0x00,0x00,0x00,0xc0,0x00,0x00,0x00, - 0x41,0x00,0x08,0x00,0x4b,0x00,0x00,0x00,0xc2,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0xc3,0x00,0x00,0x00,0xc2,0x00,0x00,0x00, - 0x0c,0x00,0x08,0x00,0x21,0x00,0x00,0x00,0xc4,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x31,0x00,0x00,0x00,0xc1,0x00,0x00,0x00,0xc3,0x00,0x00,0x00,0xbf,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xc5,0x00,0x00,0x00,0xb6,0x00,0x00,0x00, - 0xc4,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0xb8,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0xb8,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0xc6,0x00,0x00,0x00, - 0xb6,0x00,0x00,0x00,0xaf,0x00,0x00,0x00,0xc5,0x00,0x00,0x00,0xb9,0x00,0x00,0x00, - 0xf9,0x00,0x02,0x00,0x99,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x99,0x00,0x00,0x00, - 0xf5,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0xc7,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x9a,0x00,0x00,0x00,0xc6,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x26,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0xa0,0x00,0x00,0x00,0x9a,0x00,0x00,0x00, - 0xa8,0x00,0x00,0x00,0xb8,0x00,0x00,0x00,0x41,0x00,0x08,0x00,0x4b,0x00,0x00,0x00, - 0xc9,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0xca,0x00,0x00,0x00,0xc9,0x00,0x00,0x00,0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0xcb,0x00,0x00,0x00,0xca,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0xf7,0x00,0x03,0x00, - 0xcc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0xcb,0x00,0x00,0x00, - 0xcd,0x00,0x00,0x00,0xcc,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xcd,0x00,0x00,0x00, - 0x6e,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0xce,0x00,0x00,0x00,0xca,0x00,0x00,0x00, - 0x82,0x00,0x05,0x00,0x1b,0x00,0x00,0x00,0xcf,0x00,0x00,0x00,0xce,0x00,0x00,0x00, - 0x1f,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x4b,0x00,0x00,0x00,0xd0,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0xcf,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0xd1,0x00,0x00,0x00,0xd0,0x00,0x00,0x00, - 0xb4,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xd2,0x00,0x00,0x00,0xd1,0x00,0x00,0x00, - 0x28,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0xd3,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfa,0x00,0x04,0x00,0xd2,0x00,0x00,0x00,0xd4,0x00,0x00,0x00,0xd5,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0xd4,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x4a,0x00,0x00,0x00, - 0xd6,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00, - 0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0xd7,0x00,0x00,0x00, - 0xd6,0x00,0x00,0x00,0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0xd8,0x00,0x00,0x00, - 0xd7,0x00,0x00,0x00,0xd7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0xd9,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfb,0x00,0x03,0x00,0x4d,0x00,0x00,0x00,0xda,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0xda,0x00,0x00,0x00,0x41,0x00,0x07,0x00,0x4b,0x00,0x00,0x00,0xdb,0x00,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0xcf,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0xdc,0x00,0x00,0x00,0xdb,0x00,0x00,0x00, - 0x41,0x00,0x07,0x00,0x4b,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x2c,0x00,0x00,0x00,0xcf,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0xde,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x41,0x00,0x07,0x00, - 0x4b,0x00,0x00,0x00,0xdf,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x2c,0x00,0x00,0x00, - 0xcf,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0xe0,0x00,0x00,0x00,0xdf,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x4b,0x00,0x00,0x00, - 0xe1,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0xe2,0x00,0x00,0x00,0xe1,0x00,0x00,0x00, - 0x8e,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0xe3,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, - 0xe2,0x00,0x00,0x00,0x8e,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0xe4,0x00,0x00,0x00, - 0xe3,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x26,0x00,0x00,0x00, - 0xe5,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0xe4,0x00,0x00,0x00,0x83,0x00,0x05,0x00, - 0x26,0x00,0x00,0x00,0xe6,0x00,0x00,0x00,0xe5,0x00,0x00,0x00,0xd8,0x00,0x00,0x00, - 0x0c,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0xe7,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0xe6,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x4b,0x00,0x00,0x00, - 0xe8,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0xe9,0x00,0x00,0x00,0xe8,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xea,0x00,0x00,0x00,0xe7,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xeb,0x00,0x00,0x00, - 0xe7,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0xec,0x00,0x00,0x00,0xea,0x00,0x00,0x00,0xeb,0x00,0x00,0x00,0xf7,0x00,0x03,0x00, - 0xed,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0xec,0x00,0x00,0x00, - 0xee,0x00,0x00,0x00,0xed,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xee,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xef,0x00,0x00,0x00,0xe7,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xf0,0x00,0x00,0x00, - 0xea,0x00,0x00,0x00,0xef,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0xed,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0xed,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x32,0x00,0x00,0x00, - 0xf1,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0xda,0x00,0x00,0x00,0xf0,0x00,0x00,0x00, - 0xee,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0xf2,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfa,0x00,0x04,0x00,0xf1,0x00,0x00,0x00,0xf3,0x00,0x00,0x00,0xf4,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0xf3,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xf5,0x00,0x00,0x00,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xba,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0xf6,0x00,0x00,0x00,0xf5,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0xf7,0x00,0x00,0x00,0xf6,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0xf8,0x00,0x00,0x00,0xf6,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x4e,0x00,0x00,0x00, - 0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0xf9,0x00,0x00,0x00,0xf8,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0xf2,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0xf4,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xfa,0x00,0x00,0x00,0xe7,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xbe,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0xfb,0x00,0x00,0x00,0xeb,0x00,0x00,0x00,0xfa,0x00,0x00,0x00, - 0xf7,0x00,0x03,0x00,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00, - 0xfb,0x00,0x00,0x00,0xfd,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0xfd,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xff,0x00,0x00,0x00, - 0xe6,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x00,0x01,0x00,0x00,0xff,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0xa9,0x00,0x06,0x00, - 0x21,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x28,0x00,0x00,0x00, - 0x35,0x00,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0x02,0x01,0x00,0x00, - 0x00,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0x03,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0x02,0x01,0x00,0x00, - 0x2e,0x00,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0x04,0x01,0x00,0x00, - 0x00,0x01,0x00,0x00,0x4e,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0x05,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x04,0x01,0x00,0x00,0xf9,0x00,0x02,0x00,0xfc,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0xfe,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x06,0x01,0x00,0x00, - 0xe6,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x07,0x01,0x00,0x00,0x06,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0xa9,0x00,0x06,0x00, - 0x21,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x07,0x01,0x00,0x00,0x2a,0x00,0x00,0x00, - 0x36,0x00,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0x09,0x01,0x00,0x00, - 0x07,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x4e,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0x0a,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x09,0x01,0x00,0x00,0xf9,0x00,0x02,0x00,0xfc,0x00,0x00,0x00,0xf8,0x00,0x02,0x00, - 0xfc,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0x0b,0x01,0x00,0x00, - 0x01,0x01,0x00,0x00,0xfd,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0xfe,0x00,0x00,0x00, - 0xf5,0x00,0x07,0x00,0x26,0x00,0x00,0x00,0x0c,0x01,0x00,0x00,0x05,0x01,0x00,0x00, - 0xfd,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x26,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0x03,0x01,0x00,0x00,0xfd,0x00,0x00,0x00, - 0x0a,0x01,0x00,0x00,0xfe,0x00,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0x0e,0x01,0x00,0x00,0xfb,0x00,0x00,0x00,0xeb,0x00,0x00,0x00,0xfa,0x00,0x00,0x00, - 0xf9,0x00,0x02,0x00,0xf2,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xf2,0x00,0x00,0x00, - 0xf5,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0x0f,0x01,0x00,0x00,0xf7,0x00,0x00,0x00, - 0xf3,0x00,0x00,0x00,0x0b,0x01,0x00,0x00,0xfc,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x21,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0xea,0x00,0x00,0x00,0xf3,0x00,0x00,0x00, - 0x0e,0x01,0x00,0x00,0xfc,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x26,0x00,0x00,0x00, - 0x11,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0xf3,0x00,0x00,0x00,0x0c,0x01,0x00,0x00, - 0xfc,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x26,0x00,0x00,0x00,0x12,0x01,0x00,0x00, - 0xf9,0x00,0x00,0x00,0xf3,0x00,0x00,0x00,0x0d,0x01,0x00,0x00,0xfc,0x00,0x00,0x00, - 0x0c,0x00,0x07,0x00,0x26,0x00,0x00,0x00,0x13,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x44,0x00,0x00,0x00,0x12,0x01,0x00,0x00,0x11,0x01,0x00,0x00,0x94,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0xe6,0x00,0x00,0x00,0x13,0x01,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x15,0x01,0x00,0x00,0x22,0x00,0x00,0x00, - 0x14,0x01,0x00,0x00,0x88,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x16,0x01,0x00,0x00, - 0x15,0x01,0x00,0x00,0x10,0x01,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x17,0x01,0x00,0x00,0x22,0x00,0x00,0x00,0x16,0x01,0x00,0x00,0x94,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x18,0x01,0x00,0x00,0xe6,0x00,0x00,0x00,0x11,0x01,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x22,0x00,0x00,0x00, - 0x18,0x01,0x00,0x00,0x88,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x1a,0x01,0x00,0x00, - 0x19,0x01,0x00,0x00,0x10,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x1b,0x01,0x00,0x00,0x22,0x00,0x00,0x00,0x1a,0x01,0x00,0x00,0x50,0x00,0x05,0x00, - 0x45,0x00,0x00,0x00,0x1c,0x01,0x00,0x00,0x17,0x01,0x00,0x00,0x1b,0x01,0x00,0x00, - 0x83,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x1d,0x01,0x00,0x00,0xe0,0x00,0x00,0x00, - 0xde,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x1e,0x01,0x00,0x00, - 0xe0,0x00,0x00,0x00,0x1d,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x1f,0x01,0x00,0x00,0xde,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x85,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x1d,0x01,0x00,0x00,0x10,0x01,0x00,0x00, - 0x88,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x21,0x01,0x00,0x00,0x1f,0x01,0x00,0x00, - 0x20,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x22,0x01,0x00,0x00, - 0x1e,0x01,0x00,0x00,0x21,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x23,0x01,0x00,0x00,0xe2,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x83,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x24,0x01,0x00,0x00,0x22,0x01,0x00,0x00,0x23,0x01,0x00,0x00, - 0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x25,0x01,0x00,0x00,0x24,0x01,0x00,0x00, - 0x23,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x26,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfa,0x00,0x04,0x00,0x25,0x01,0x00,0x00,0x27,0x01,0x00,0x00,0x26,0x01,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x27,0x01,0x00,0x00,0xf9,0x00,0x02,0x00,0xd9,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x26,0x01,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x28,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0x29,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x2a,0x01,0x00,0x00,0xdc,0x00,0x00,0x00,0x0f,0x01,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0x2b,0x01,0x00,0x00,0x17,0x01,0x00,0x00,0x1b,0x01,0x00,0x00, - 0x2a,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0x2c,0x01,0x00,0x00, - 0x28,0x01,0x00,0x00,0x29,0x01,0x00,0x00,0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00, - 0x2d,0x01,0x00,0x00,0x2c,0x01,0x00,0x00,0x2b,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x2e,0x01,0x00,0x00,0x2d,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x2f,0x01,0x00,0x00, - 0x2e,0x01,0x00,0x00,0x24,0x01,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0x30,0x01,0x00,0x00,0x2f,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x31,0x01,0x00,0x00,0x13,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x32,0x01,0x00,0x00,0x14,0x00,0x00,0x00, - 0x50,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0x33,0x01,0x00,0x00,0xe9,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0x34,0x01,0x00,0x00, - 0x1c,0x01,0x00,0x00,0x33,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x35,0x01,0x00,0x00,0x34,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x36,0x01,0x00,0x00,0x34,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0x37,0x01,0x00,0x00,0x35,0x01,0x00,0x00, - 0x36,0x01,0x00,0x00,0x2a,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00, - 0x38,0x01,0x00,0x00,0x31,0x01,0x00,0x00,0x32,0x01,0x00,0x00,0x57,0x00,0x06,0x00, - 0x39,0x00,0x00,0x00,0x39,0x01,0x00,0x00,0x38,0x01,0x00,0x00,0x37,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x3a,0x01,0x00,0x00, - 0x39,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x3b,0x01,0x00,0x00,0x3a,0x01,0x00,0x00,0x24,0x01,0x00,0x00,0xa9,0x00,0x06,0x00, - 0x21,0x00,0x00,0x00,0x3c,0x01,0x00,0x00,0x3b,0x01,0x00,0x00,0x23,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x3d,0x01,0x00,0x00, - 0x30,0x01,0x00,0x00,0x3c,0x01,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0x3e,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0x3f,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x45,0x00,0x00,0x00, - 0x40,0x01,0x00,0x00,0x1c,0x01,0x00,0x00,0x33,0x01,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x41,0x01,0x00,0x00,0x40,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x42,0x01,0x00,0x00,0x40,0x01,0x00,0x00, - 0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0x43,0x01,0x00,0x00, - 0x41,0x01,0x00,0x00,0x42,0x01,0x00,0x00,0x2a,0x01,0x00,0x00,0x56,0x00,0x05,0x00, - 0x16,0x00,0x00,0x00,0x44,0x01,0x00,0x00,0x3e,0x01,0x00,0x00,0x3f,0x01,0x00,0x00, - 0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00,0x45,0x01,0x00,0x00,0x44,0x01,0x00,0x00, - 0x43,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x46,0x01,0x00,0x00,0x45,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0x47,0x01,0x00,0x00,0x46,0x01,0x00,0x00,0x24,0x01,0x00,0x00, - 0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0x48,0x01,0x00,0x00,0x47,0x01,0x00,0x00, - 0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x49,0x01,0x00,0x00,0x3d,0x01,0x00,0x00,0x48,0x01,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x4a,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x4b,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00, - 0x45,0x00,0x00,0x00,0x4c,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0xe9,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0x4d,0x01,0x00,0x00,0x1c,0x01,0x00,0x00, - 0x4c,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x4e,0x01,0x00,0x00, - 0x4d,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x4f,0x01,0x00,0x00,0x4d,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0x50,0x01,0x00,0x00,0x4e,0x01,0x00,0x00,0x4f,0x01,0x00,0x00, - 0x2a,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0x51,0x01,0x00,0x00, - 0x4a,0x01,0x00,0x00,0x4b,0x01,0x00,0x00,0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00, - 0x52,0x01,0x00,0x00,0x51,0x01,0x00,0x00,0x50,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x53,0x01,0x00,0x00,0x52,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x54,0x01,0x00,0x00, - 0x53,0x01,0x00,0x00,0x24,0x01,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0x55,0x01,0x00,0x00,0x54,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x56,0x01,0x00,0x00,0x49,0x01,0x00,0x00, - 0x55,0x01,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x57,0x01,0x00,0x00, - 0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x58,0x01,0x00,0x00, - 0x14,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0x59,0x01,0x00,0x00, - 0x1c,0x01,0x00,0x00,0x4c,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x5a,0x01,0x00,0x00,0x59,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x5b,0x01,0x00,0x00,0x59,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0x5c,0x01,0x00,0x00,0x5a,0x01,0x00,0x00, - 0x5b,0x01,0x00,0x00,0x2a,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00, - 0x5d,0x01,0x00,0x00,0x57,0x01,0x00,0x00,0x58,0x01,0x00,0x00,0x57,0x00,0x06,0x00, - 0x39,0x00,0x00,0x00,0x5e,0x01,0x00,0x00,0x5d,0x01,0x00,0x00,0x5c,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x5f,0x01,0x00,0x00, - 0x5e,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x60,0x01,0x00,0x00,0x5f,0x01,0x00,0x00,0x24,0x01,0x00,0x00,0xa9,0x00,0x06,0x00, - 0x21,0x00,0x00,0x00,0x61,0x01,0x00,0x00,0x60,0x01,0x00,0x00,0x23,0x00,0x00,0x00, - 0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x62,0x01,0x00,0x00, - 0x56,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x63,0x01,0x00,0x00,0x62,0x01,0x00,0x00,0x50,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, - 0xd9,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xd9,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x21,0x00,0x00,0x00,0x64,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x27,0x01,0x00,0x00, - 0x63,0x01,0x00,0x00,0x26,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x65,0x01,0x00,0x00,0xc7,0x00,0x00,0x00,0x64,0x01,0x00,0x00,0xf9,0x00,0x02,0x00, - 0xd3,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xd5,0x00,0x00,0x00,0xf7,0x00,0x03,0x00, - 0x66,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xfb,0x00,0x03,0x00,0x4d,0x00,0x00,0x00, - 0x67,0x01,0x00,0x00,0xf8,0x00,0x02,0x00,0x67,0x01,0x00,0x00,0x41,0x00,0x06,0x00, - 0x4c,0x00,0x00,0x00,0x68,0x01,0x00,0x00,0x0d,0x00,0x00,0x00,0x37,0x00,0x00,0x00, - 0xcf,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x3a,0x00,0x00,0x00,0x69,0x01,0x00,0x00, - 0x68,0x01,0x00,0x00,0x41,0x00,0x06,0x00,0x4b,0x00,0x00,0x00,0x6a,0x01,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x6b,0x01,0x00,0x00,0x6a,0x01,0x00,0x00,0x8e,0x00,0x05,0x00, - 0x26,0x00,0x00,0x00,0x6c,0x01,0x00,0x00,0x6d,0x00,0x00,0x00,0x6b,0x01,0x00,0x00, - 0x8e,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x6d,0x01,0x00,0x00,0x6c,0x01,0x00,0x00, - 0x31,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x6e,0x01,0x00,0x00, - 0x52,0x00,0x00,0x00,0x6d,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x6f,0x01,0x00,0x00,0x6e,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x70,0x01,0x00,0x00,0x6e,0x01,0x00,0x00,0x01,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x71,0x01,0x00,0x00,0x6e,0x01,0x00,0x00, - 0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x39,0x00,0x00,0x00,0x72,0x01,0x00,0x00, - 0x6f,0x01,0x00,0x00,0x70,0x01,0x00,0x00,0x71,0x01,0x00,0x00,0x23,0x00,0x00,0x00, - 0x90,0x00,0x05,0x00,0x39,0x00,0x00,0x00,0x73,0x01,0x00,0x00,0x72,0x01,0x00,0x00, - 0x69,0x01,0x00,0x00,0x41,0x00,0x07,0x00,0x4b,0x00,0x00,0x00,0x74,0x01,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0xcf,0x00,0x00,0x00,0x1f,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x75,0x01,0x00,0x00,0x74,0x01,0x00,0x00, - 0x41,0x00,0x06,0x00,0x4b,0x00,0x00,0x00,0x76,0x01,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x30,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0x77,0x01,0x00,0x00,0x76,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x78,0x01,0x00,0x00,0x73,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0x79,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0x78,0x01,0x00,0x00, - 0x78,0x01,0x00,0x00,0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0x7a,0x01,0x00,0x00, - 0x73,0x01,0x00,0x00,0x73,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x7b,0x01,0x00,0x00, - 0x7a,0x01,0x00,0x00,0x79,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x7c,0x01,0x00,0x00,0x7b,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x85,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x7d,0x01,0x00,0x00,0x7c,0x01,0x00,0x00,0x22,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x7e,0x01,0x00,0x00,0x7d,0x01,0x00,0x00, - 0x22,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x7f,0x01,0x00,0x00, - 0x7b,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x80,0x01,0x00,0x00,0x7f,0x01,0x00,0x00,0x22,0x00,0x00,0x00,0x83,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x81,0x01,0x00,0x00,0x22,0x00,0x00,0x00,0x80,0x01,0x00,0x00, - 0x50,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0x82,0x01,0x00,0x00,0x7e,0x01,0x00,0x00, - 0x81,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x83,0x01,0x00,0x00, - 0x7b,0x01,0x00,0x00,0x02,0x00,0x00,0x00,0x94,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x84,0x01,0x00,0x00,0x6d,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x0c,0x00,0x08,0x00, - 0x21,0x00,0x00,0x00,0x85,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, - 0x84,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x83,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x86,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x85,0x01,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x87,0x01,0x00,0x00,0x28,0x00,0x00,0x00, - 0x86,0x01,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x88,0x01,0x00,0x00, - 0x23,0x00,0x00,0x00,0x87,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x89,0x01,0x00,0x00,0x6b,0x01,0x00,0x00,0x88,0x01,0x00,0x00,0x83,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x8a,0x01,0x00,0x00,0x83,0x01,0x00,0x00,0x89,0x01,0x00,0x00, - 0xbf,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x8b,0x01,0x00,0x00,0x7e,0x01,0x00,0x00, - 0x2e,0x00,0x00,0x00,0xf7,0x00,0x03,0x00,0x8c,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfa,0x00,0x04,0x00,0x8b,0x01,0x00,0x00,0x8d,0x01,0x00,0x00,0x8c,0x01,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x8d,0x01,0x00,0x00,0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00, - 0x8e,0x01,0x00,0x00,0x7e,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, - 0x8c,0x01,0x00,0x00,0xf8,0x00,0x02,0x00,0x8c,0x01,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x32,0x00,0x00,0x00,0x8f,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0x67,0x01,0x00,0x00, - 0x8e,0x01,0x00,0x00,0x8d,0x01,0x00,0x00,0xa8,0x00,0x04,0x00,0x32,0x00,0x00,0x00, - 0x90,0x01,0x00,0x00,0x8f,0x01,0x00,0x00,0xf7,0x00,0x03,0x00,0x91,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0x90,0x01,0x00,0x00,0x92,0x01,0x00,0x00, - 0x91,0x01,0x00,0x00,0xf8,0x00,0x02,0x00,0x92,0x01,0x00,0x00,0xb8,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0x93,0x01,0x00,0x00,0x81,0x01,0x00,0x00,0x2e,0x00,0x00,0x00, - 0xf9,0x00,0x02,0x00,0x91,0x01,0x00,0x00,0xf8,0x00,0x02,0x00,0x91,0x01,0x00,0x00, - 0xf5,0x00,0x07,0x00,0x32,0x00,0x00,0x00,0x94,0x01,0x00,0x00,0x38,0x00,0x00,0x00, - 0x8c,0x01,0x00,0x00,0x93,0x01,0x00,0x00,0x92,0x01,0x00,0x00,0xa8,0x00,0x04,0x00, - 0x32,0x00,0x00,0x00,0x95,0x01,0x00,0x00,0x94,0x01,0x00,0x00,0xf7,0x00,0x03,0x00, - 0x96,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00,0x95,0x01,0x00,0x00, - 0x97,0x01,0x00,0x00,0x96,0x01,0x00,0x00,0xf8,0x00,0x02,0x00,0x97,0x01,0x00,0x00, - 0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x98,0x01,0x00,0x00,0x81,0x01,0x00,0x00, - 0x23,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x96,0x01,0x00,0x00,0xf8,0x00,0x02,0x00, - 0x96,0x01,0x00,0x00,0xf5,0x00,0x07,0x00,0x32,0x00,0x00,0x00,0x99,0x01,0x00,0x00, - 0x38,0x00,0x00,0x00,0x91,0x01,0x00,0x00,0x98,0x01,0x00,0x00,0x97,0x01,0x00,0x00, - 0xa8,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x9a,0x01,0x00,0x00,0x99,0x01,0x00,0x00, - 0xf7,0x00,0x03,0x00,0x9b,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xfa,0x00,0x04,0x00, - 0x9a,0x01,0x00,0x00,0x9c,0x01,0x00,0x00,0x9b,0x01,0x00,0x00,0xf8,0x00,0x02,0x00, - 0x9c,0x01,0x00,0x00,0xba,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x9d,0x01,0x00,0x00, - 0x8a,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0xf9,0x00,0x02,0x00,0x9b,0x01,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x9b,0x01,0x00,0x00,0xf5,0x00,0x07,0x00,0x32,0x00,0x00,0x00, - 0x9e,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0x96,0x01,0x00,0x00,0x9d,0x01,0x00,0x00, - 0x9c,0x01,0x00,0x00,0xf7,0x00,0x03,0x00,0x9f,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0xfa,0x00,0x04,0x00,0x9e,0x01,0x00,0x00,0xa0,0x01,0x00,0x00,0x9f,0x01,0x00,0x00, - 0xf8,0x00,0x02,0x00,0xa0,0x01,0x00,0x00,0xf9,0x00,0x02,0x00,0x66,0x01,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x9f,0x01,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00, - 0xa1,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0xa2,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x7f,0x00,0x04,0x00,0x21,0x00,0x00,0x00, - 0xa3,0x01,0x00,0x00,0x77,0x01,0x00,0x00,0x50,0x00,0x05,0x00,0x45,0x00,0x00,0x00, - 0xa4,0x01,0x00,0x00,0xa3,0x01,0x00,0x00,0xa3,0x01,0x00,0x00,0x81,0x00,0x05,0x00, - 0x45,0x00,0x00,0x00,0xa5,0x01,0x00,0x00,0x82,0x01,0x00,0x00,0xa4,0x01,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xa6,0x01,0x00,0x00,0xa5,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xa7,0x01,0x00,0x00, - 0xa5,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00, - 0xa8,0x01,0x00,0x00,0xa6,0x01,0x00,0x00,0xa7,0x01,0x00,0x00,0x75,0x01,0x00,0x00, - 0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0xa9,0x01,0x00,0x00,0xa1,0x01,0x00,0x00, - 0xa2,0x01,0x00,0x00,0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00,0xaa,0x01,0x00,0x00, - 0xa9,0x01,0x00,0x00,0xa8,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0xab,0x01,0x00,0x00,0xaa,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xac,0x01,0x00,0x00,0xab,0x01,0x00,0x00, - 0x8a,0x01,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0xad,0x01,0x00,0x00, - 0xac,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0xae,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0xaf,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00, - 0x45,0x00,0x00,0x00,0xb0,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0xa3,0x01,0x00,0x00, - 0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0xb1,0x01,0x00,0x00,0x82,0x01,0x00,0x00, - 0xb0,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xb2,0x01,0x00,0x00, - 0xb1,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xb3,0x01,0x00,0x00,0xb1,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0xb4,0x01,0x00,0x00,0xb2,0x01,0x00,0x00,0xb3,0x01,0x00,0x00, - 0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0xb5,0x01,0x00,0x00, - 0xae,0x01,0x00,0x00,0xaf,0x01,0x00,0x00,0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00, - 0xb6,0x01,0x00,0x00,0xb5,0x01,0x00,0x00,0xb4,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xb7,0x01,0x00,0x00,0xb6,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xb8,0x01,0x00,0x00, - 0xb7,0x01,0x00,0x00,0x8a,0x01,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0xb9,0x01,0x00,0x00,0xb8,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xba,0x01,0x00,0x00,0xad,0x01,0x00,0x00, - 0xb9,0x01,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0xbb,0x01,0x00,0x00, - 0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xbc,0x01,0x00,0x00, - 0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0xbd,0x01,0x00,0x00, - 0x77,0x01,0x00,0x00,0xa3,0x01,0x00,0x00,0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00, - 0xbe,0x01,0x00,0x00,0x82,0x01,0x00,0x00,0xbd,0x01,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0xbf,0x01,0x00,0x00,0xbe,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xc0,0x01,0x00,0x00,0xbe,0x01,0x00,0x00, - 0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0xc1,0x01,0x00,0x00, - 0xbf,0x01,0x00,0x00,0xc0,0x01,0x00,0x00,0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00, - 0x16,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0xbb,0x01,0x00,0x00,0xbc,0x01,0x00,0x00, - 0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00,0xc3,0x01,0x00,0x00,0xc2,0x01,0x00,0x00, - 0xc1,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xc4,0x01,0x00,0x00,0xc3,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0xc5,0x01,0x00,0x00,0xc4,0x01,0x00,0x00,0x8a,0x01,0x00,0x00, - 0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0xc6,0x01,0x00,0x00,0xc5,0x01,0x00,0x00, - 0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xc7,0x01,0x00,0x00,0xba,0x01,0x00,0x00,0xc6,0x01,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0xc8,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0xc9,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00, - 0x45,0x00,0x00,0x00,0xca,0x01,0x00,0x00,0xa3,0x01,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0xcb,0x01,0x00,0x00,0x82,0x01,0x00,0x00, - 0xca,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xcc,0x01,0x00,0x00, - 0xcb,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xcd,0x01,0x00,0x00,0xcb,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0xce,0x01,0x00,0x00,0xcc,0x01,0x00,0x00,0xcd,0x01,0x00,0x00, - 0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0xcf,0x01,0x00,0x00, - 0xc8,0x01,0x00,0x00,0xc9,0x01,0x00,0x00,0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00, - 0xd0,0x01,0x00,0x00,0xcf,0x01,0x00,0x00,0xce,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xd1,0x01,0x00,0x00,0xd0,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xd2,0x01,0x00,0x00, - 0xd1,0x01,0x00,0x00,0x8a,0x01,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0xd3,0x01,0x00,0x00,0xd2,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xd4,0x01,0x00,0x00,0xc7,0x01,0x00,0x00, - 0xd3,0x01,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0xd5,0x01,0x00,0x00, - 0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xd6,0x01,0x00,0x00, - 0x14,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0xd7,0x01,0x00,0x00, - 0x7e,0x01,0x00,0x00,0x81,0x01,0x00,0x00,0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00, - 0x16,0x00,0x00,0x00,0xd8,0x01,0x00,0x00,0xd5,0x01,0x00,0x00,0xd6,0x01,0x00,0x00, - 0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00,0xd9,0x01,0x00,0x00,0xd8,0x01,0x00,0x00, - 0xd7,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xda,0x01,0x00,0x00,0xd9,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0xdb,0x01,0x00,0x00,0xda,0x01,0x00,0x00,0x8a,0x01,0x00,0x00, - 0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0xdc,0x01,0x00,0x00,0xdb,0x01,0x00,0x00, - 0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xdd,0x01,0x00,0x00,0xd4,0x01,0x00,0x00,0xdc,0x01,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0xde,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0xdf,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00, - 0x45,0x00,0x00,0x00,0xe0,0x01,0x00,0x00,0x77,0x01,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0xe1,0x01,0x00,0x00,0x82,0x01,0x00,0x00, - 0xe0,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xe2,0x01,0x00,0x00, - 0xe1,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xe3,0x01,0x00,0x00,0xe1,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0xe4,0x01,0x00,0x00,0xe2,0x01,0x00,0x00,0xe3,0x01,0x00,0x00, - 0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0xe5,0x01,0x00,0x00, - 0xde,0x01,0x00,0x00,0xdf,0x01,0x00,0x00,0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00, - 0xe6,0x01,0x00,0x00,0xe5,0x01,0x00,0x00,0xe4,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xe7,0x01,0x00,0x00,0xe6,0x01,0x00,0x00, - 0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0xe8,0x01,0x00,0x00, - 0xe7,0x01,0x00,0x00,0x8a,0x01,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0xe9,0x01,0x00,0x00,0xe8,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xea,0x01,0x00,0x00,0xdd,0x01,0x00,0x00, - 0xe9,0x01,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0xeb,0x01,0x00,0x00, - 0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xec,0x01,0x00,0x00, - 0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0xed,0x01,0x00,0x00, - 0xa3,0x01,0x00,0x00,0x77,0x01,0x00,0x00,0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00, - 0xee,0x01,0x00,0x00,0x82,0x01,0x00,0x00,0xed,0x01,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0xef,0x01,0x00,0x00,0xee,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xf0,0x01,0x00,0x00,0xee,0x01,0x00,0x00, - 0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0xf1,0x01,0x00,0x00, - 0xef,0x01,0x00,0x00,0xf0,0x01,0x00,0x00,0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00, - 0x16,0x00,0x00,0x00,0xf2,0x01,0x00,0x00,0xeb,0x01,0x00,0x00,0xec,0x01,0x00,0x00, - 0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00,0xf3,0x01,0x00,0x00,0xf2,0x01,0x00,0x00, - 0xf1,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xf4,0x01,0x00,0x00,0xf3,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0xf5,0x01,0x00,0x00,0xf4,0x01,0x00,0x00,0x8a,0x01,0x00,0x00, - 0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0xf6,0x01,0x00,0x00,0xf5,0x01,0x00,0x00, - 0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xf7,0x01,0x00,0x00,0xea,0x01,0x00,0x00,0xf6,0x01,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0xf8,0x01,0x00,0x00,0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0xf9,0x01,0x00,0x00,0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00, - 0x45,0x00,0x00,0x00,0xfa,0x01,0x00,0x00,0x2e,0x00,0x00,0x00,0x77,0x01,0x00,0x00, - 0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0xfb,0x01,0x00,0x00,0x82,0x01,0x00,0x00, - 0xfa,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0xfc,0x01,0x00,0x00, - 0xfb,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0xfd,0x01,0x00,0x00,0xfb,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00, - 0x26,0x00,0x00,0x00,0xfe,0x01,0x00,0x00,0xfc,0x01,0x00,0x00,0xfd,0x01,0x00,0x00, - 0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00,0x16,0x00,0x00,0x00,0xff,0x01,0x00,0x00, - 0xf8,0x01,0x00,0x00,0xf9,0x01,0x00,0x00,0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00, - 0x00,0x02,0x00,0x00,0xff,0x01,0x00,0x00,0xfe,0x01,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x01,0x02,0x00,0x00,0x00,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00,0x32,0x00,0x00,0x00,0x02,0x02,0x00,0x00, - 0x01,0x02,0x00,0x00,0x8a,0x01,0x00,0x00,0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00, - 0x03,0x02,0x00,0x00,0x02,0x02,0x00,0x00,0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00, - 0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x04,0x02,0x00,0x00,0xf7,0x01,0x00,0x00, - 0x03,0x02,0x00,0x00,0x3d,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x05,0x02,0x00,0x00, - 0x13,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x06,0x02,0x00,0x00, - 0x14,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0x07,0x02,0x00,0x00, - 0x77,0x01,0x00,0x00,0x77,0x01,0x00,0x00,0x81,0x00,0x05,0x00,0x45,0x00,0x00,0x00, - 0x08,0x02,0x00,0x00,0x82,0x01,0x00,0x00,0x07,0x02,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x09,0x02,0x00,0x00,0x08,0x02,0x00,0x00,0x00,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x0a,0x02,0x00,0x00,0x08,0x02,0x00,0x00, - 0x01,0x00,0x00,0x00,0x50,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0x0b,0x02,0x00,0x00, - 0x09,0x02,0x00,0x00,0x0a,0x02,0x00,0x00,0x75,0x01,0x00,0x00,0x56,0x00,0x05,0x00, - 0x16,0x00,0x00,0x00,0x0c,0x02,0x00,0x00,0x05,0x02,0x00,0x00,0x06,0x02,0x00,0x00, - 0x57,0x00,0x06,0x00,0x39,0x00,0x00,0x00,0x0d,0x02,0x00,0x00,0x0c,0x02,0x00,0x00, - 0x0b,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x0e,0x02,0x00,0x00,0x0d,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xbe,0x00,0x05,0x00, - 0x32,0x00,0x00,0x00,0x0f,0x02,0x00,0x00,0x0e,0x02,0x00,0x00,0x8a,0x01,0x00,0x00, - 0xa9,0x00,0x06,0x00,0x21,0x00,0x00,0x00,0x10,0x02,0x00,0x00,0x0f,0x02,0x00,0x00, - 0x23,0x00,0x00,0x00,0x2e,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x11,0x02,0x00,0x00,0x04,0x02,0x00,0x00,0x10,0x02,0x00,0x00,0x85,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x12,0x02,0x00,0x00,0x11,0x02,0x00,0x00,0x4f,0x00,0x00,0x00, - 0xf9,0x00,0x02,0x00,0x66,0x01,0x00,0x00,0xf8,0x00,0x02,0x00,0x66,0x01,0x00,0x00, - 0xf5,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0x13,0x02,0x00,0x00,0x23,0x00,0x00,0x00, - 0xa0,0x01,0x00,0x00,0x12,0x02,0x00,0x00,0x9f,0x01,0x00,0x00,0x85,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x14,0x02,0x00,0x00,0xc7,0x00,0x00,0x00,0x13,0x02,0x00,0x00, - 0xf9,0x00,0x02,0x00,0xd3,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xd3,0x00,0x00,0x00, - 0xf5,0x00,0x07,0x00,0x21,0x00,0x00,0x00,0x15,0x02,0x00,0x00,0x65,0x01,0x00,0x00, - 0xd9,0x00,0x00,0x00,0x14,0x02,0x00,0x00,0x66,0x01,0x00,0x00,0xf9,0x00,0x02,0x00, - 0xcc,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0xcc,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x21,0x00,0x00,0x00,0x16,0x02,0x00,0x00,0xc7,0x00,0x00,0x00,0x99,0x00,0x00,0x00, - 0x15,0x02,0x00,0x00,0xd3,0x00,0x00,0x00,0x94,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x17,0x02,0x00,0x00,0x6d,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x0c,0x00,0x08,0x00, - 0x21,0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, - 0x17,0x02,0x00,0x00,0x2e,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x81,0x00,0x05,0x00, - 0x26,0x00,0x00,0x00,0x19,0x02,0x00,0x00,0xc8,0x00,0x00,0x00,0x72,0x00,0x00,0x00, - 0x0c,0x00,0x06,0x00,0x26,0x00,0x00,0x00,0x1a,0x02,0x00,0x00,0x01,0x00,0x00,0x00, - 0x45,0x00,0x00,0x00,0x19,0x02,0x00,0x00,0x94,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x1b,0x02,0x00,0x00,0x6d,0x00,0x00,0x00,0x1a,0x02,0x00,0x00,0x0c,0x00,0x08,0x00, - 0x21,0x00,0x00,0x00,0x1c,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x00,0x00, - 0x1b,0x02,0x00,0x00,0x2e,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x0c,0x00,0x07,0x00, - 0x21,0x00,0x00,0x00,0x1d,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, - 0x1c,0x02,0x00,0x00,0x7d,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x1e,0x02,0x00,0x00,0x5c,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x83,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x1f,0x02,0x00,0x00,0x23,0x00,0x00,0x00,0x1e,0x02,0x00,0x00, - 0x85,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x20,0x02,0x00,0x00,0x1d,0x02,0x00,0x00, - 0x1f,0x02,0x00,0x00,0x8e,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x21,0x02,0x00,0x00, - 0x76,0x00,0x00,0x00,0x18,0x02,0x00,0x00,0x8e,0x00,0x05,0x00,0x26,0x00,0x00,0x00, - 0x22,0x02,0x00,0x00,0x7b,0x00,0x00,0x00,0x20,0x02,0x00,0x00,0x8e,0x00,0x05,0x00, - 0x26,0x00,0x00,0x00,0x23,0x02,0x00,0x00,0x22,0x02,0x00,0x00,0x18,0x02,0x00,0x00, - 0x81,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x24,0x02,0x00,0x00,0x21,0x02,0x00,0x00, - 0x23,0x02,0x00,0x00,0x41,0x00,0x07,0x00,0x4a,0x00,0x00,0x00,0x25,0x02,0x00,0x00, - 0x0d,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, - 0x3d,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x26,0x02,0x00,0x00,0x25,0x02,0x00,0x00, - 0x4f,0x00,0x08,0x00,0x26,0x00,0x00,0x00,0x27,0x02,0x00,0x00,0x26,0x02,0x00,0x00, - 0x26,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x28,0x02,0x00,0x00,0x24,0x02,0x00,0x00, - 0x27,0x02,0x00,0x00,0x8e,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x29,0x02,0x00,0x00, - 0x28,0x02,0x00,0x00,0x16,0x02,0x00,0x00,0x81,0x00,0x05,0x00,0x26,0x00,0x00,0x00, - 0x2a,0x02,0x00,0x00,0x8b,0x00,0x00,0x00,0x29,0x02,0x00,0x00,0xf9,0x00,0x02,0x00, - 0x8d,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x8d,0x00,0x00,0x00,0xf5,0x00,0x07,0x00, - 0x26,0x00,0x00,0x00,0x8c,0x00,0x00,0x00,0x8b,0x00,0x00,0x00,0x95,0x00,0x00,0x00, - 0x2a,0x02,0x00,0x00,0xcc,0x00,0x00,0x00,0x80,0x00,0x05,0x00,0x1b,0x00,0x00,0x00, - 0x8f,0x00,0x00,0x00,0x8e,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0xf9,0x00,0x02,0x00, - 0x8a,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x91,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x21,0x00,0x00,0x00,0x2b,0x02,0x00,0x00,0x67,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x2c,0x02,0x00,0x00,0x8b,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00,0x2d,0x02,0x00,0x00, - 0x8b,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x21,0x00,0x00,0x00, - 0x2e,0x02,0x00,0x00,0x8b,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00, - 0x39,0x00,0x00,0x00,0x2f,0x02,0x00,0x00,0x2c,0x02,0x00,0x00,0x2d,0x02,0x00,0x00, - 0x2e,0x02,0x00,0x00,0x2b,0x02,0x00,0x00,0xf9,0x00,0x02,0x00,0x55,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x55,0x00,0x00,0x00,0xf5,0x00,0x07,0x00,0x39,0x00,0x00,0x00, - 0x30,0x02,0x00,0x00,0x67,0x00,0x00,0x00,0x6c,0x00,0x00,0x00,0x2f,0x02,0x00,0x00, - 0x91,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x30,0x02,0x00,0x00, - 0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, -}; - -static const unsigned char _fragmentMainDXIL[] = { - 0x44,0x58,0x42,0x43,0x76,0x14,0xe9,0x03,0x2c,0x49,0xf4,0x24,0xed,0x17,0xb2,0xda, - 0xdc,0xbc,0x6e,0x13,0x01,0x00,0x00,0x00,0x98,0x22,0x00,0x00,0x07,0x00,0x00,0x00, - 0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0xc8,0x00,0x00,0x00,0x04,0x01,0x00,0x00, - 0x78,0x02,0x00,0x00,0xa0,0x0d,0x00,0x00,0xbc,0x0d,0x00,0x00,0x53,0x46,0x49,0x30, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, - 0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x68,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43, - 0x4f,0x4f,0x52,0x44,0x00,0x00,0x00,0x00,0x4f,0x53,0x47,0x31,0x34,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x28,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65, - 0x74,0x00,0x00,0x00,0x50,0x53,0x56,0x30,0x6c,0x01,0x00,0x00,0x34,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x03,0x01,0x00,0x03, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x1c,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, - 0x44,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x00,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e, - 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x43,0x00,0x03,0x02,0x00,0x00,0x0a,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x01,0x01,0x43,0x00,0x03,0x02,0x00,0x00,0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x01,0x02,0x42,0x00,0x03,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x44,0x10,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0x54,0x41,0x54,0x20,0x0b,0x00,0x00, - 0x60,0x00,0x00,0x00,0xc8,0x02,0x00,0x00,0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00, - 0x10,0x00,0x00,0x00,0x08,0x0b,0x00,0x00,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00, - 0xbf,0x02,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c, - 0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x18,0x45,0x02,0x42,0x92,0x0b,0x42, - 0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x62,0x88,0x48,0x90,0x14,0x20, - 0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90,0x11,0x23,0xc4,0x50, - 0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x31,0x46,0x06,0x51,0x18,0x00,0x00, - 0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07,0x40,0x02,0xa8,0x0d, - 0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff,0xff,0xff,0xff,0x1f, - 0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x82,0x60,0x42, - 0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00,0x85,0x00,0x00,0x00, - 0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04,0x13,0x23,0xa4,0x84,0x04,0x13,0x23,0xe3, - 0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c,0x0b,0x84,0xc4,0x4c,0x10,0xbc,0xc1,0x08, - 0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00,0x29,0xc6,0x40,0x10, - 0x44,0x41,0x90,0x51,0x0c,0x80,0x20,0x88,0x62,0x20,0xe4,0xa6,0xe1,0xf2,0x27,0xec, - 0x21,0x24,0x7f,0x25,0xa4,0x95,0x98,0xfc,0xe2,0xb6,0x51,0x31,0x0c,0xc3,0x40,0x50, - 0x71,0xcf,0x70,0xf9,0x13,0xf6,0x10,0x92,0x1f,0x02,0xcd,0xb0,0x10,0x28,0x58,0x0a, - 0xa3,0x10,0x0c,0x33,0x0c,0xc3,0x40,0x10,0xc4,0x40,0x4d,0x41,0x06,0x62,0x18,0x86, - 0x61,0x18,0xe8,0x29,0xc3,0x40,0x0c,0x14,0x15,0x62,0x20,0x86,0x81,0xa6,0xa3,0x86, - 0xcb,0x9f,0xb0,0x87,0x90,0x7c,0x6e,0xa3,0x8a,0x95,0x98,0xfc,0xe2,0xb6,0x11,0x31, - 0x0c,0xc3,0x50,0x88,0x8a,0x60,0x08,0xb2,0x4a,0x31,0x10,0xc3,0x30,0x10,0x36,0x47, - 0x10,0x14,0x83,0x21,0x0a,0x82,0xd0,0x68,0x1b,0x08,0x18,0x46,0x20,0x86,0x99,0xda, - 0x60,0x1c,0xd8,0x21,0x1c,0xe6,0x61,0x1e,0xdc,0x80,0x16,0xca,0x01,0x1f,0xe8,0xa1, - 0x1e,0xe4,0xa1,0x1c,0xe4,0x80,0x14,0xf8,0xc0,0x1e,0xca,0x61,0x1c,0xe8,0xe1,0x1d, - 0xe4,0x81,0x0f,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x81,0x0d,0xc0,0x80,0x0e,0xfc, - 0x00,0x0c,0xfc,0x40,0x0f,0xf4,0xa0,0x1d,0xd2,0x01,0x1e,0xe6,0xe1,0x17,0xe8,0x21, - 0x1f,0xe0,0xa1,0x1c,0x50,0x40,0xcc,0x24,0x06,0xe3,0xc0,0x0e,0xe1,0x30,0x0f,0xf3, - 0xe0,0x06,0xb4,0x50,0x0e,0xf8,0x40,0x0f,0xf5,0x20,0x0f,0xe5,0x20,0x07,0xa4,0xc0, - 0x07,0xf6,0x50,0x0e,0xe3,0x40,0x0f,0xef,0x20,0x0f,0x7c,0x60,0x0e,0xec,0xf0,0x0e, - 0xe1,0x40,0x0f,0x6c,0x00,0x06,0x74,0xe0,0x07,0x60,0xe0,0x07,0x48,0xf0,0x3e,0x02, - 0x67,0x92,0x83,0x71,0x60,0x87,0x70,0x98,0x87,0x79,0x70,0x03,0x5a,0x28,0x07,0x7c, - 0xa0,0x87,0x7a,0x90,0x87,0x72,0x90,0x03,0x52,0x08,0x05,0x79,0x90,0x87,0x70,0xc8, - 0x07,0x3e,0xb0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0xe0,0x03,0x73,0x60,0x87, - 0x77,0x08,0x07,0x7a,0x60,0x03,0x30,0xa0,0x03,0x3f,0x00,0x03,0x3f,0xd0,0x03,0x3d, - 0x68,0x87,0x74,0x80,0x87,0x79,0xf8,0x05,0x7a,0xc8,0x07,0x78,0x28,0x07,0x14,0x10, - 0x33,0x9d,0xc1,0x38,0xb0,0x43,0x38,0xcc,0xc3,0x3c,0xb8,0x01,0x2d,0x94,0x03,0x3e, - 0xd0,0x43,0x3d,0xc8,0x43,0x39,0xc8,0x01,0x29,0x84,0x82,0x3c,0xc8,0x43,0x38,0xe4, - 0x03,0x1f,0xd8,0x43,0x39,0x8c,0x03,0x3d,0xbc,0x83,0x3c,0xf0,0x81,0x39,0xb0,0xc3, - 0x3b,0x84,0x03,0x3d,0xb0,0x01,0x18,0xd0,0x81,0x1f,0x80,0x81,0x1f,0x20,0xc1,0x13, - 0x69,0x4c,0x02,0x2f,0x11,0xca,0x44,0xf0,0x8e,0x91,0xa6,0x88,0x12,0x26,0xbf,0x44, - 0x8c,0x63,0x42,0x84,0xe7,0x79,0x5e,0x22,0xa0,0x07,0x06,0x87,0x23,0x4d,0x0b,0x80, - 0x39,0xd4,0xe4,0x4f,0xd8,0x43,0xfc,0x5f,0x04,0x18,0x0c,0xd1,0x4c,0x6e,0x83,0x14, - 0x4e,0xc4,0x48,0x48,0xf1,0x3c,0xcf,0xf3,0x3c,0xcf,0x3c,0x53,0x52,0x6f,0x92,0xa6, - 0x88,0x12,0x26,0x9f,0x05,0x98,0x67,0x21,0x22,0x76,0x02,0x26,0x02,0x05,0x04,0xb1, - 0xc9,0x40,0x00,0x00,0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79, - 0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d, - 0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0, - 0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e, - 0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73, - 0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10, - 0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, - 0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78, - 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e, - 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x34,0x40,0x00,0x0c,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x30,0xe4,0x79,0x80,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60, - 0xc8,0x13,0x01,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02, - 0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43,0x1e,0x0c,0x08,0x80,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x86,0x3c,0x1b,0x10,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x64,0x81,0x00,0x1b,0x00,0x00,0x00,0x32,0x1e,0x98,0x18,0x19,0x11,0x4c,0x90, - 0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22,0x4a,0x60,0x04,0xa0,0x18,0x8a,0xa0,0x24, - 0xca,0xa1,0x0c,0x0a,0x7c,0xa2,0x00,0x03,0x4a,0xa3,0x10,0x0a,0xa4,0x80,0x0a,0xac, - 0x40,0x03,0x0a,0x38,0xa0,0xc0,0x03,0x0a,0x3c,0xa1,0xc0,0x17,0xca,0xa3,0x28,0x09, - 0xca,0xfb,0xa0,0x38,0x0a,0x82,0x8a,0x92,0x18,0x01,0x28,0x82,0x32,0x28,0x90,0x42, - 0x20,0xad,0x06,0x28,0x9c,0x01,0x20,0x72,0x06,0x80,0xd0,0x19,0x00,0x52,0x67,0x00, - 0x68,0x9d,0x01,0xa0,0x76,0x06,0x80,0xdc,0xb1,0x18,0x85,0x38,0x8e,0x03,0x38,0x8e, - 0x03,0x78,0x1e,0x00,0x79,0x18,0x00,0x00,0x34,0x01,0x00,0x00,0x1a,0x03,0x4c,0x90, - 0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c, - 0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c, - 0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x08,0x65,0x82,0x40,0x2c,0x1b,0x84,0x81, - 0xd8,0x20,0x10,0x04,0x05,0xb8,0xb9,0x09,0x02,0xc1,0x6c,0x18,0x0e,0x84,0x98,0x20, - 0x84,0x41,0x1a,0x70,0x11,0x0b,0x9b,0x2b,0xa3,0x2a,0xc3,0xa3,0xab,0x93,0x2b,0x9b, - 0x20,0x10,0xcd,0x04,0x81,0x70,0x36,0x08,0x44,0xb3,0x21,0x21,0x94,0x85,0x21,0x06, - 0x86,0x70,0x26,0x08,0x64,0xa0,0x06,0x54,0xe6,0xd0,0xc2,0xc8,0xde,0xee,0x9a,0xc2, - 0xe0,0xe6,0x26,0x08,0xc4,0xb3,0x21,0x19,0xa0,0x88,0x19,0x06,0x89,0x70,0x36,0x08, - 0xcf,0x34,0x41,0x58,0x83,0x36,0x60,0x45,0x97,0x07,0x57,0xf6,0x65,0x24,0x17,0x76, - 0xd6,0x56,0xe6,0x46,0x57,0xe5,0x96,0x66,0xf6,0x26,0xd7,0x36,0x37,0x41,0x20,0xa0, - 0x09,0x02,0x11,0x6d,0x40,0x88,0xca,0xba,0x88,0x01,0x03,0x36,0x04,0xd9,0x04,0xa1, - 0x0d,0xdc,0x80,0x8b,0x58,0xd8,0x5c,0xd9,0x54,0x58,0x1b,0x1c,0x5b,0x99,0xdc,0x06, - 0x84,0xd8,0x38,0x86,0x18,0x08,0x80,0xcd,0x1c,0x5a,0x18,0xd9,0xdb,0xdd,0x54,0x58, - 0x1b,0x1c,0x5b,0x99,0xdc,0x06,0x64,0xd8,0x3c,0x66,0x18,0x08,0x60,0x83,0xd0,0x7d, - 0x1b,0x08,0x0a,0xd0,0xc0,0x60,0x82,0x80,0x06,0x6b,0x30,0x41,0x20,0x24,0x32,0x70, - 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x13,0x04,0x62,0x9a,0x20, - 0x10,0xd4,0x06,0x04,0x21,0x83,0x8b,0x90,0x9a,0x32,0x30,0x03,0x3a,0x64,0x69,0x72, - 0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e,0x67,0x65,0x13,0x04,0xa2,0xda,0x80, - 0x20,0x68,0x70,0xa5,0x81,0xd4,0x94,0x81,0x19,0xb0,0x18,0x7b,0x63,0x7b,0x93,0x9b, - 0x20,0x10,0xd6,0x06,0x04,0x59,0x83,0x8b,0x0d,0xa4,0xa6,0x0c,0xcc,0x80,0xc4,0xd8, - 0x9b,0x5b,0xd9,0x04,0x81,0xb8,0x36,0x20,0x88,0x1b,0x5c,0x6f,0x20,0x35,0x65,0x60, - 0x06,0x1b,0x8a,0x31,0x38,0x03,0x35,0x68,0x03,0x38,0x98,0x20,0xa8,0x01,0x1b,0xf0, - 0x33,0x92,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b,0x9b, - 0xfb,0x1a,0x0b,0x6b,0x2b,0x93,0x0b,0x83,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x9b, - 0x20,0x68,0x68,0xb0,0x41,0x41,0xe6,0xe0,0x22,0xa4,0xa6,0xa1,0x83,0x32,0x30,0x03, - 0x62,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x5f,0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x1b,0x10,0xc4,0x0e,0xae,0x34,0x90, - 0x9a,0x32,0x30,0x03,0x6a,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x5f,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x1b, - 0x10,0x04,0x0f,0x2e,0x36,0x90,0x9a,0x32,0x30,0x03,0x66,0x46,0x72,0x61,0x67,0x6d, - 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x5f,0x65,0x6d,0x69,0x73, - 0x73,0x69,0x76,0x65,0x1b,0x10,0x44,0x0f,0xae,0x37,0x90,0x9a,0x32,0x30,0x03,0x66, - 0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x5f,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x1b,0x10,0x84,0x0f,0xae,0x31,0x90, - 0x9a,0x32,0x30,0x03,0x5e,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x5f,0x63,0x6f,0x75,0x6e,0x74,0x73,0x13,0x04,0x02,0xdb, - 0x80,0x20,0x7e,0x70,0xfd,0x81,0xd4,0x94,0x81,0x19,0xb0,0x33,0x92,0x0b,0x3b,0x6b, - 0x2b,0x73,0xa3,0xab,0x72,0x4b,0x33,0x7b,0x93,0x6b,0x9b,0xfb,0x9a,0x43,0x0b,0x23, - 0x7b,0xbb,0x83,0x0a,0x93,0x0b,0x6b,0x9b,0x9b,0x20,0x10,0xd9,0x06,0x04,0x09,0x85, - 0x4b,0x14,0xa4,0xa6,0x0c,0xcc,0x80,0x9d,0x91,0x5c,0xd8,0x59,0x5b,0x99,0x1b,0x5d, - 0x95,0x5b,0x9a,0xd9,0x9b,0x5c,0xdb,0xdc,0xd7,0x1c,0x5a,0x18,0xd9,0xdb,0x5d,0x53, - 0x18,0x9d,0x5c,0x1a,0xde,0x86,0xc1,0x0c,0xcc,0x80,0x99,0x20,0x10,0xda,0x06,0x04, - 0x21,0x05,0xa6,0x14,0x2e,0x53,0x90,0x1a,0x6e,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e, - 0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x5f,0x73,0x68,0x61,0x64,0x6f,0x77, - 0x49,0x6e,0x66,0x6f,0x13,0x04,0x62,0xdb,0x80,0x20,0xa8,0x70,0xa5,0x82,0xd4,0x94, - 0x81,0x19,0xf0,0x32,0x92,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xab,0x72,0x4b,0x33,0x7b, - 0x93,0x6b,0x9b,0xfb,0x62,0x4b,0x3b,0x43,0xa3,0x9b,0x9b,0x20,0x10,0xdc,0x06,0x02, - 0x59,0x85,0x8b,0x15,0x36,0x2c,0x58,0x1d,0xdc,0x41,0x1e,0xec,0x41,0x1f,0x80,0xc2, - 0x28,0x9c,0x82,0x2a,0xb4,0xc2,0x86,0x82,0x10,0x83,0x38,0x90,0x03,0x57,0x98,0x20, - 0x08,0xc0,0x06,0x60,0xc3,0x40,0xc4,0x42,0x2c,0x6c,0x08,0x64,0x61,0xc3,0x30,0xc0, - 0xc2,0x2c,0x4c,0x10,0xdc,0xe0,0x0d,0x36,0x04,0xb5,0x40,0xc6,0x4c,0x2e,0xec,0xac, - 0xad,0xcc,0x8d,0xae,0x29,0x2c,0xcd,0x8d,0x08,0x55,0x11,0xd6,0xd0,0xd3,0x93,0x14, - 0xd1,0x04,0xa1,0x10,0x83,0x09,0x42,0x31,0x06,0x1b,0x02,0x62,0x82,0x50,0x90,0xc1, - 0x04,0xa1,0x28,0x83,0x0d,0xc2,0x25,0x6d,0x58,0x08,0x5c,0xc8,0x05,0x5d,0xd8,0x05, - 0x5e,0x18,0x7a,0x81,0xd0,0x05,0x5f,0xd8,0x10,0x0c,0x1b,0x96,0x01,0x17,0x72,0x41, - 0x17,0xc0,0x81,0x17,0x86,0x5e,0x18,0x74,0xc1,0x17,0x36,0x04,0xcc,0x06,0xe1,0xba, - 0x36,0x2c,0x0c,0x2e,0xe4,0x82,0x2e,0x88,0x03,0x2f,0x0c,0xbc,0xc0,0xe8,0xc2,0x38, - 0x6c,0x18,0x7e,0x21,0x1c,0xc8,0x81,0xc9,0x94,0xd5,0x17,0x55,0x98,0xdc,0x59,0x19, - 0xdd,0x04,0xa1,0x30,0x83,0x09,0x42,0x71,0x06,0x13,0x04,0xa2,0xdb,0x20,0x5c,0xe9, - 0xb0,0x61,0x21,0xcc,0x21,0x17,0xce,0x61,0x17,0x74,0x61,0x40,0x07,0x42,0x17,0xd4, - 0x61,0x43,0xb0,0x0e,0x1b,0x86,0x72,0x60,0x07,0x60,0x43,0x01,0x0b,0xb7,0xd0,0x0e, - 0x61,0x00,0x10,0x11,0x93,0x0b,0x73,0x1b,0x43,0x2b,0x9b,0xa3,0x61,0xc6,0xf6,0x16, - 0x46,0x37,0x37,0x41,0x20,0x3c,0x16,0x69,0x6e,0x73,0x74,0x73,0x13,0x04,0xe2,0x23, - 0x91,0xe6,0x46,0x37,0x37,0x41,0x20,0xc0,0x80,0xc6,0x5c,0xda,0xd9,0x17,0x1b,0xd9, - 0x04,0x81,0x08,0x03,0x1a,0x73,0x69,0x67,0x5f,0x73,0x74,0x44,0xe8,0xca,0xf0,0xbe, - 0xdc,0xde,0xe4,0xda,0x58,0xd4,0xa5,0xb9,0xd1,0xcd,0x6d,0x80,0xde,0xa1,0x0c,0xe0, - 0x21,0x1e,0xe4,0x61,0x1e,0xe8,0xa1,0x1e,0xec,0xe1,0x1e,0xf0,0xc1,0x0c,0xf2,0x21, - 0x1d,0xf4,0x41,0xaa,0xc2,0xc6,0x66,0xd7,0xe6,0x92,0x46,0x56,0xe6,0x46,0x37,0x25, - 0x08,0xaa,0x90,0xe1,0xb9,0xd8,0x95,0xc9,0xcd,0xa5,0xbd,0xb9,0x4d,0x09,0x88,0x26, - 0x64,0x78,0x2e,0x76,0x61,0x6c,0x76,0x65,0x72,0x53,0x82,0xa2,0x0e,0x19,0x9e,0xcb, - 0x1c,0x5a,0x18,0x59,0x99,0x5c,0xd3,0x1b,0x59,0x19,0xdb,0x94,0x00,0x29,0x43,0x86, - 0xe7,0x22,0x57,0x36,0xf7,0x56,0x27,0x37,0x56,0x36,0x37,0x25,0x00,0x83,0x4a,0x64, - 0x78,0x2e,0x74,0x79,0x70,0x65,0x41,0x6e,0x6e,0x6f,0x74,0x61,0x74,0x69,0x6f,0x6e, - 0x73,0x53,0x04,0x57,0x98,0x85,0x3a,0x64,0x78,0x2e,0x76,0x69,0x65,0x77,0x49,0x64, - 0x53,0x74,0x61,0x74,0x65,0x53,0x82,0x5a,0xa8,0x43,0x86,0xe7,0x52,0xe6,0x46,0x27, - 0x97,0x07,0xf5,0x96,0xe6,0x46,0x37,0x37,0x25,0x68,0x87,0x2e,0x64,0x78,0x2e,0x63, - 0x6f,0x75,0x6e,0x74,0x65,0x72,0x73,0x53,0x02,0x7d,0x00,0x00,0x79,0x18,0x00,0x00, - 0x51,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, - 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, - 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, - 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, - 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, - 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, - 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, - 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, - 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, - 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, - 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, - 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, - 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, - 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, - 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, - 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, - 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70, - 0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87, - 0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e, - 0xf0,0x30,0x83,0x81,0xc8,0x01,0x1f,0xdc,0x40,0x1c,0xe4,0xa1,0x1c,0xc2,0x61,0x1d, - 0xdc,0x40,0x1c,0xe4,0x01,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x27,0x00,0x00,0x00, - 0x66,0x00,0x0d,0x97,0xef,0x3c,0x7e,0x80,0x34,0x40,0x84,0xf9,0xc5,0x6d,0xdb,0xc1, - 0x36,0x5c,0xbe,0xf3,0xf8,0x42,0x40,0x15,0x05,0x11,0x95,0x0e,0x30,0x94,0x84,0x01, - 0x08,0x98,0x5f,0xdc,0xb6,0x25,0x48,0xc3,0xe5,0x3b,0x8f,0x2f,0x44,0x04,0x30,0x11, - 0x21,0xd0,0x0c,0x0b,0x61,0x04,0xce,0x70,0xf9,0xce,0xe3,0x0f,0xce,0x74,0xfb,0xc5, - 0x6d,0x5b,0xc0,0x34,0x5c,0xbe,0xf3,0xf8,0x8b,0x03,0x0c,0x62,0xf3,0x50,0x93,0x5f, - 0xdc,0xb6,0x0d,0x40,0xc3,0xe5,0x3b,0x8f,0x2f,0x01,0xcc,0xb3,0x10,0x7e,0x71,0xdb, - 0x26,0x50,0x0d,0x97,0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52,0xd3,0x43,0x4d,0x7e, - 0x71,0xdb,0x86,0x20,0x0d,0x97,0xef,0x3c,0xfe,0x44,0x44,0x13,0x02,0x44,0x98,0x5f, - 0xdc,0xb6,0x15,0x3c,0xc3,0xe5,0x3b,0x8f,0x4f,0x35,0x40,0x84,0xf9,0xc5,0x6d,0x1b, - 0x00,0x53,0x44,0x80,0xc1,0x10,0xcd,0x64,0x02,0x48,0x03,0x00,0x00,0x00,0x00,0x00, - 0x48,0x41,0x53,0x48,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0xa1,0x0b,0x24, - 0x89,0x0b,0x11,0x71,0x2e,0x64,0x7a,0x94,0xc7,0x0c,0x7c,0x84,0x44,0x58,0x49,0x4c, - 0xd4,0x14,0x00,0x00,0x60,0x00,0x00,0x00,0x35,0x05,0x00,0x00,0x44,0x58,0x49,0x4c, - 0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0xbc,0x14,0x00,0x00,0x42,0x43,0xc0,0xde, - 0x21,0x0c,0x00,0x00,0x2c,0x05,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00, - 0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39, - 0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x18,0x45,0x02, - 0x42,0x92,0x0b,0x42,0xc4,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x62,0x88, - 0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90, - 0x11,0x23,0xc4,0x50,0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x31,0x46,0x06, - 0x51,0x18,0x00,0x00,0x08,0x00,0x00,0x00,0x1b,0x8c,0xe0,0xff,0xff,0xff,0xff,0x07, - 0x40,0x02,0xa8,0x0d,0x84,0xf0,0xff,0xff,0xff,0xff,0x03,0x20,0x6d,0x30,0x86,0xff, - 0xff,0xff,0xff,0x1f,0x00,0x09,0xa8,0x00,0x49,0x18,0x00,0x00,0x03,0x00,0x00,0x00, - 0x13,0x82,0x60,0x42,0x20,0x4c,0x08,0x06,0x00,0x00,0x00,0x00,0x89,0x20,0x00,0x00, - 0x86,0x00,0x00,0x00,0x32,0x22,0x88,0x09,0x20,0x64,0x85,0x04,0x13,0x23,0xa4,0x84, - 0x04,0x13,0x23,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8c,0x8c,0x0b,0x84,0xc4,0x4c, - 0x10,0xc0,0xc1,0x08,0x40,0x09,0x00,0x0a,0x66,0x00,0xe6,0x08,0xc0,0x60,0x8e,0x00, - 0x29,0xc6,0x40,0x10,0x44,0x41,0x90,0x51,0x0c,0x80,0x20,0x88,0x62,0x20,0xe4,0xa6, - 0xe1,0xf2,0x27,0xec,0x21,0x24,0x7f,0x25,0xa4,0x95,0x98,0xfc,0xe2,0xb6,0x51,0x31, - 0x0c,0xc3,0x40,0x50,0x71,0xcf,0x70,0xf9,0x13,0xf6,0x10,0x92,0x1f,0x02,0xcd,0xb0, - 0x10,0x28,0x58,0x0a,0xa3,0x10,0x0c,0x33,0x0c,0xc3,0x40,0x10,0xc4,0x40,0x4d,0x41, - 0x06,0x62,0x18,0x86,0x61,0x18,0xe8,0x29,0xc3,0x40,0x0c,0x14,0x15,0x62,0x20,0x86, - 0x81,0xa6,0xa3,0x86,0xcb,0x9f,0xb0,0x87,0x90,0x7c,0x6e,0xa3,0x8a,0x95,0x98,0xfc, - 0xe2,0xb6,0x11,0x31,0x0c,0xc3,0x50,0x88,0x8a,0x60,0x08,0xb2,0x4a,0x31,0x10,0xc3, - 0x30,0x10,0x36,0x47,0x10,0x14,0x83,0x21,0x0a,0x82,0xd0,0x68,0x1b,0x08,0x18,0x46, - 0x20,0x86,0x99,0xda,0x60,0x1c,0xd8,0x21,0x1c,0xe6,0x61,0x1e,0xdc,0x80,0x16,0xca, - 0x01,0x1f,0xe8,0xa1,0x1e,0xe4,0xa1,0x1c,0xe4,0x80,0x14,0xf8,0xc0,0x1e,0xca,0x61, - 0x1c,0xe8,0xe1,0x1d,0xe4,0x81,0x0f,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x81,0x0d, - 0xc0,0x80,0x0e,0xfc,0x00,0x0c,0xfc,0x40,0x0f,0xf4,0xa0,0x1d,0xd2,0x01,0x1e,0xe6, - 0xe1,0x17,0xe8,0x21,0x1f,0xe0,0xa1,0x1c,0x50,0x40,0xcc,0x24,0x06,0xe3,0xc0,0x0e, - 0xe1,0x30,0x0f,0xf3,0xe0,0x06,0xb4,0x50,0x0e,0xf8,0x40,0x0f,0xf5,0x20,0x0f,0xe5, - 0x20,0x07,0xa4,0xc0,0x07,0xf6,0x50,0x0e,0xe3,0x40,0x0f,0xef,0x20,0x0f,0x7c,0x60, - 0x0e,0xec,0xf0,0x0e,0xe1,0x40,0x0f,0x6c,0x00,0x06,0x74,0xe0,0x07,0x60,0xe0,0x07, - 0x48,0xf0,0x3e,0x02,0x67,0x92,0x83,0x71,0x60,0x87,0x70,0x98,0x87,0x79,0x70,0x03, - 0x5a,0x28,0x07,0x7c,0xa0,0x87,0x7a,0x90,0x87,0x72,0x90,0x03,0x52,0x08,0x05,0x79, - 0x90,0x87,0x70,0xc8,0x07,0x3e,0xb0,0x87,0x72,0x18,0x07,0x7a,0x78,0x07,0x79,0xe0, - 0x03,0x73,0x60,0x87,0x77,0x08,0x07,0x7a,0x60,0x03,0x30,0xa0,0x03,0x3f,0x00,0x03, - 0x3f,0xd0,0x03,0x3d,0x68,0x87,0x74,0x80,0x87,0x79,0xf8,0x05,0x7a,0xc8,0x07,0x78, - 0x28,0x07,0x14,0x10,0x33,0x9d,0xc1,0x38,0xb0,0x43,0x38,0xcc,0xc3,0x3c,0xb8,0x01, - 0x2d,0x94,0x03,0x3e,0xd0,0x43,0x3d,0xc8,0x43,0x39,0xc8,0x01,0x29,0x84,0x82,0x3c, - 0xc8,0x43,0x38,0xe4,0x03,0x1f,0xd8,0x43,0x39,0x8c,0x03,0x3d,0xbc,0x83,0x3c,0xf0, - 0x81,0x39,0xb0,0xc3,0x3b,0x84,0x03,0x3d,0xb0,0x01,0x18,0xd0,0x81,0x1f,0x80,0x81, - 0x1f,0x20,0xc1,0x13,0x69,0x4c,0x02,0x2f,0x11,0xca,0x44,0xf0,0x8e,0x91,0xa6,0x88, - 0x12,0x26,0xbf,0x44,0x8c,0x63,0x42,0x84,0xe7,0x79,0x5e,0x22,0xa0,0x07,0x06,0x87, - 0x23,0x4d,0x0b,0x80,0x39,0xd4,0xe4,0x4f,0xd8,0x43,0xfc,0x5f,0x04,0x18,0x0c,0xd1, - 0x4c,0x6e,0x83,0x14,0x4e,0xc4,0x48,0x48,0xf1,0x3c,0xcf,0xf3,0x3c,0xcf,0x3c,0x53, - 0x52,0x6f,0x92,0xa6,0x88,0x12,0x26,0x9f,0x05,0x98,0x67,0x21,0x22,0x76,0x02,0x26, - 0x02,0x05,0x04,0xb1,0xc9,0x40,0x4c,0x01,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0, - 0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50, - 0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07, - 0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78, - 0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0, - 0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07, - 0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d, - 0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07, - 0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x86,0x3c,0x06,0x10,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x79,0x10,0x20,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0xf2,0x34, - 0x40,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0xe4,0x79,0x80,0x00,0x08, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xc8,0x13,0x01,0x01,0x10,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xc0,0x90,0x67,0x02,0x02,0x20,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x80,0x21,0x8f,0x05,0x04,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x43, - 0x1e,0x0c,0x08,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x86,0x3c,0x1b,0x10, - 0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x81,0x00,0x11,0x00,0x00,0x00, - 0x32,0x1e,0x98,0x14,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x22, - 0x4a,0x60,0x04,0xa0,0x24,0x8a,0xa1,0x08,0xca,0xa1,0x0c,0x0a,0x7c,0xa2,0x3c,0xa8, - 0x28,0x89,0x11,0x80,0x22,0x28,0x83,0x02,0x29,0x04,0x0a,0x67,0x00,0x88,0x9c,0x01, - 0xa0,0x75,0x06,0x80,0xda,0x19,0x00,0x72,0xc7,0x62,0x14,0xe2,0x38,0x0e,0xe0,0x38, - 0x0e,0xe0,0x79,0x00,0x79,0x18,0x00,0x00,0x6f,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90, - 0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c, - 0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c, - 0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x08,0x65,0x82,0x40,0x2c,0x1b,0x84,0x81, - 0x98,0x20,0x10,0xcc,0x06,0x61,0x30,0x28,0xc0,0xcd,0x4d,0x10,0x88,0x66,0xc3,0x80, - 0x24,0xc4,0x04,0x21,0x0c,0x32,0x02,0x13,0x04,0xc2,0xd9,0x20,0x10,0xc6,0x86,0x84, - 0x58,0x98,0x86,0x18,0x1a,0xc2,0x99,0x20,0x90,0x81,0x36,0x41,0x20,0x9e,0x0d,0xc9, - 0x00,0x31,0xcd,0x30,0x44,0x84,0xb3,0x41,0x78,0xa4,0x09,0xc2,0x1a,0x6c,0x13,0x04, - 0x02,0x9a,0x20,0x10,0xd1,0x06,0x84,0xa0,0x98,0x8a,0x18,0x2c,0x60,0x43,0x70,0x4d, - 0x10,0xda,0x80,0xdb,0x80,0x10,0x19,0xd3,0x10,0x03,0x01,0x6c,0x40,0x86,0x8c,0x69, - 0x86,0x81,0x00,0x36,0x08,0xda,0xb6,0x81,0x98,0x00,0x8c,0x9b,0x20,0xb8,0x41,0xb7, - 0x21,0xf0,0x26,0x08,0x02,0x40,0xc6,0x4c,0x2e,0xec,0xac,0xad,0xcc,0x8d,0xae,0x29, - 0x2c,0xcd,0x8d,0x08,0x55,0x11,0xd6,0xd0,0xd3,0x93,0x14,0xd1,0x04,0xa1,0x98,0x26, - 0x08,0x05,0xb5,0x21,0x20,0x26,0x08,0x45,0x35,0x41,0x28,0xac,0x0d,0x42,0x15,0x6d, - 0x58,0x08,0x31,0x18,0x03,0x32,0x28,0x03,0x33,0x18,0xce,0x80,0x20,0x03,0x34,0xd8, - 0x10,0x0c,0x1b,0x96,0x41,0x0c,0xc6,0x80,0x0c,0xd4,0xc0,0x0c,0x86,0x33,0x18,0xc8, - 0x00,0x0d,0x36,0x04,0xcd,0x06,0xa1,0xaa,0x36,0x2c,0x8d,0x18,0x8c,0x01,0x19,0xb0, - 0x81,0x19,0x0c,0x66,0xd0,0x90,0x41,0x1b,0x6c,0x18,0xd2,0x60,0x0d,0xdc,0x80,0xc9, - 0x94,0xd5,0x17,0x55,0x98,0xdc,0x59,0x19,0xdd,0x04,0xa1,0xb8,0x26,0x08,0x05,0x36, - 0x41,0x20,0xa4,0x0d,0x42,0x35,0x07,0x1b,0x16,0x02,0x0e,0xc6,0x20,0x0e,0xca,0x80, - 0x0c,0x06,0x39,0x20,0xc8,0x80,0x0e,0x36,0x04,0x75,0xb0,0x61,0x78,0x03,0x3b,0x00, - 0x36,0x14,0x60,0x10,0x06,0x77,0xd0,0x01,0x55,0xd8,0xd8,0xec,0xda,0x5c,0xd2,0xc8, - 0xca,0xdc,0xe8,0xa6,0x04,0x41,0x15,0x32,0x3c,0x17,0xbb,0x32,0xb9,0xb9,0xb4,0x37, - 0xb7,0x29,0x01,0xd1,0x84,0x0c,0xcf,0xc5,0x2e,0x8c,0xcd,0xae,0x4c,0x6e,0x4a,0x60, - 0xd4,0x21,0xc3,0x73,0x99,0x43,0x0b,0x23,0x2b,0x93,0x6b,0x7a,0x23,0x2b,0x63,0x9b, - 0x12,0x24,0x65,0xc8,0xf0,0x5c,0xe4,0xca,0xe6,0xde,0xea,0xe4,0xc6,0xca,0xe6,0xa6, - 0x04,0x5c,0x1d,0x32,0x3c,0x17,0xbb,0xb4,0xb2,0xbb,0x24,0xb2,0x29,0xba,0x30,0xba, - 0xb2,0x29,0x81,0x57,0x87,0x0c,0xcf,0xa5,0xcc,0x8d,0x4e,0x2e,0x0f,0xea,0x2d,0xcd, - 0x8d,0x6e,0x6e,0x4a,0x70,0x07,0x00,0x00,0x79,0x18,0x00,0x00,0x51,0x00,0x00,0x00, - 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, - 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, - 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, - 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, - 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, - 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, - 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, - 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, - 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, - 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, - 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, - 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, - 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, - 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, - 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, - 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, - 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87, - 0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a, - 0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e,0xf0,0x30,0x83,0x81, - 0xc8,0x01,0x1f,0xdc,0x40,0x1c,0xe4,0xa1,0x1c,0xc2,0x61,0x1d,0xdc,0x40,0x1c,0xe4, - 0x01,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x27,0x00,0x00,0x00,0x66,0x00,0x0d,0x97, - 0xef,0x3c,0x7e,0x80,0x34,0x40,0x84,0xf9,0xc5,0x6d,0xdb,0xc1,0x36,0x5c,0xbe,0xf3, - 0xf8,0x42,0x40,0x15,0x05,0x11,0x95,0x0e,0x30,0x94,0x84,0x01,0x08,0x98,0x5f,0xdc, - 0xb6,0x25,0x48,0xc3,0xe5,0x3b,0x8f,0x2f,0x44,0x04,0x30,0x11,0x21,0xd0,0x0c,0x0b, - 0x61,0x04,0xce,0x70,0xf9,0xce,0xe3,0x0f,0xce,0x74,0xfb,0xc5,0x6d,0x5b,0xc0,0x34, - 0x5c,0xbe,0xf3,0xf8,0x8b,0x03,0x0c,0x62,0xf3,0x50,0x93,0x5f,0xdc,0xb6,0x0d,0x40, - 0xc3,0xe5,0x3b,0x8f,0x2f,0x01,0xcc,0xb3,0x10,0x7e,0x71,0xdb,0x26,0x50,0x0d,0x97, - 0xef,0x3c,0xbe,0x34,0x39,0x11,0x81,0x52,0xd3,0x43,0x4d,0x7e,0x71,0xdb,0x86,0x20, - 0x0d,0x97,0xef,0x3c,0xfe,0x44,0x44,0x13,0x02,0x44,0x98,0x5f,0xdc,0xb6,0x15,0x3c, - 0xc3,0xe5,0x3b,0x8f,0x4f,0x35,0x40,0x84,0xf9,0xc5,0x6d,0x1b,0x00,0x53,0x44,0x80, - 0xc1,0x10,0xcd,0x64,0x02,0x48,0x03,0x00,0x61,0x20,0x00,0x00,0x39,0x03,0x00,0x00, - 0x13,0x04,0x5c,0x2c,0x10,0x00,0x00,0x00,0x3b,0x00,0x00,0x00,0xa4,0x8d,0x00,0x50, - 0x51,0x02,0x44,0x14,0xc4,0x28,0x03,0xb0,0x07,0xce,0x28,0x03,0xe0,0x06,0xca,0x28, - 0x03,0xb0,0x34,0xa3,0x0c,0x40,0x28,0x94,0xc1,0x28,0x03,0xb0,0x98,0x41,0x39,0x15, - 0xcc,0x28,0x03,0xb0,0x07,0x4a,0x79,0x8d,0x32,0x00,0x0b,0x34,0xca,0x00,0x2c,0xca, - 0x28,0x03,0xb0,0xe4,0xc1,0x28,0x03,0xb0,0x38,0xa3,0x0c,0xc0,0x1b,0x28,0xa3,0x0c, - 0x00,0x1c,0x28,0xa3,0x0c,0xc0,0xf2,0xcc,0x00,0x94,0x5d,0xe1,0x95,0x5b,0x21,0x14, - 0x57,0xc9,0x95,0x4c,0x29,0x94,0xd1,0x28,0x03,0xb0,0xa4,0x41,0x21,0x8d,0x32,0x00, - 0x4b,0x1d,0x94,0x4b,0xa9,0x8c,0x32,0x00,0x7b,0x00,0x8d,0x32,0x04,0x8b,0x42,0xc3, - 0x18,0x41,0x1e,0x8f,0xa7,0xef,0x8d,0x11,0xb4,0xe6,0x9c,0x93,0xdf,0x18,0x01,0x08, - 0x82,0xa0,0x0a,0x06,0x63,0x04,0x20,0x08,0x82,0x30,0x18,0x8c,0x11,0xdc,0x78,0x3b, - 0xb6,0xdc,0x28,0x43,0xb0,0x0a,0xaa,0x30,0x46,0xa0,0xb3,0xe6,0x1c,0x7f,0x63,0x04, - 0x20,0x08,0x82,0x24,0x18,0x8c,0x11,0x80,0x20,0x08,0xe2,0x61,0x30,0x46,0x00,0x82, - 0x20,0x88,0x82,0xc1,0x28,0x43,0x00,0x0b,0xae,0x30,0xca,0x10,0xc0,0xc2,0x2c,0x8c, - 0x11,0xa8,0x78,0x7d,0xca,0xde,0x18,0x01,0x08,0x82,0x20,0x08,0x06,0x63,0x04,0x20, - 0x08,0x82,0x20,0x28,0x8c,0x11,0x80,0x20,0x08,0xc2,0xdf,0x08,0xc0,0x18,0x01,0x08, - 0x82,0x20,0xfe,0xcd,0x00,0x00,0x00,0x00,0x23,0x06,0x09,0x00,0x82,0x60,0xb0,0xb1, - 0x82,0x17,0x0a,0xab,0xb0,0x0a,0x77,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x5b,0x2b, - 0x7c,0xa2,0xb0,0x0a,0xab,0x80,0x07,0x23,0x06,0x09,0x00,0x82,0x60,0xb0,0xb9,0x02, - 0x18,0x84,0x42,0x2b,0xb4,0x42,0x1e,0x8c,0x18,0x24,0x00,0x08,0x82,0xc1,0xf6,0x0a, - 0x61,0x20,0x0a,0xad,0xd0,0x0a,0x7a,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x1b,0x2c, - 0x88,0x01,0x29,0xb8,0x82,0x2b,0xec,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18,0xb9, - 0x50,0x06,0xac,0xf0,0x0a,0xa6,0x90,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x60,0xe8, - 0x82,0x19,0xb4,0x02,0x2c,0xf0,0x81,0x1a,0x8c,0x18,0x24,0x00,0x08,0x82,0x81,0xb1, - 0x0b,0x67,0x20,0x0b,0xb1,0x80,0x0a,0x6b,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x06, - 0x2f,0xa0,0xc1,0x2c,0xc8,0x82,0x1f,0xb0,0xc1,0x88,0x41,0x02,0x80,0x20,0x18,0x18, - 0xbd,0x90,0x06,0xb4,0x30,0x0b,0xa9,0xd0,0x06,0x23,0x06,0x09,0x00,0x82,0x60,0x60, - 0xf8,0x82,0x1a,0xd0,0x02,0x2d,0xac,0x82,0x1b,0x8c,0x18,0x24,0x00,0x08,0x82,0x81, - 0xf1,0x0b,0x6b,0x50,0x0b,0xb5,0x10,0x0a,0x6f,0x30,0x62,0x90,0x00,0x20,0x08,0x06, - 0x06,0x38,0xb0,0x81,0x2d,0xd8,0x02,0x2b,0xc0,0xc1,0x88,0xc1,0x01,0x80,0x20,0x18, - 0x58,0xbb,0x00,0x07,0x49,0x19,0x8c,0x26,0x04,0xc3,0x70,0x44,0x30,0x09,0xdf,0x88, - 0xc1,0x01,0x80,0x20,0x18,0x58,0xbe,0x30,0x07,0x0c,0x1e,0x8c,0x26,0x04,0xc0,0x68, - 0x82,0x10,0x8c,0x26,0x0c,0xc2,0x68,0x02,0x31,0xcc,0x32,0x04,0x82,0x31,0x62,0xf0, - 0x00,0x20,0x08,0x06,0xcd,0x39,0xdc,0x01,0x25,0x41,0x8f,0x65,0xfd,0xc2,0x2f,0xe4, - 0x81,0x35,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0xa3,0x09,0xc4,0x60, - 0x44,0x22,0x1f,0x23,0x12,0xf9,0x18,0x91,0xc8,0xc7,0x88,0x44,0x3e,0xb3,0x04,0xc2, - 0x40,0xc5,0x80,0x04,0x1a,0x30,0x50,0x31,0x20,0x81,0x06,0x0c,0x54,0x0c,0x48,0xa0, - 0x01,0x03,0x15,0x03,0x12,0x68,0xc0,0x68,0x42,0x25,0x0c,0x47,0x04,0x67,0x20,0x7c, - 0xb3,0x0c,0xdb,0x10,0x8c,0x18,0x2c,0x00,0x08,0x82,0xc1,0x53,0x0f,0xa5,0xd0,0x71, - 0x5b,0xc7,0x6d,0x23,0x06,0x06,0x00,0x82,0x60,0x10,0xd5,0x83,0x28,0x04,0x16,0x7c, - 0xf2,0x31,0xe1,0x93,0x8f,0x0d,0x9f,0x7c,0x46,0x0c,0x0e,0x00,0x04,0xc1,0xc0,0xba, - 0x07,0x56,0x28,0x83,0x79,0x18,0x4d,0x08,0x80,0xd1,0x04,0x21,0x18,0x4d,0x18,0x04, - 0x1b,0xc6,0x20,0x3e,0x36,0x8c,0x41,0x7c,0x6c,0x18,0x83,0xf8,0x8c,0x18,0x2c,0x00, - 0x08,0x82,0xc1,0x13,0x12,0xb1,0x30,0x08,0xc1,0x20,0x04,0x23,0x06,0x06,0x00,0x82, - 0x60,0x10,0x85,0x84,0x2b,0x04,0x56,0x04,0xf2,0xb1,0x42,0x90,0x8f,0x15,0x83,0x7c, - 0x46,0x13,0xd0,0x00,0xb0,0x60,0x14,0xe4,0x63,0x79,0x10,0xc4,0xc7,0xb4,0x40,0x3e, - 0xa6,0x09,0xf2,0x31,0x6d,0x90,0x8f,0x19,0xa1,0x20,0x1f,0x0b,0xfc,0x00,0x3e,0xf6, - 0x99,0x02,0x7c,0xec,0x3b,0x05,0xf8,0xd8,0x87,0x0a,0xf0,0xb1,0x61,0x91,0x8f,0x0d, - 0x8c,0x7c,0x6c,0x68,0xe4,0x63,0x83,0x2a,0xc0,0xc7,0x86,0x55,0x80,0x8f,0x0d,0xac, - 0x00,0x9f,0xd1,0x84,0x3c,0x08,0x2c,0x08,0xe4,0x63,0x81,0x2d,0xc8,0xc7,0x02,0x5a, - 0x80,0xcf,0x88,0xc1,0x01,0x80,0x20,0x18,0x58,0x38,0xd1,0x0e,0xa6,0x30,0x0f,0xa3, - 0x09,0x01,0x70,0x01,0x41,0x23,0x06,0x07,0x00,0x82,0x60,0x60,0xed,0x04,0x3c,0xa4, - 0xc2,0x3d,0x8c,0x26,0x04,0xc0,0x68,0x82,0x10,0x8c,0x26,0x0c,0x82,0xcd,0x41,0x25, - 0x1f,0x0b,0x08,0xf9,0x18,0x1d,0x5c,0xf2,0xb1,0xa0,0x90,0x8f,0xd5,0x41,0x26,0x1f, - 0x0b,0x0c,0xf9,0x8c,0x18,0x1c,0x00,0x08,0x82,0x81,0x55,0x16,0xfa,0x30,0x0b,0x22, - 0x31,0x9a,0x10,0x00,0xa3,0x09,0x42,0x30,0x9a,0x30,0x08,0x96,0x0c,0xf0,0x31,0x64, - 0x80,0x8f,0x1d,0x03,0x7c,0x66,0x09,0x88,0x81,0x0a,0x02,0x56,0x86,0xdc,0xca,0x06, - 0x2a,0x06,0x64,0x88,0xad,0x6c,0xa0,0x62,0x40,0x86,0xd8,0xca,0x06,0x2a,0x06,0x64, - 0x88,0xad,0x6c,0xb8,0x81,0xb0,0xd0,0x60,0x96,0xa1,0xc8,0x82,0x2a,0xd4,0xe2,0x2c, - 0xf0,0x07,0x18,0x8c,0x18,0x1c,0x00,0x08,0x82,0x81,0x35,0x17,0x28,0x11,0x0e,0xc1, - 0x68,0x42,0x30,0x0c,0x47,0x04,0xea,0x10,0x7c,0x56,0xd4,0x04,0x0c,0x46,0x0c,0x0e, - 0x00,0x04,0xc1,0xc0,0xba,0x0b,0x96,0x28,0x87,0x60,0x96,0xc1,0x38,0x86,0xd1,0x84, - 0x00,0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x30,0x79,0x18,0xe2,0x63,0xf3,0x30,0xc4, - 0xc7,0xe8,0x61,0x88,0xcf,0x88,0xc1,0x02,0x80,0x20,0x18,0x3c,0xa1,0x11,0x13,0x83, - 0x10,0x0c,0x42,0x30,0x62,0x60,0x00,0x20,0x08,0x06,0x51,0x68,0xb8,0x44,0x60,0x41, - 0x21,0x1f,0x13,0x0a,0xf9,0xd8,0x50,0xc8,0x67,0x96,0x60,0x19,0x4d,0x80,0x80,0xd1, - 0x84,0x28,0x18,0x4d,0x90,0x04,0x1b,0xdc,0x21,0x3e,0x36,0xb8,0x43,0x7c,0x6c,0x70, - 0x87,0xf8,0xd8,0x30,0xc8,0xc7,0x86,0x41,0x3e,0x26,0x04,0xf0,0x31,0x82,0x90,0x8f, - 0x09,0x01,0x7c,0x46,0x0c,0x0c,0x00,0x04,0xc1,0x20,0x82,0x0d,0xb7,0x08,0x46,0x0c, - 0x0e,0x00,0x04,0xc1,0x60,0x82,0x0d,0x9e,0x08,0x60,0xc2,0x94,0x80,0x3e,0xa6,0x08, - 0xf4,0x31,0x65,0xa0,0x8f,0x15,0x85,0x7c,0x2c,0x40,0x09,0xf8,0x58,0x4a,0x04,0xf4, - 0x19,0x4d,0xf8,0x86,0xe1,0x88,0x80,0x25,0x84,0x6f,0x96,0x01,0x49,0x02,0x53,0x04, - 0xfa,0x8c,0x18,0x18,0x00,0x08,0x82,0x41,0xb4,0x1b,0x62,0x11,0x58,0x60,0x13,0xf2, - 0x19,0x31,0x30,0x00,0x10,0x04,0x83,0xa8,0x37,0xc6,0x22,0x30,0x98,0x08,0xe2,0x33, - 0x62,0x70,0x00,0x20,0x08,0x06,0x93,0x6f,0xa8,0x45,0x20,0x13,0x23,0x06,0x07,0x00, - 0x82,0x60,0x30,0xfd,0x46,0x5a,0x04,0x32,0x61,0x81,0x22,0x9f,0x59,0x82,0x64,0xa0, - 0x62,0x10,0x10,0xeb,0x18,0x8e,0x68,0x83,0x9c,0x08,0xbe,0x59,0x06,0x65,0x09,0x46, - 0x13,0xd6,0x00,0x18,0x4d,0x60,0x83,0x60,0x34,0xa1,0x0d,0x84,0x11,0x83,0x05,0x00, - 0x41,0x30,0x78,0xd0,0x03,0x2f,0x06,0x21,0x18,0x84,0x60,0xc4,0xc0,0x00,0x40,0x10, - 0x0c,0x22,0xf4,0xa8,0x8b,0xc0,0x82,0x42,0x3e,0x26,0x14,0xf2,0xb1,0xa1,0x90,0x8f, - 0x85,0x85,0x16,0x1f,0x13,0x0b,0x2d,0x3e,0x36,0x16,0x5a,0x7c,0x46,0x0c,0x16,0x00, - 0x04,0xc1,0xe0,0x81,0x0f,0xd0,0x18,0x84,0xc0,0x28,0x08,0xeb,0x83,0xba,0x80,0xc1, - 0x88,0xc1,0x01,0x80,0x20,0x18,0x58,0xef,0x41,0x1a,0x3d,0x11,0x8c,0x26,0x04,0xc0, - 0x68,0x82,0x10,0x98,0x10,0xc4,0xc7,0x0c,0x21,0x3e,0x16,0x08,0xf4,0x19,0x31,0x30, - 0x00,0x10,0x04,0x83,0xe8,0x3e,0xda,0x23,0xb0,0xc0,0x2d,0xe4,0x63,0x75,0x11,0xc4, - 0xc7,0x86,0x41,0x3e,0x16,0x64,0xf2,0xb1,0x60,0x90,0xcf,0x2c,0xc1,0x32,0xd0,0x31, - 0xd8,0x83,0x61,0x0b,0x89,0x2d,0x28,0x03,0x1d,0x83,0x3d,0x18,0xb6,0x90,0xd8,0x82, - 0x32,0xd0,0x31,0xd8,0x83,0x61,0x0b,0x89,0x2d,0x28,0x03,0x1d,0x03,0xb9,0x18,0x7c, - 0x90,0x20,0x8a,0xb9,0x82,0x69,0xc0,0x60,0xc4,0xe0,0x00,0x40,0x10,0x0c,0x2c,0x10, - 0xa9,0x0d,0xb7,0x08,0x46,0x13,0x02,0x61,0x38,0x22,0xc0,0x0b,0xe1,0x9b,0x65,0x60, - 0xb0,0xe0,0x04,0x82,0x2a,0x38,0x0f,0x18,0x31,0x38,0x00,0x10,0x04,0x03,0xab,0x44, - 0x74,0x63,0x2e,0x82,0xd1,0x84,0x00,0x18,0x8e,0x08,0xfe,0x22,0xf8,0x46,0x0c,0x0e, - 0x00,0x04,0xc1,0xc0,0x42,0x91,0xde,0xb0,0x0b,0xf6,0x18,0x4d,0x08,0x00,0x0b,0x58, - 0x43,0x3e,0x16,0xdc,0x84,0x7c,0x4c,0xb8,0x09,0xf9,0xd8,0x70,0x13,0xf2,0x99,0x65, - 0x68,0xaa,0x63,0x34,0x61,0x17,0x80,0xd1,0x04,0x5e,0x08,0x46,0x13,0x7a,0x41,0xb0, - 0xbc,0x18,0xe2,0x63,0x47,0x00,0x1f,0xd3,0x0b,0x22,0x3e,0x86,0x04,0xf0,0xb1,0xbd, - 0x28,0xe2,0x63,0x49,0x00,0x9f,0x11,0x03,0x03,0x00,0x41,0x30,0x88,0x72,0x24,0x46, - 0x8a,0x11,0x03,0x03,0x00,0x41,0x30,0x88,0x74,0x44,0x46,0x88,0x11,0x03,0x03,0x00, - 0x41,0x30,0x88,0x76,0x64,0x46,0x86,0xe1,0x88,0x41,0x18,0xbe,0xe1,0x08,0x42,0x18, - 0xbe,0x12,0x02,0x9d,0x65,0x70,0x9e,0x60,0x38,0x62,0x91,0x0d,0xe1,0x9b,0x6e,0x98, - 0x0d,0xd9,0x08,0xa6,0x1b,0x66,0x23,0x37,0x84,0x59,0x82,0x69,0x38,0x02,0x39,0x86, - 0x6f,0x96,0x01,0x8a,0x82,0xe1,0x88,0xc6,0x36,0x84,0x6f,0xba,0x41,0x37,0xc0,0x23, - 0x98,0x6e,0xe8,0x8d,0xdb,0x10,0xa6,0x1b,0x70,0xc3,0x37,0x86,0x59,0x02,0x69,0x38, - 0xe2,0xd1,0x0d,0xe1,0x9b,0x6e,0x10,0x8f,0xf4,0x08,0xa6,0x1b,0x76,0x23,0x3c,0x84, - 0x59,0x02,0x69,0xa0,0x62,0x60,0x20,0x22,0x1a,0xa8,0x18,0xf8,0x06,0xd2,0x9b,0x68, - 0xa0,0x62,0x70,0x20,0xbf,0x89,0x06,0x2a,0x06,0x07,0x02,0x9d,0x68,0xa0,0x62,0x10, - 0x1d,0x48,0x89,0xa6,0x1b,0x2a,0xaa,0x99,0x25,0x98,0x06,0x2a,0x06,0x30,0x70,0x18, - 0x69,0xa0,0x62,0x80,0x03,0x87,0x90,0x06,0x2a,0x06,0xd4,0x71,0x1c,0x69,0xa0,0x62, - 0x60,0x1d,0xc7,0x91,0x06,0x2a,0x06,0x33,0x70,0x5c,0x47,0x1a,0xa8,0x18,0x60,0xc7, - 0x81,0xa4,0x81,0x8a,0x41,0x76,0x1c,0x48,0x32,0x81,0x90,0x8f,0x09,0x86,0x7c,0x4c, - 0x08,0xe2,0x63,0x87,0x21,0x1f,0x83,0x8f,0x20,0x3e,0x86,0x28,0xf2,0x19,0x31,0x58, - 0x00,0x10,0x04,0x83,0xc7,0x4f,0x5c,0x24,0x0d,0xce,0xa0,0x0c,0x08,0x21,0xb0,0x40, - 0x3e,0xe4,0x63,0x81,0x43,0x1f,0x0b,0xe8,0x03,0x3e,0x23,0x06,0x0b,0x00,0x82,0x60, - 0xf0,0x88,0x8a,0x8c,0xb4,0xc1,0x1a,0xa4,0x01,0x7d,0x3c,0x8e,0x05,0xf6,0x21,0x1f, - 0x0b,0x24,0xfa,0x18,0x7e,0x04,0xf1,0x19,0x4d,0xf0,0x83,0x61,0x34,0xe1,0x0f,0x04, - 0x13,0x82,0xf8,0xd8,0x10,0xd0,0xc7,0x88,0x41,0x3e,0x96,0x0d,0xf2,0x31,0x21,0xa0, - 0x8f,0x11,0x41,0x7c,0x4c,0x14,0x46,0x44,0x3e,0x26,0x04,0xf1,0x19,0x8e,0x08,0x42, - 0x44,0xf8,0x66,0x19,0x2e,0x2a,0x18,0x4d,0x48,0x85,0xc0,0xc2,0x20,0x80,0xcf,0x68, - 0x02,0x2a,0x04,0x23,0x06,0x0f,0x00,0x82,0x60,0xd0,0xd4,0x4a,0x99,0x8c,0x48,0x88, - 0x4c,0x8f,0x40,0x22,0xad,0xd2,0x2a,0x67,0x42,0x22,0xa3,0x09,0x01,0x30,0x1c,0x11, - 0x1c,0xc3,0x77,0xc1,0x50,0x76,0x15,0xf0,0x19,0x31,0x78,0x00,0x10,0x04,0x83,0x46, - 0x57,0xd4,0x04,0x45,0x4c,0x24,0xa0,0x8e,0x14,0x91,0x15,0x59,0x61,0x93,0x14,0x19, - 0x4d,0x08,0x80,0xe1,0x88,0x80,0x19,0xbe,0x0b,0x86,0xb2,0xc0,0x80,0x8f,0x75,0x4b, - 0x7c,0x46,0x0c,0x1e,0x00,0x04,0xc1,0xa0,0x01,0x17,0x38,0x71,0x11,0x16,0x09,0xb4, - 0xe6,0x45,0x70,0x05,0x57,0xe4,0xe4,0x45,0x46,0x13,0x02,0x60,0x38,0x22,0x90,0x86, - 0xef,0x82,0xa1,0xcc,0x08,0xe0,0x63,0x5f,0x04,0x9f,0x11,0x83,0x07,0x00,0x41,0x30, - 0x68,0xcc,0xc5,0x4e,0x68,0x44,0x46,0xc8,0x20,0x98,0x6a,0xc4,0x57,0x7c,0x05,0x4f, - 0x6a,0x64,0x34,0x21,0x00,0x86,0x23,0x02,0x6c,0xf8,0x2e,0x18,0xca,0x8c,0x00,0x3e, - 0x56,0x06,0x57,0x7c,0x46,0x0c,0x1e,0x00,0x04,0xc1,0xa0,0x61,0x17,0x3e,0xd1,0x11, - 0x1c,0x51,0x83,0x20,0xdb,0x11,0x72,0x21,0x17,0x3f,0xd9,0x91,0xd1,0x84,0x00,0x18, - 0x8e,0x08,0xbc,0xe1,0xbb,0x60,0x28,0x33,0x02,0xf8,0x58,0x10,0x27,0xf2,0x99,0x25, - 0xb8,0x6c,0x1d,0xe8,0x04,0x04,0x46,0x0e,0x37,0x02,0x1f,0x23,0x87,0x1b,0x81,0x8f, - 0x91,0xc3,0x8d,0xc0,0xa7,0x88,0x75,0xb9,0x0a,0x54,0x05,0x46,0x0c,0x0e,0x00,0x04, - 0xc1,0xc0,0xa2,0x97,0x54,0x11,0x93,0x60,0x34,0x21,0x00,0x46,0x13,0x84,0x60,0x34, - 0x61,0x10,0x46,0x13,0x88,0xa1,0x8e,0x58,0x81,0x11,0x83,0x03,0x00,0x41,0x30,0xb0, - 0xf4,0xe5,0x55,0xd0,0x24,0x18,0x4d,0x08,0x80,0xd1,0x04,0x21,0x18,0x4d,0x18,0x84, - 0xd1,0x04,0x62,0xa8,0x06,0x57,0x60,0xc4,0xe0,0x00,0x40,0x10,0x0c,0x2c,0x90,0xa9, - 0x15,0x37,0x09,0x46,0x13,0x02,0x60,0x34,0x41,0x08,0x46,0x13,0x06,0x61,0x34,0x81, - 0x18,0x6a,0x52,0x17,0x18,0x31,0x38,0x00,0x10,0x04,0x03,0xcb,0x64,0x76,0x85,0x4e, - 0x82,0xd1,0x84,0x00,0x18,0x4d,0x10,0x82,0xd1,0x84,0x41,0x18,0x4d,0x20,0x06,0xb3, - 0x38,0xf9,0x8c,0x18,0x20,0x00,0x08,0x82,0x01,0xb6,0x32,0xbd,0x12,0x71,0xc1,0x88, - 0x01,0x02,0x80,0x20,0x18,0x60,0x2c,0xe3,0x2b,0x0c,0x17,0x58,0x70,0x40,0xc7,0x32, - 0x30,0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x60,0x2f,0x13,0x2e,0x14,0x18,0x04, - 0x23,0x06,0x08,0x00,0x82,0x60,0x80,0xc1,0x8c,0xb8,0x3c,0x60,0x10,0x58,0xa0,0x40, - 0xc7,0x38,0x32,0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x60,0x33,0x53,0x2e,0x17, - 0x19,0x04,0x23,0x06,0x08,0x00,0x82,0x60,0x80,0xd1,0x8c,0xb9,0x48,0x64,0x10,0x58, - 0xd0,0x40,0xc7,0x3e,0x34,0x90,0xcf,0x88,0x01,0x02,0x80,0x20,0x18,0x60,0x37,0x93, - 0x2e,0x1a,0x1a,0x04,0x23,0x06,0x08,0x00,0x82,0x60,0x80,0xe1,0x8c,0xba,0x54,0x68, - 0x10,0x58,0x00,0x41,0xc7,0x9a,0x80,0x3e,0xa6,0x08,0xf4,0xb1,0x63,0xa0,0x8f,0x0d, - 0xb4,0x22,0x1f,0x0b,0x6a,0x05,0x3e,0x46,0xd8,0x8a,0x7c,0xec,0x56,0x82,0xf8,0x8c, - 0x18,0x2c,0x00,0x08,0x82,0xc1,0x53,0x36,0xf5,0xe2,0x26,0x6d,0xc2,0x26,0x67,0x61, - 0x16,0x65,0x31,0x62,0x70,0x00,0x20,0x08,0x06,0x13,0xd9,0xc0,0x4b,0x80,0x2b,0x23, - 0x06,0x07,0x00,0x82,0x60,0x30,0x95,0xcd,0xbb,0x04,0xb8,0x62,0xb9,0x12,0xc4,0xc7, - 0x02,0x5f,0x91,0x8f,0x05,0xbb,0x02,0x1f,0x0b,0x7e,0x42,0x3e,0xc6,0x04,0xf1,0x19, - 0x8e,0x58,0x7e,0x85,0xf8,0x86,0x23,0x98,0x5f,0x11,0xbe,0x12,0x82,0x1d,0x8e,0x60, - 0xc4,0x85,0xf8,0x4a,0x08,0x76,0x38,0xc2,0x19,0x17,0xe1,0xab,0x40,0xd8,0xe1,0x08, - 0xa4,0x5c,0x84,0xaf,0x84,0x60,0x67,0x19,0x2e,0x2b,0x18,0x4d,0x58,0x8b,0xc0,0xaa, - 0x20,0x3e,0x46,0x09,0xf1,0x19,0x4d,0x88,0x8b,0x60,0xc4,0xe0,0x01,0x40,0x10,0x0c, - 0x9a,0xbd,0x59,0x99,0x74,0x39,0x97,0x41,0x08,0xd4,0x65,0x6e,0xe6,0xa6,0x65,0xd4, - 0x65,0x34,0x21,0x00,0x86,0x23,0x02,0x68,0xf8,0x2e,0x18,0x6a,0xc4,0xe0,0x01,0x40, - 0x10,0x0c,0x9a,0xbf,0x79,0x99,0x76,0x59,0x17,0xce,0x28,0xdc,0xe5,0x6e,0xee,0x26, - 0x66,0xdc,0x65,0x34,0x21,0x00,0x86,0x23,0x02,0x6a,0xf8,0x2e,0x18,0xca,0x82,0x02, - 0x3e,0x16,0x06,0x0d,0x7c,0x46,0x0c,0x1e,0x00,0x04,0xc1,0xa0,0x29,0x9d,0x9a,0x99, - 0x97,0x78,0x09,0x98,0x85,0x5e,0xfa,0xa6,0x6f,0x6e,0x86,0x5e,0x46,0x13,0x02,0x60, - 0x38,0x22,0xd0,0x86,0xef,0x82,0xa1,0xcc,0x08,0xe0,0x33,0x62,0xf0,0x00,0x20,0x08, - 0x06,0x8d,0xea,0xe8,0x0c,0xbe,0xd8,0x8b,0x54,0x06,0x50,0xbe,0x88,0x8e,0xe8,0xf0, - 0x4c,0xbe,0x8c,0x26,0x04,0xc0,0x70,0x44,0xf0,0x0d,0xdf,0x05,0x43,0x59,0x11,0xc0, - 0x67,0xc4,0xe0,0x01,0x40,0x10,0x0c,0x9a,0xd7,0xf9,0x99,0x7e,0xd9,0x17,0x36,0x50, - 0x83,0xca,0x5f,0x4e,0xe7,0x74,0xc2,0xc6,0x5f,0x46,0x13,0x02,0x60,0x38,0x22,0x20, - 0x83,0xe1,0xbb,0x60,0x28,0x2b,0x02,0xf8,0x8c,0x18,0x3c,0x00,0x08,0x82,0x41,0x43, - 0x3b,0x64,0x23,0x32,0x20,0x03,0xbd,0x81,0x36,0x32,0xac,0xc3,0x3a,0x66,0x33,0x32, - 0xa3,0x09,0x01,0x30,0x1c,0x11,0xa4,0xc1,0xf0,0x5d,0x30,0x94,0x15,0x01,0x7c,0x8c, - 0x0e,0xc4,0x00,0x3e,0x23,0x06,0x0f,0x00,0x82,0x60,0xd0,0xe8,0x8e,0xda,0xa0,0x8c, - 0xc9,0x88,0x41,0x00,0x06,0x29,0x23,0x3b,0xb2,0xc3,0x36,0x29,0x33,0x9a,0x10,0x00, - 0xc3,0x11,0xc1,0x1b,0x0c,0xdf,0x05,0x43,0x99,0x11,0xc0,0x67,0xc4,0xe0,0x01,0x40, - 0x10,0x0c,0x9a,0xdf,0x79,0x9b,0x96,0x59,0x19,0x3e,0x30,0xca,0xc0,0x65,0x6e,0xe7, - 0x76,0xe2,0xc6,0x65,0x46,0x13,0x02,0x60,0x38,0x22,0xa0,0x83,0xe1,0xbb,0x60,0x28, - 0x2b,0x02,0xf8,0x8c,0x18,0x3c,0x00,0x08,0x82,0x41,0x43,0x3e,0x74,0x23,0x33,0x30, - 0x03,0x06,0x8b,0x1a,0xcc,0x0c,0xef,0xf0,0x8e,0xdd,0xcc,0xcc,0x68,0x42,0x00,0x0c, - 0x47,0x04,0x79,0x30,0x7c,0x17,0x0c,0x65,0x45,0x00,0x1f,0x0b,0xd4,0x46,0x3e,0xb3, - 0x04,0xd7,0x40,0xc9,0x20,0x58,0x74,0x56,0xc9,0x07,0x45,0x67,0x93,0x05,0xe9,0x21, - 0x9f,0x59,0x02,0x6c,0xa0,0x62,0x10,0x2e,0xda,0x59,0x46,0x0c,0x16,0x00,0x04,0xc1, - 0xe0,0x61,0x1f,0xbe,0xa9,0x17,0x7a,0x99,0x17,0xf7,0x68,0x0f,0xf6,0x18,0x31,0x38, - 0x00,0x10,0x04,0x83,0x69,0x7d,0xee,0x26,0xf8,0x99,0x11,0x83,0x03,0x00,0x41,0x30, - 0x98,0xd8,0xc7,0x6e,0x82,0x9f,0xb1,0xf8,0x60,0x17,0xf8,0x58,0x7c,0xb0,0x0b,0x7c, - 0x2c,0x3e,0xd8,0x05,0x3e,0x23,0x06,0x0b,0x00,0x82,0x60,0xf0,0xc8,0x8f,0xe8,0x0c, - 0x42,0x30,0x08,0xc1,0x88,0x81,0x01,0x80,0x20,0x18,0x44,0xf2,0xf3,0x37,0x81,0x05, - 0x85,0x7c,0x4c,0x28,0xe4,0x63,0x43,0x21,0x9f,0x11,0x83,0x05,0x00,0x41,0x30,0x78, - 0xee,0xe7,0x74,0x40,0xe6,0x5f,0xfc,0x65,0x10,0x82,0x11,0x83,0x03,0x00,0x41,0x30, - 0x98,0xec,0x47,0x74,0x02,0xb5,0x19,0x31,0x38,0x00,0x10,0x04,0x83,0xe9,0x7e,0x42, - 0x27,0x50,0x9b,0x11,0x03,0x03,0x00,0x41,0x30,0x88,0xf2,0x07,0x74,0x02,0x0b,0xc2, - 0x45,0x3e,0x23,0x06,0x06,0x00,0x82,0x60,0x10,0xed,0x4f,0xe8,0x04,0x23,0x06,0x07, - 0x00,0x82,0x60,0x60,0xe9,0xcf,0xeb,0xa0,0x0d,0xe9,0x8c,0x26,0x04,0x81,0x05,0x72, - 0x23,0x1f,0x8b,0x9b,0x20,0x3e,0x56,0xd0,0x8b,0x7c,0x2c,0x10,0xe4,0x63,0xc1,0xbb, - 0xc8,0xc7,0x84,0x77,0x91,0x8f,0x0d,0xef,0x22,0x1f,0x1b,0xfa,0x05,0x3e,0x36,0xf4, - 0x0b,0x7c,0x6c,0xe8,0x17,0xf8,0x58,0xad,0xb0,0x0f,0x0c,0x46,0x0c,0x0e,0x00,0x04, - 0xc1,0xc0,0x3a,0x21,0xde,0xa9,0x9b,0x60,0x34,0x21,0x00,0x46,0x13,0x84,0x60,0x34, - 0x61,0x10,0x0c,0x0c,0xc6,0x40,0x3e,0x46,0x04,0xf2,0xb1,0x40,0x91,0x8f,0x15,0x83, - 0x7c,0x2c,0x58,0xe4,0x63,0x46,0x21,0x1f,0x0b,0x18,0xf9,0x58,0x51,0x2e,0xf0,0x31, - 0xa2,0x5c,0xe0,0x63,0x43,0xb9,0xc0,0x67,0x96,0x20,0x1b,0xa8,0x18,0x0c,0x0c,0xb6, - 0x88,0x81,0x8a,0xc1,0xc0,0x60,0x8b,0x18,0xa8,0x18,0x0c,0x0c,0xb6,0x08,0x63,0x97, - 0x1a,0x82,0xc1,0x70,0x43,0x00,0x42,0x60,0x30,0xcb,0xa0,0x11,0xc1,0x2c,0xc1,0x36, - 0x50,0x31,0xf0,0x9a,0xa0,0x68,0x03,0x15,0x03,0xaf,0x09,0x8a,0x36,0x50,0x31,0xf0, - 0x9a,0xa0,0x68,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0x88,0xd1,0xfa,0xe4,0x50,0x0e, - 0xc1,0xd0,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x18,0xad,0x4f,0x0e,0xe5,0x50, - 0x09,0x09,0x23,0x06,0x09,0x00,0x82,0x60,0x80,0x88,0xd1,0xfa,0xe4,0x50,0x0e,0xbd, - 0x50,0x30,0x62,0x90,0x00,0x20,0x08,0x06,0x88,0x18,0xad,0x4f,0x0e,0xe5,0x90,0x0b, - 0xb9,0x0d,0x02,0x00,0x00,0x00,0x00,0x00, -}; - -static const unsigned char _fragmentMainMSL[] = { - 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, - 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, - 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, - 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, - 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x78,0x34,0x20,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, - 0x69,0x78,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x31,0x32,0x38,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, - 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x4c,0x69,0x67,0x68,0x74,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, - 0x67,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, - 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x20,0x63,0x6f,0x6e,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, - 0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x34,0x20,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, - 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x65,0x6d,0x69,0x73,0x73,0x69, - 0x76,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6d, - 0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x63,0x6f,0x75,0x6e,0x74,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72, - 0x61,0x6d,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x38, - 0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x38,0x5d,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x4c,0x69,0x67,0x68,0x74,0x20,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x38,0x5d, - 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x66,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6f,0x75,0x74,0x5f,0x76, - 0x61,0x72,0x5f,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x20,0x5b,0x5b,0x63, - 0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, - 0x74,0x72,0x75,0x63,0x74,0x20,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61, - 0x69,0x6e,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x33,0x20,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x30,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30, - 0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20, - 0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31, - 0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x31,0x29,0x5d,0x5d, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x69,0x6e,0x5f, - 0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x32,0x20,0x5b,0x5b, - 0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x32,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, - 0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x66,0x72,0x61,0x67, - 0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x66,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x4d,0x61,0x69,0x6e,0x28,0x66,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x4d,0x61,0x69,0x6e,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73, - 0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74, - 0x61,0x6e,0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e, - 0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x26,0x20,0x46,0x72,0x61,0x67,0x6d, - 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x20,0x5b,0x5b,0x62,0x75, - 0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x62,0x61,0x73,0x65, - 0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72, - 0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32, - 0x64,0x5f,0x61,0x72,0x72,0x61,0x79,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x73, - 0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74, - 0x75,0x72,0x65,0x28,0x31,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x20,0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x5b,0x5b, - 0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x72,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x31,0x29, - 0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x4d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d, - 0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, - 0x5f,0x35,0x36,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x64,0x6f,0x0a,0x20,0x20,0x20, - 0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x34,0x20,0x5f,0x31,0x30,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x69,0x66,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x77,0x20, - 0x3e,0x20,0x30,0x2e,0x35,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30, - 0x33,0x20,0x3d,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x20,0x2a, - 0x20,0x62,0x61,0x73,0x65,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x28,0x62,0x61,0x73,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c, - 0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f, - 0x4f,0x52,0x44,0x32,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x33,0x20,0x3d,0x20,0x46,0x72,0x61,0x67,0x6d, - 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x62,0x61,0x73,0x65, - 0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61, - 0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x7a,0x20,0x3e,0x20,0x30,0x2e,0x35,0x29,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x35,0x36,0x30,0x20,0x3d,0x20,0x5f,0x31,0x30, - 0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62, - 0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f, - 0x31,0x30,0x39,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d, - 0x61,0x6c,0x69,0x7a,0x65,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f, - 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x31,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x31,0x34,0x20, - 0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x69,0x7a, - 0x65,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f, - 0x6e,0x2e,0x78,0x79,0x7a,0x20,0x2d,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61, - 0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x29,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x31, - 0x38,0x20,0x3d,0x20,0x5f,0x31,0x30,0x33,0x2e,0x78,0x79,0x7a,0x20,0x2a,0x20,0x28, - 0x31,0x2e,0x30,0x20,0x2d,0x20,0x28,0x30,0x2e,0x36,0x30,0x30,0x30,0x30,0x30,0x30, - 0x32,0x33,0x38,0x34,0x31,0x38,0x35,0x37,0x39,0x31,0x30,0x31,0x35,0x36,0x32,0x35, - 0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x78,0x29,0x29, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20, - 0x5f,0x31,0x32,0x32,0x20,0x3d,0x20,0x31,0x2e,0x30,0x20,0x2b,0x20,0x28,0x32,0x2e, - 0x30,0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x78,0x29, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33, - 0x20,0x5f,0x31,0x32,0x33,0x20,0x3d,0x20,0x6d,0x69,0x78,0x28,0x66,0x6c,0x6f,0x61, - 0x74,0x33,0x28,0x30,0x2e,0x30,0x33,0x39,0x39,0x39,0x39,0x39,0x39,0x39,0x31,0x30, - 0x35,0x39,0x33,0x30,0x33,0x32,0x38,0x33,0x36,0x39,0x31,0x34,0x30,0x36,0x32,0x35, - 0x29,0x2c,0x20,0x5f,0x31,0x30,0x33,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x33,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x78,0x29, - 0x29,0x20,0x2a,0x20,0x5f,0x31,0x32,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x31,0x32,0x35,0x20,0x3d,0x20,0x6d, - 0x69,0x78,0x28,0x32,0x35,0x36,0x2e,0x30,0x2c,0x20,0x34,0x2e,0x30,0x2c,0x20,0x46, - 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, - 0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x79,0x20,0x2a,0x20,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61, - 0x74,0x65,0x72,0x69,0x61,0x6c,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x5f,0x31,0x33,0x37,0x20,0x3d,0x20,0x69,0x6e, - 0x74,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x63,0x6f,0x75,0x6e,0x74,0x73,0x2e,0x78,0x29,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x33, - 0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x33,0x39,0x20, - 0x3d,0x20,0x28,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x73,0x2e,0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x2e,0x78,0x79,0x7a, - 0x20,0x2a,0x20,0x5f,0x31,0x30,0x33,0x2e,0x78,0x79,0x7a,0x29,0x20,0x2a,0x20,0x5f, - 0x31,0x32,0x32,0x29,0x20,0x2b,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x65,0x6d,0x69,0x73,0x73,0x69,0x76,0x65, - 0x2e,0x78,0x79,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x33,0x20,0x5f,0x31,0x34,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x66,0x6f,0x72,0x20,0x28,0x69,0x6e,0x74,0x20,0x5f,0x31,0x34,0x32, - 0x20,0x3d,0x20,0x30,0x3b,0x20,0x5f,0x31,0x34,0x32,0x20,0x3c,0x20,0x38,0x3b,0x20, - 0x5f,0x31,0x33,0x39,0x20,0x3d,0x20,0x5f,0x31,0x34,0x30,0x2c,0x20,0x5f,0x31,0x34, - 0x32,0x2b,0x2b,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f, - 0x31,0x34,0x32,0x20,0x3e,0x3d,0x20,0x5f,0x31,0x33,0x37,0x29,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x34,0x30,0x20, - 0x3d,0x20,0x5f,0x31,0x33,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x63,0x6f,0x6e,0x74,0x69,0x6e,0x75,0x65, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x20,0x5f,0x31,0x39,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x32,0x30,0x30,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20, - 0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x70, - 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2e,0x77,0x20,0x3d,0x3d, - 0x20,0x30,0x2e,0x30,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x5f,0x31,0x39,0x39,0x20,0x3d,0x20,0x31,0x2e,0x30,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x5f,0x32,0x30,0x30,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72, - 0x6d,0x61,0x6c,0x69,0x7a,0x65,0x28,0x2d,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b, - 0x5f,0x31,0x34,0x32,0x5d,0x2e,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52, - 0x61,0x6e,0x67,0x65,0x2e,0x78,0x79,0x7a,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f, - 0x31,0x36,0x34,0x20,0x3d,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31, - 0x34,0x32,0x5d,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65, - 0x2e,0x78,0x79,0x7a,0x20,0x2d,0x20,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, - 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x20,0x5f,0x31,0x36,0x35,0x20,0x3d,0x20,0x6c,0x65,0x6e,0x67,0x74,0x68,0x28, - 0x5f,0x31,0x36,0x34,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x31, - 0x36,0x38,0x20,0x3d,0x20,0x5f,0x31,0x36,0x34,0x20,0x2f,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x33,0x28,0x70,0x72,0x65,0x63,0x69,0x73,0x65,0x3a,0x3a,0x6d,0x61,0x78,0x28, - 0x5f,0x31,0x36,0x35,0x2c,0x20,0x39,0x2e,0x39,0x39,0x39,0x39,0x39,0x39,0x37,0x34, - 0x37,0x33,0x37,0x38,0x37,0x35,0x31,0x36,0x33,0x35,0x35,0x35,0x31,0x34,0x35,0x32, - 0x36,0x33,0x36,0x37,0x31,0x38,0x38,0x65,0x2d,0x30,0x35,0x29,0x29,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x20,0x5f,0x31,0x37,0x31,0x20,0x3d,0x20,0x31,0x2e,0x30,0x20, - 0x2f,0x20,0x28,0x31,0x2e,0x30,0x20,0x2b,0x20,0x28,0x5f,0x31,0x36,0x35,0x20,0x2a, - 0x20,0x5f,0x31,0x36,0x35,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f, - 0x31,0x38,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e, - 0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73, - 0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e, - 0x52,0x61,0x6e,0x67,0x65,0x2e,0x77,0x20,0x3e,0x20,0x30,0x2e,0x30,0x29,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x38,0x32,0x20,0x3d,0x20,0x5f,0x31,0x37,0x31, - 0x20,0x2a,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x63,0x6c,0x61,0x6d,0x70,0x28,0x31, - 0x2e,0x30,0x20,0x2d,0x20,0x70,0x6f,0x77,0x72,0x28,0x5f,0x31,0x36,0x35,0x20,0x2f, - 0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x64, - 0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e,0x67,0x65,0x2e,0x77,0x2c, - 0x20,0x34,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f, - 0x31,0x38,0x32,0x20,0x3d,0x20,0x5f,0x31,0x37,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x31,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72, - 0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c, - 0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x70,0x6f,0x73,0x69, - 0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x2e,0x77,0x20,0x3d,0x3d,0x20,0x32,0x2e, - 0x30,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x39,0x38,0x20,0x3d,0x20, - 0x5f,0x31,0x38,0x32,0x20,0x2a,0x20,0x73,0x6d,0x6f,0x6f,0x74,0x68,0x73,0x74,0x65, - 0x70,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e, - 0x63,0x6f,0x6e,0x65,0x2e,0x79,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b, - 0x5f,0x31,0x34,0x32,0x5d,0x2e,0x63,0x6f,0x6e,0x65,0x2e,0x78,0x2c,0x20,0x64,0x6f, - 0x74,0x28,0x2d,0x5f,0x31,0x36,0x38,0x2c,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e, - 0x6f,0x72,0x6d,0x61,0x6c,0x69,0x7a,0x65,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e, - 0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73, - 0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e, - 0x52,0x61,0x6e,0x67,0x65,0x2e,0x78,0x79,0x7a,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x39,0x38,0x20, - 0x3d,0x20,0x5f,0x31,0x38,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x39,0x39,0x20,0x3d, - 0x20,0x5f,0x31,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x30,0x30,0x20,0x3d,0x20,0x5f,0x31, - 0x36,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x35,0x33,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74, - 0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x63,0x6f,0x6e,0x65,0x2e,0x7a,0x20,0x3e, - 0x20,0x30,0x2e,0x35,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x5f,0x32,0x30,0x37,0x20,0x3d,0x20,0x69, - 0x6e,0x74,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d, - 0x2e,0x63,0x6f,0x6e,0x65,0x2e,0x7a,0x29,0x20,0x2d,0x20,0x31,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x35,0x33,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x46,0x72, - 0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73, - 0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e, - 0x78,0x20,0x3d,0x3d,0x20,0x32,0x2e,0x30,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x33,0x35,0x36,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x64,0x6f,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x32,0x33,0x30,0x20,0x3d,0x20,0x28, - 0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72,0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f, - 0x52,0x44,0x30,0x20,0x2b,0x20,0x28,0x28,0x5f,0x31,0x30,0x39,0x20,0x2a,0x20,0x46, - 0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x78,0x29,0x20, - 0x2a,0x20,0x38,0x2e,0x30,0x29,0x29,0x20,0x2d,0x20,0x46,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68,0x74, - 0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, - 0x54,0x79,0x70,0x65,0x2e,0x78,0x79,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x32,0x33,0x31,0x20,0x3d,0x20, - 0x61,0x62,0x73,0x28,0x5f,0x32,0x33,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x32,0x33,0x34,0x20,0x3d,0x20, - 0x5f,0x32,0x33,0x31,0x2e,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x32,0x33,0x35,0x20,0x3d,0x20,0x5f,0x32,0x33, - 0x31,0x2e,0x79,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f, - 0x6c,0x20,0x5f,0x32,0x34,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x69,0x66,0x20,0x28,0x5f,0x32,0x33,0x34,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x33,0x35, - 0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x34,0x31,0x20,0x3d,0x20,0x5f, - 0x32,0x33,0x34,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x33,0x31,0x2e,0x7a,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x34,0x31, - 0x20,0x3d,0x20,0x66,0x61,0x6c,0x73,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x20,0x5f,0x32,0x37,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x32,0x37,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x32,0x37,0x33,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20, - 0x5f,0x32,0x37,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66, - 0x20,0x28,0x5f,0x32,0x34,0x31,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f, - 0x6f,0x6c,0x20,0x5f,0x32,0x34,0x36,0x20,0x3d,0x20,0x5f,0x32,0x33,0x30,0x2e,0x78, - 0x20,0x3e,0x20,0x30,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x5f,0x32,0x37,0x31,0x20,0x3d,0x20,0x5f,0x32,0x34,0x36,0x20, - 0x3f,0x20,0x30,0x2e,0x30,0x20,0x3a,0x20,0x31,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x37,0x32,0x20,0x3d,0x20, - 0x5f,0x32,0x33,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x5f,0x32,0x37,0x33,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28, - 0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x29,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x37,0x34, - 0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x32,0x34,0x36,0x20,0x3f, - 0x20,0x31,0x2e,0x30,0x20,0x3a,0x20,0x28,0x2d,0x31,0x2e,0x30,0x29,0x2c,0x20,0x30, - 0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x32,0x35, - 0x30,0x20,0x3d,0x20,0x5f,0x32,0x33,0x31,0x2e,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x32,0x35, - 0x31,0x20,0x3d,0x20,0x5f,0x32,0x33,0x35,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x35,0x30, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x32,0x36,0x37,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x32,0x36, - 0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x32,0x36,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x32,0x35,0x31, - 0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x32,0x35,0x36,0x20,0x3d,0x20,0x5f,0x32,0x33,0x30, - 0x2e,0x79,0x20,0x3e,0x20,0x30,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x36,0x37,0x20,0x3d, - 0x20,0x5f,0x32,0x35,0x36,0x20,0x3f,0x20,0x32,0x2e,0x30,0x20,0x3a,0x20,0x33,0x2e, - 0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x5f,0x32,0x36,0x38,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33, - 0x28,0x30,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x5f,0x32,0x35,0x36,0x20, - 0x3f,0x20,0x28,0x2d,0x31,0x2e,0x30,0x29,0x20,0x3a,0x20,0x31,0x2e,0x30,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x5f,0x32,0x36,0x39,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x30, - 0x2e,0x30,0x2c,0x20,0x5f,0x32,0x35,0x36,0x20,0x3f,0x20,0x31,0x2e,0x30,0x20,0x3a, - 0x20,0x28,0x2d,0x31,0x2e,0x30,0x29,0x2c,0x20,0x30,0x2e,0x30,0x29,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c, - 0x20,0x5f,0x32,0x36,0x33,0x20,0x3d,0x20,0x5f,0x32,0x33,0x30,0x2e,0x7a,0x20,0x3e, - 0x20,0x30,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x36,0x37,0x20,0x3d,0x20,0x5f,0x32,0x36, - 0x33,0x20,0x3f,0x20,0x34,0x2e,0x30,0x20,0x3a,0x20,0x35,0x2e,0x30,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f, - 0x32,0x36,0x38,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x30,0x2e,0x30, - 0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x36, - 0x39,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x30,0x2e,0x30,0x2c,0x20, - 0x30,0x2e,0x30,0x2c,0x20,0x5f,0x32,0x36,0x33,0x20,0x3f,0x20,0x31,0x2e,0x30,0x20, - 0x3a,0x20,0x28,0x2d,0x31,0x2e,0x30,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x37,0x31,0x20,0x3d,0x20,0x5f,0x32,0x36,0x37, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32, - 0x37,0x32,0x20,0x3d,0x20,0x5f,0x32,0x35,0x31,0x20,0x3f,0x20,0x5f,0x32,0x33,0x35, - 0x20,0x3a,0x20,0x5f,0x32,0x35,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x5f,0x32,0x37,0x33,0x20,0x3d,0x20,0x5f,0x32,0x36,0x38, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x32, - 0x37,0x34,0x20,0x3d,0x20,0x5f,0x32,0x36,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x20,0x5f,0x32,0x37,0x39,0x20,0x3d,0x20,0x30,0x2e,0x35,0x20,0x2b,0x20, - 0x28,0x28,0x30,0x2e,0x35,0x20,0x2a,0x20,0x64,0x6f,0x74,0x28,0x5f,0x32,0x33,0x30, - 0x2c,0x20,0x63,0x72,0x6f,0x73,0x73,0x28,0x5f,0x32,0x37,0x34,0x2c,0x20,0x5f,0x32, - 0x37,0x33,0x29,0x29,0x29,0x20,0x2f,0x20,0x5f,0x32,0x37,0x32,0x29,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x32,0x38, - 0x33,0x20,0x3d,0x20,0x30,0x2e,0x35,0x20,0x2d,0x20,0x28,0x28,0x30,0x2e,0x35,0x20, - 0x2a,0x20,0x64,0x6f,0x74,0x28,0x5f,0x32,0x33,0x30,0x2c,0x20,0x5f,0x32,0x37,0x33, - 0x29,0x29,0x20,0x2f,0x20,0x5f,0x32,0x37,0x32,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x32,0x38,0x34,0x20, - 0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x5f,0x32,0x37,0x39,0x2c,0x20,0x5f, - 0x32,0x38,0x33,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x32,0x38,0x35,0x20,0x3d,0x20,0x46,0x72,0x61,0x67,0x6d, - 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e,0x77,0x20,0x2d, - 0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30, - 0x37,0x5d,0x2e,0x7a,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x32,0x39,0x32,0x20,0x3d,0x20,0x28,0x28,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e,0x77, - 0x20,0x2f,0x20,0x5f,0x32,0x38,0x35,0x29,0x20,0x2d,0x20,0x28,0x28,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e,0x7a, - 0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f, - 0x32,0x30,0x37,0x5d,0x2e,0x77,0x29,0x20,0x2f,0x20,0x28,0x5f,0x32,0x38,0x35,0x20, - 0x2a,0x20,0x5f,0x32,0x37,0x32,0x29,0x29,0x29,0x20,0x2d,0x20,0x28,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x78,0x20,0x2a,0x20,0x32, - 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20, - 0x28,0x5f,0x32,0x39,0x32,0x20,0x3e,0x20,0x31,0x2e,0x30,0x29,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x5f,0x33,0x35,0x36,0x20,0x3d,0x20,0x31,0x2e,0x30,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x72,0x65,0x61,0x6b, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x32,0x39,0x38,0x20,0x3d, - 0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30, - 0x37,0x5d,0x2e,0x79,0x20,0x2b,0x20,0x5f,0x32,0x37,0x31,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x32,0x39,0x39, - 0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x32,0x37,0x39,0x2c,0x20, - 0x5f,0x32,0x38,0x33,0x2c,0x20,0x5f,0x32,0x39,0x38,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x33,0x30,0x37, - 0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x46,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f, - 0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20, - 0x5f,0x33,0x31,0x31,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x32, - 0x38,0x34,0x20,0x2b,0x20,0x5f,0x33,0x30,0x37,0x2c,0x20,0x5f,0x32,0x39,0x38,0x29, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33, - 0x20,0x5f,0x33,0x32,0x33,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f, - 0x32,0x38,0x34,0x20,0x2d,0x20,0x5f,0x33,0x30,0x37,0x2c,0x20,0x5f,0x32,0x39,0x38, - 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x32,0x20,0x5f,0x33,0x33,0x32,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, - 0x30,0x2e,0x30,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61, - 0x6d,0x73,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x33,0x33,0x36,0x20,0x3d,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x33,0x28,0x5f,0x32,0x38,0x34,0x20,0x2b,0x20,0x5f,0x33,0x33,0x32,0x2c, - 0x20,0x5f,0x32,0x39,0x38,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x33,0x34,0x38,0x20,0x3d,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x33,0x28,0x5f,0x32,0x38,0x34,0x20,0x2d,0x20,0x5f,0x33,0x33,0x32, - 0x2c,0x20,0x5f,0x32,0x39,0x38,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x5f,0x33,0x35,0x36,0x20,0x3d,0x20,0x28,0x28,0x28,0x28,0x66,0x6c,0x6f,0x61, - 0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x2c,0x20,0x5f,0x32,0x39,0x39,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69,0x6e,0x74, - 0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x32,0x39,0x39,0x2e,0x7a,0x29,0x29,0x29,0x2e, - 0x78,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x39,0x32,0x29,0x20,0x2b,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c, - 0x65,0x72,0x2c,0x20,0x5f,0x33,0x31,0x31,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69,0x6e, - 0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x33,0x31,0x31,0x2e,0x7a,0x29,0x29,0x29, - 0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x39,0x32,0x29,0x29,0x20,0x2b,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e, - 0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f,0x33,0x32,0x33,0x2e,0x78,0x79,0x2c,0x20,0x75, - 0x69,0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x33,0x32,0x33,0x2e,0x7a,0x29, - 0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x39,0x32,0x29,0x29,0x20,0x2b, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70, - 0x73,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53, - 0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f,0x33,0x33,0x36,0x2e,0x78,0x79,0x2c, - 0x20,0x75,0x69,0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x33,0x33,0x36,0x2e, - 0x7a,0x29,0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x39,0x32,0x29,0x29, - 0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d, - 0x61,0x70,0x73,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f, - 0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f,0x33,0x34,0x38,0x2e,0x78, - 0x79,0x2c,0x20,0x75,0x69,0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x33,0x34, - 0x38,0x2e,0x7a,0x29,0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x32,0x39,0x32, - 0x29,0x29,0x20,0x2a,0x20,0x30,0x2e,0x32,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x32, - 0x39,0x38,0x30,0x32,0x33,0x32,0x32,0x33,0x38,0x37,0x36,0x39,0x35,0x33,0x31,0x32, - 0x35,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x72,0x65,0x61,0x6b, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20,0x77,0x68,0x69,0x6c,0x65,0x28,0x66,0x61, - 0x6c,0x73,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x35,0x33,0x33,0x20,0x3d, - 0x20,0x5f,0x31,0x39,0x39,0x20,0x2a,0x20,0x5f,0x33,0x35,0x36,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x20,0x5f,0x35,0x33,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x64,0x6f,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x34,0x20,0x5f,0x33,0x37,0x31,0x20,0x3d,0x20,0x46,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f, - 0x77,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x20,0x2a,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x69,0x6e,0x5f,0x76,0x61,0x72, - 0x5f,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x30,0x20,0x2b,0x20,0x28,0x28,0x5f, - 0x31,0x30,0x39,0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72, - 0x61,0x6d,0x73,0x2e,0x78,0x29,0x20,0x2a,0x20,0x38,0x2e,0x30,0x29,0x2c,0x20,0x31, - 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x33,0x20,0x5f,0x33,0x37,0x39,0x20,0x3d,0x20,0x5f,0x33,0x37,0x31,0x2e, - 0x78,0x79,0x7a,0x20,0x2f,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x33,0x37, - 0x31,0x2e,0x77,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x33,0x38,0x32,0x20,0x3d,0x20,0x28,0x5f,0x33,0x37,0x39, - 0x2e,0x78,0x20,0x2a,0x20,0x30,0x2e,0x35,0x29,0x20,0x2b,0x20,0x30,0x2e,0x35,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f, - 0x33,0x38,0x35,0x20,0x3d,0x20,0x30,0x2e,0x35,0x20,0x2d,0x20,0x28,0x5f,0x33,0x37, - 0x39,0x2e,0x79,0x20,0x2a,0x20,0x30,0x2e,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x33,0x38,0x36,0x20, - 0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x5f,0x33,0x38,0x32,0x2c,0x20,0x5f, - 0x33,0x38,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x20,0x5f,0x33,0x39,0x34,0x20,0x3d,0x20,0x5f,0x33,0x37,0x39,0x2e, - 0x7a,0x20,0x2d,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61, - 0x6d,0x73,0x2e,0x78,0x20,0x2a,0x20,0x28,0x31,0x2e,0x30,0x20,0x2b,0x20,0x28,0x32, - 0x2e,0x30,0x20,0x2a,0x20,0x28,0x31,0x2e,0x30,0x20,0x2d,0x20,0x66,0x61,0x73,0x74, - 0x3a,0x3a,0x63,0x6c,0x61,0x6d,0x70,0x28,0x64,0x6f,0x74,0x28,0x5f,0x31,0x30,0x39, - 0x2c,0x20,0x5f,0x32,0x30,0x30,0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e, - 0x30,0x29,0x29,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x33,0x39,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x28,0x69,0x73,0x75,0x6e,0x6f,0x72,0x64,0x65, - 0x72,0x65,0x64,0x28,0x5f,0x33,0x38,0x32,0x2c,0x20,0x30,0x2e,0x30,0x29,0x20,0x7c, - 0x7c,0x20,0x5f,0x33,0x38,0x32,0x20,0x3e,0x3d,0x20,0x30,0x2e,0x30,0x29,0x29,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x33,0x39,0x39,0x20,0x3d,0x20,0x5f,0x33,0x38, - 0x32,0x20,0x3e,0x20,0x31,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x33,0x39,0x39,0x20,0x3d,0x20,0x74,0x72,0x75, - 0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x34,0x30,0x34,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x33,0x39, - 0x39,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x34,0x30,0x34,0x20,0x3d,0x20, - 0x5f,0x33,0x38,0x35,0x20,0x3c,0x20,0x30,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65,0x6c, - 0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x34,0x30,0x34,0x20,0x3d,0x20, - 0x74,0x72,0x75,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x34,0x30, - 0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21, - 0x5f,0x34,0x30,0x34,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x34,0x30,0x39, - 0x20,0x3d,0x20,0x5f,0x33,0x38,0x35,0x20,0x3e,0x20,0x31,0x2e,0x30,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x34,0x30,0x39, - 0x20,0x3d,0x20,0x74,0x72,0x75,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20, - 0x5f,0x34,0x31,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66, - 0x20,0x28,0x21,0x5f,0x34,0x30,0x39,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f, - 0x34,0x31,0x34,0x20,0x3d,0x20,0x5f,0x33,0x39,0x34,0x20,0x3e,0x20,0x31,0x2e,0x30, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f, - 0x34,0x31,0x34,0x20,0x3d,0x20,0x74,0x72,0x75,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x69,0x66, - 0x20,0x28,0x5f,0x34,0x31,0x34,0x29,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x35, - 0x33,0x31,0x20,0x3d,0x20,0x31,0x2e,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x62,0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x20,0x5f,0x34,0x31,0x39,0x20,0x3d,0x20,0x2d,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x79,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x34,0x32, - 0x34,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x33,0x38,0x36,0x20, - 0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x5f,0x34,0x31,0x39,0x29,0x2c,0x20, - 0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37, - 0x5d,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x33,0x20,0x5f,0x34,0x33,0x36,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x33,0x28,0x5f,0x33,0x38,0x36,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, - 0x28,0x30,0x2e,0x30,0x2c,0x20,0x5f,0x34,0x31,0x39,0x29,0x2c,0x20,0x46,0x72,0x61, - 0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e,0x79, - 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x33,0x20,0x5f,0x34,0x34,0x39,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28, - 0x5f,0x33,0x38,0x36,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x46,0x72, - 0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73, - 0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x79,0x2c,0x20,0x5f, - 0x34,0x31,0x39,0x29,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66, - 0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x34,0x36,0x32,0x20, - 0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x33,0x38,0x36,0x20,0x2b,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x5f,0x34,0x31,0x39,0x2c,0x20,0x30,0x2e,0x30, - 0x29,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f, - 0x32,0x30,0x37,0x5d,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x34,0x37,0x31,0x20,0x3d,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x33,0x38,0x32,0x2c,0x20,0x5f,0x33,0x38,0x35, - 0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72, - 0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32, - 0x30,0x37,0x5d,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x34,0x38,0x34,0x20,0x3d,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x33,0x28,0x5f,0x33,0x38,0x36,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x32,0x28,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73, - 0x2e,0x79,0x2c,0x20,0x30,0x2e,0x30,0x29,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f, - 0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e,0x79,0x29,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f, - 0x34,0x39,0x37,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x33,0x38, - 0x36,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x5f,0x34,0x31,0x39,0x2c, - 0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x79, - 0x29,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f, - 0x32,0x30,0x37,0x5d,0x2e,0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20,0x5f,0x35,0x31,0x30,0x20,0x3d,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x33,0x38,0x36,0x20,0x2b,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e, - 0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64,0x6f,0x77, - 0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x79,0x29,0x2c,0x20,0x46,0x72,0x61,0x67,0x6d, - 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e,0x79,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x20, - 0x5f,0x35,0x32,0x33,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x33,0x28,0x5f,0x33, - 0x38,0x36,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x46,0x72,0x61,0x67, - 0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73,0x68,0x61, - 0x64,0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x2e,0x79,0x29,0x2c,0x20,0x46,0x72, - 0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x73, - 0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x5f,0x32,0x30,0x37,0x5d,0x2e, - 0x79,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x35,0x33,0x31, - 0x20,0x3d,0x20,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x28,0x66,0x6c,0x6f,0x61,0x74, - 0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x5f,0x34,0x32,0x34,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69,0x6e,0x74,0x28, - 0x72,0x69,0x6e,0x74,0x28,0x5f,0x34,0x32,0x34,0x2e,0x7a,0x29,0x29,0x29,0x2e,0x78, - 0x20,0x3e,0x3d,0x20,0x5f,0x33,0x39,0x34,0x29,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x2c,0x20,0x5f,0x34,0x33,0x36,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69,0x6e,0x74, - 0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x34,0x33,0x36,0x2e,0x7a,0x29,0x29,0x29,0x2e, - 0x78,0x20,0x3e,0x3d,0x20,0x5f,0x33,0x39,0x34,0x29,0x29,0x20,0x2b,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x2c,0x20,0x5f,0x34,0x34,0x39,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69, - 0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x34,0x34,0x39,0x2e,0x7a,0x29,0x29, - 0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x33,0x39,0x34,0x29,0x29,0x20,0x2b,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73, - 0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61, - 0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f,0x34,0x36,0x32,0x2e,0x78,0x79,0x2c,0x20, - 0x75,0x69,0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x34,0x36,0x32,0x2e,0x7a, - 0x29,0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x33,0x39,0x34,0x29,0x29,0x20, - 0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61, - 0x70,0x73,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77, - 0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f,0x34,0x37,0x31,0x2e,0x78,0x79, - 0x2c,0x20,0x75,0x69,0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x34,0x37,0x31, - 0x2e,0x7a,0x29,0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x33,0x39,0x34,0x29, - 0x29,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64,0x6f,0x77, - 0x4d,0x61,0x70,0x73,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f,0x34,0x38,0x34,0x2e, - 0x78,0x79,0x2c,0x20,0x75,0x69,0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28,0x5f,0x34, - 0x38,0x34,0x2e,0x7a,0x29,0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f,0x33,0x39, - 0x34,0x29,0x29,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x28,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f,0x34,0x39, - 0x37,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69,0x6e,0x74,0x28,0x72,0x69,0x6e,0x74,0x28, - 0x5f,0x34,0x39,0x37,0x2e,0x7a,0x29,0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d,0x20,0x5f, - 0x33,0x39,0x34,0x29,0x29,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x28,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x5f, - 0x35,0x31,0x30,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69,0x6e,0x74,0x28,0x72,0x69,0x6e, - 0x74,0x28,0x5f,0x35,0x31,0x30,0x2e,0x7a,0x29,0x29,0x29,0x2e,0x78,0x20,0x3e,0x3d, - 0x20,0x5f,0x33,0x39,0x34,0x29,0x29,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x28, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x70,0x73,0x2e,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x28,0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c, - 0x20,0x5f,0x35,0x32,0x33,0x2e,0x78,0x79,0x2c,0x20,0x75,0x69,0x6e,0x74,0x28,0x72, - 0x69,0x6e,0x74,0x28,0x5f,0x35,0x32,0x33,0x2e,0x7a,0x29,0x29,0x29,0x2e,0x78,0x20, - 0x3e,0x3d,0x20,0x5f,0x33,0x39,0x34,0x29,0x29,0x20,0x2a,0x20,0x30,0x2e,0x31,0x31, - 0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x39,0x33,0x38,0x39,0x35,0x33,0x33,0x39,0x39, - 0x36,0x35,0x38,0x32,0x30,0x33,0x31,0x32,0x35,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x62,0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x20, - 0x77,0x68,0x69,0x6c,0x65,0x28,0x66,0x61,0x6c,0x73,0x65,0x29,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x5f,0x35,0x33,0x33,0x20,0x3d,0x20,0x5f,0x31,0x39,0x39,0x20,0x2a,0x20, - 0x5f,0x35,0x33,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x35,0x33,0x34,0x20,0x3d,0x20,0x5f, - 0x35,0x33,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x65, - 0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x5f,0x35,0x33,0x34,0x20,0x3d,0x20,0x5f,0x31,0x39,0x39,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f, - 0x35,0x33,0x36,0x20,0x3d,0x20,0x66,0x61,0x73,0x74,0x3a,0x3a,0x63,0x6c,0x61,0x6d, - 0x70,0x28,0x64,0x6f,0x74,0x28,0x5f,0x31,0x30,0x39,0x2c,0x20,0x5f,0x32,0x30,0x30, - 0x29,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x34,0x30,0x20,0x3d, - 0x20,0x5f,0x31,0x33,0x39,0x20,0x2b,0x20,0x28,0x28,0x28,0x28,0x5f,0x31,0x31,0x38, - 0x20,0x2a,0x20,0x5f,0x35,0x33,0x36,0x29,0x20,0x2b,0x20,0x28,0x28,0x5f,0x31,0x32, - 0x33,0x20,0x2a,0x20,0x28,0x70,0x6f,0x77,0x72,0x28,0x66,0x61,0x73,0x74,0x3a,0x3a, - 0x63,0x6c,0x61,0x6d,0x70,0x28,0x64,0x6f,0x74,0x28,0x5f,0x31,0x30,0x39,0x2c,0x20, - 0x66,0x61,0x73,0x74,0x3a,0x3a,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x69,0x7a,0x65,0x28, - 0x5f,0x32,0x30,0x30,0x20,0x2b,0x20,0x5f,0x31,0x31,0x34,0x29,0x29,0x2c,0x20,0x30, - 0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x2c,0x20,0x5f,0x31,0x32,0x35,0x29,0x20, - 0x2a,0x20,0x28,0x31,0x2e,0x30,0x20,0x2d,0x20,0x28,0x46,0x72,0x61,0x67,0x6d,0x65, - 0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6d,0x61,0x74,0x65,0x72, - 0x69,0x61,0x6c,0x2e,0x79,0x20,0x2a,0x20,0x30,0x2e,0x35,0x29,0x29,0x29,0x29,0x20, - 0x2a,0x20,0x5f,0x35,0x33,0x36,0x29,0x29,0x20,0x2a,0x20,0x46,0x72,0x61,0x67,0x6d, - 0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x2e,0x6c,0x69,0x67,0x68, - 0x74,0x73,0x5b,0x5f,0x31,0x34,0x32,0x5d,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x2e,0x78, - 0x79,0x7a,0x29,0x20,0x2a,0x20,0x5f,0x35,0x33,0x34,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f, - 0x35,0x36,0x30,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x5f,0x31,0x33, - 0x39,0x2c,0x20,0x5f,0x31,0x30,0x33,0x2e,0x77,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x62,0x72,0x65,0x61,0x6b,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d, - 0x20,0x77,0x68,0x69,0x6c,0x65,0x28,0x66,0x61,0x6c,0x73,0x65,0x29,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x6f,0x75,0x74,0x5f,0x76,0x61,0x72,0x5f,0x53, - 0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x20,0x3d,0x20,0x5f,0x35,0x36,0x30,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b, - 0x0a,0x7d,0x0a,0x0a, -}; - -static const SceneShaderT sceneShaderFragmentMain = { "fragmentMain", _fragmentMainSPIRV, sizeof(_fragmentMainSPIRV), _fragmentMainDXIL, sizeof(_fragmentMainDXIL), _fragmentMainMSL, sizeof(_fragmentMainMSL) }; - -static const unsigned char _depthMainSPIRV[] = { - 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0x23,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0e,0x00,0x03,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x06,0x00,0x04,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x64,0x65,0x70,0x74,0x68,0x4d,0x61,0x69,0x6e,0x00,0x00,0x00, - 0x10,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00, - 0x05,0x00,0x00,0x00,0x58,0x02,0x00,0x00,0x05,0x00,0x07,0x00,0x02,0x00,0x00,0x00, - 0x74,0x79,0x70,0x65,0x2e,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x00,0x00,0x00,0x06,0x00,0x08,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, - 0x69,0x6f,0x6e,0x00,0x06,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x6d,0x6f,0x64,0x65,0x6c,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x02,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72,0x69,0x78, - 0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x03,0x00,0x00,0x00,0x44,0x72,0x61,0x77, - 0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, - 0x04,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x04,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x6a,0x6f,0x69,0x6e,0x74,0x73,0x00,0x00,0x05,0x00,0x06,0x00, - 0x05,0x00,0x00,0x00,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x00,0x00,0x00,0x00,0x05,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x74,0x79,0x70,0x65, - 0x2e,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d, - 0x73,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00, - 0x06,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x61,0x6d,0x62,0x69, - 0x65,0x6e,0x74,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00,0x06,0x00,0x06,0x00, - 0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x65,0x6d,0x69,0x73,0x73,0x69,0x76,0x65, - 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x6d,0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x00,0x00,0x00,0x00,0x06,0x00,0x05,0x00, - 0x06,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x63,0x6f,0x75,0x6e,0x74,0x73,0x00,0x00, - 0x06,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x50,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00, - 0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61, - 0x74,0x72,0x69,0x78,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x06,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x00,0x00, - 0x06,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x6c,0x69,0x67,0x68, - 0x74,0x73,0x00,0x00,0x05,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x4c,0x69,0x67,0x68, - 0x74,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x00,0x00,0x00,0x00, - 0x06,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x64,0x69,0x72,0x65, - 0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e,0x67,0x65,0x00,0x00,0x06,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, - 0x06,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x63,0x6f,0x6e,0x65, - 0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x46,0x72,0x61,0x67, - 0x6d,0x65,0x6e,0x74,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x00,0x00,0x00,0x00, - 0x05,0x00,0x06,0x00,0x09,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x32,0x64,0x2e, - 0x69,0x6d,0x61,0x67,0x65,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00, - 0x62,0x61,0x73,0x65,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x00,0x05,0x00,0x06,0x00, - 0x0b,0x00,0x00,0x00,0x74,0x79,0x70,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x62,0x61,0x73,0x65, - 0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x05,0x00,0x07,0x00,0x0d,0x00,0x00,0x00, - 0x74,0x79,0x70,0x65,0x2e,0x32,0x64,0x2e,0x69,0x6d,0x61,0x67,0x65,0x2e,0x61,0x72, - 0x72,0x61,0x79,0x00,0x05,0x00,0x05,0x00,0x0e,0x00,0x00,0x00,0x73,0x68,0x61,0x64, - 0x6f,0x77,0x4d,0x61,0x70,0x73,0x00,0x00,0x05,0x00,0x06,0x00,0x0f,0x00,0x00,0x00, - 0x73,0x68,0x61,0x64,0x6f,0x77,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x00,0x00,0x00, - 0x05,0x00,0x05,0x00,0x01,0x00,0x00,0x00,0x64,0x65,0x70,0x74,0x68,0x4d,0x61,0x69, - 0x6e,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0c,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x02,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x02,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x04,0x00, - 0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, - 0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x04,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x48,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x47,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x12,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x50,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x48,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x70,0x02,0x00,0x00,0x48,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0xf0,0x02,0x00,0x00,0x47,0x00,0x03,0x00,0x06,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x18,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x1e,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x16,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00, - 0x80,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00, - 0x19,0x00,0x00,0x00,0x1e,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x1a,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x2b,0x00,0x04,0x00,0x18,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x1c,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x1c,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, - 0x1e,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x13,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x1e,0x00,0x0c,0x00,0x06,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x11,0x00,0x00,0x00, - 0x12,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1c,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x09,0x00,0x00,0x00, - 0x14,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x1d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x1a,0x00,0x02,0x00, - 0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x0d,0x00,0x00,0x00,0x14,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x1f,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x20,0x00,0x00,0x00, - 0x21,0x00,0x03,0x00,0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x17,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1a,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1c,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1d,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1e,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1f,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, - 0x1e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x00,0x05,0x00, - 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0xf8,0x00,0x02,0x00,0x22,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, -}; - -static const unsigned char _depthMainDXIL[] = { - 0x44,0x58,0x42,0x43,0x7a,0xf9,0xeb,0x17,0x80,0x8a,0x06,0xd1,0x88,0x87,0x8c,0x1f, - 0xba,0x06,0xbc,0x20,0x01,0x00,0x00,0x00,0x04,0x08,0x00,0x00,0x07,0x00,0x00,0x00, - 0x3c,0x00,0x00,0x00,0x4c,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x6c,0x00,0x00,0x00, - 0xc4,0x00,0x00,0x00,0x6c,0x04,0x00,0x00,0x88,0x04,0x00,0x00,0x53,0x46,0x49,0x30, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x49,0x53,0x47,0x31, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x4f,0x53,0x47,0x31, - 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x53,0x56,0x30, - 0x50,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x0c,0x00,0x00,0x00,0x00,0x64,0x65,0x70,0x74,0x68,0x4d,0x61,0x69,0x6e,0x00,0x00, - 0x00,0x00,0x00,0x00,0x53,0x54,0x41,0x54,0xa0,0x03,0x00,0x00,0x60,0x00,0x00,0x00, - 0xe8,0x00,0x00,0x00,0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00,0x10,0x00,0x00,0x00, - 0x88,0x03,0x00,0x00,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xdf,0x00,0x00,0x00, - 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x07,0x81,0x23,0x91, - 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, - 0x1e,0x04,0x8b,0x62,0x80,0x0c,0x45,0x02,0x42,0x92,0x0b,0x42,0x64,0x10,0x32,0x14, - 0x38,0x08,0x18,0x4b,0x0a,0x32,0x32,0x88,0x48,0x90,0x14,0x20,0x43,0x46,0x88,0xa5, - 0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90,0x91,0x21,0xc4,0x50,0x41,0x51,0x81,0x8c, - 0xe1,0x83,0xe5,0x8a,0x04,0x19,0x46,0x06,0x89,0x20,0x00,0x00,0x0b,0x00,0x00,0x00, - 0x32,0x22,0xc8,0x08,0x20,0x64,0x85,0x04,0x93,0x21,0xa4,0x84,0x04,0x93,0x21,0xe3, - 0x84,0xa1,0x90,0x14,0x12,0x4c,0x86,0x8c,0x0b,0x84,0x64,0x4c,0x10,0x14,0x23,0x00, - 0x25,0x00,0x65,0x20,0x60,0x8e,0x00,0x0c,0x00,0x00,0x00,0x00,0x13,0x14,0x72,0xc0, - 0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0,0x87,0x0d,0xaf,0x50, - 0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30,0x07,0x72,0xa0,0x07, - 0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x78, - 0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0xd0, - 0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07, - 0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x6d, - 0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, - 0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07, - 0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0xb2,0x40,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x32,0x1e,0x98,0x0c, - 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x62,0x09,0x8c,0x00,0x14, - 0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x38,0x00,0x00,0x00,0x1a,0x03,0x4c,0x90, - 0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd,0x2e,0x8c,0xae,0x2c, - 0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66,0x0c,0x87,0x26,0x2c, - 0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x88,0x60,0x82,0x40,0x08,0x1b,0x84,0x81, - 0xd8,0x20,0x10,0x04,0x05,0xb8,0xb9,0x09,0x02,0x31,0x6c,0x18,0x0e,0x84,0x98,0x20, - 0x08,0xc0,0x06,0x60,0xc3,0x30,0x2c,0xcb,0x86,0x80,0xd9,0x30,0x0c,0x4a,0xc3,0x84, - 0xac,0x0c,0x8e,0x0e,0xad,0x29,0x2c,0xcd,0x6d,0x43,0xa1,0x3c,0x00,0x00,0xb0,0x48, - 0x73,0x9b,0xa3,0x9b,0xdb,0x20,0x44,0x43,0x15,0x36,0x36,0xbb,0x36,0x97,0x34,0xb2, - 0x32,0x37,0xba,0x29,0x41,0x50,0x85,0x0c,0xcf,0xc5,0xae,0x4c,0x6e,0x2e,0xed,0xcd, - 0x6d,0x4a,0x40,0x34,0x21,0xc3,0x73,0xb1,0x0b,0x63,0xb3,0x2b,0x93,0x9b,0x12,0x14, - 0x75,0xc8,0xf0,0x5c,0xe6,0xd0,0xc2,0xc8,0xca,0xe4,0x9a,0xde,0xc8,0xca,0xd8,0xa6, - 0x04,0x48,0x25,0x32,0x3c,0x17,0xba,0x3c,0xb8,0xb2,0x20,0x37,0xb7,0x37,0xba,0x30, - 0xba,0xb4,0x37,0xb7,0xb9,0x29,0x41,0x53,0x87,0x0c,0xcf,0xa5,0xcc,0x8d,0x4e,0x2e, - 0x0f,0xea,0x2d,0xcd,0x8d,0x6e,0x6e,0x4a,0xf0,0x74,0x21,0xc3,0x73,0x19,0x7b,0xab, - 0x73,0xa3,0x2b,0x93,0x9b,0x9b,0x12,0x44,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00, - 0x4c,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88, - 0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6, - 0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce, - 0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8, - 0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48, - 0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11, - 0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e, - 0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89, - 0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b, - 0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37, - 0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78, - 0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81, - 0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1, - 0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c, - 0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39, - 0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc, - 0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70, - 0x03,0x72,0x10,0x87,0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87, - 0x77,0xa8,0x07,0x7a,0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e, - 0xf0,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x03,0x00,0x00,0x00,0x06,0x90,0x0c,0xc4, - 0x33,0x1d,0x26,0x80,0x34,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x41,0x53,0x48, - 0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0xbe,0xe4,0xc3,0xef,0x9d,0x69,0x4d, - 0xb2,0xed,0x21,0xd0,0xd8,0x30,0xaf,0x24,0x44,0x58,0x49,0x4c,0x74,0x03,0x00,0x00, - 0x60,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x44,0x58,0x49,0x4c,0x00,0x01,0x00,0x00, - 0x10,0x00,0x00,0x00,0x5c,0x03,0x00,0x00,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00, - 0xd4,0x00,0x00,0x00,0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x13,0x00,0x00,0x00, - 0x07,0x81,0x23,0x91,0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c, - 0x25,0x05,0x08,0x19,0x1e,0x04,0x8b,0x62,0x80,0x0c,0x45,0x02,0x42,0x92,0x0b,0x42, - 0x64,0x10,0x32,0x14,0x38,0x08,0x18,0x4b,0x0a,0x32,0x32,0x88,0x48,0x90,0x14,0x20, - 0x43,0x46,0x88,0xa5,0x00,0x19,0x32,0x42,0xe4,0x48,0x0e,0x90,0x91,0x21,0xc4,0x50, - 0x41,0x51,0x81,0x8c,0xe1,0x83,0xe5,0x8a,0x04,0x19,0x46,0x06,0x89,0x20,0x00,0x00, - 0x0b,0x00,0x00,0x00,0x32,0x22,0xc8,0x08,0x20,0x64,0x85,0x04,0x93,0x21,0xa4,0x84, - 0x04,0x93,0x21,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x86,0x8c,0x0b,0x84,0x64,0x4c, - 0x10,0x14,0x23,0x00,0x25,0x00,0x65,0x20,0x60,0x8e,0x00,0x0c,0x00,0x00,0x00,0x00, - 0x13,0x14,0x72,0xc0,0x87,0x74,0x60,0x87,0x36,0x68,0x87,0x79,0x68,0x03,0x72,0xc0, - 0x87,0x0d,0xaf,0x50,0x0e,0x6d,0xd0,0x0e,0x7a,0x50,0x0e,0x6d,0x00,0x0f,0x7a,0x30, - 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x73,0x20,0x07, - 0x6d,0x90,0x0e,0x78,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a, - 0x30,0x07,0x72,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90, - 0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x10,0x07,0x76,0xa0,0x07, - 0x73,0x20,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6, - 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, - 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x76,0x40,0x07,0x43,0x9e,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xb2,0x40,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x32,0x1e,0x98,0x0c,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x62, - 0x09,0x8c,0x00,0x94,0x44,0x31,0x00,0x00,0x79,0x18,0x00,0x00,0x2a,0x00,0x00,0x00, - 0x1a,0x03,0x4c,0x90,0x46,0x02,0x13,0xc4,0x8f,0x0c,0x6f,0x0c,0x05,0x4e,0x2e,0xcd, - 0x2e,0x8c,0xae,0x2c,0x05,0x24,0xc6,0x25,0xc7,0x05,0xc6,0x25,0x06,0x04,0x45,0x66, - 0x0c,0x87,0x26,0x2c,0x66,0xac,0x26,0x65,0x43,0x10,0x4c,0x10,0x88,0x60,0x82,0x40, - 0x08,0x1b,0x84,0x81,0x98,0x20,0x10,0xc3,0x06,0x61,0x30,0x28,0xc0,0xcd,0x4d,0x10, - 0x08,0x62,0xc3,0x80,0x24,0xc4,0x04,0x41,0x00,0x98,0x90,0x95,0xc1,0xd1,0xa1,0x35, - 0x85,0xa5,0xb9,0x6d,0x28,0x16,0x06,0x00,0x80,0x2a,0x6c,0x6c,0x76,0x6d,0x2e,0x69, - 0x64,0x65,0x6e,0x74,0x53,0x82,0xa0,0x0a,0x19,0x9e,0x8b,0x5d,0x99,0xdc,0x5c,0xda, - 0x9b,0xdb,0x94,0x80,0x68,0x42,0x86,0xe7,0x62,0x17,0xc6,0x66,0x57,0x26,0x37,0x25, - 0x30,0xea,0x90,0xe1,0xb9,0xcc,0xa1,0x85,0x91,0x95,0xc9,0x35,0xbd,0x91,0x95,0xb1, - 0x4d,0x09,0x92,0x3a,0x64,0x78,0x2e,0x65,0x6e,0x74,0x72,0x79,0x50,0x6f,0x69,0x6e, - 0x74,0x73,0x53,0x02,0x06,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x4c,0x00,0x00,0x00, - 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, - 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, - 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, - 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, - 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, - 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, - 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, - 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, - 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, - 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, - 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, - 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, - 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, - 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, - 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, - 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, - 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x8c,0xc8,0x21,0x07,0x7c,0x70,0x03,0x72,0x10,0x87, - 0x73,0x70,0x03,0x7b,0x08,0x07,0x79,0x60,0x87,0x70,0xc8,0x87,0x77,0xa8,0x07,0x7a, - 0x98,0x81,0x3c,0xe4,0x80,0x0f,0x6e,0x40,0x0f,0xe5,0xd0,0x0e,0xf0,0x00,0x00,0x00, - 0x71,0x20,0x00,0x00,0x03,0x00,0x00,0x00,0x06,0x90,0x0c,0xc4,0x33,0x1d,0x26,0x80, - 0x34,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x04,0x01,0x02, - 0x00,0x00,0x00,0x00, -}; - -static const unsigned char _depthMainMSL[] = { - 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, - 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, - 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, - 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, - 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x44,0x72,0x61,0x77,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6d,0x6f,0x64,0x65,0x6c,0x56,0x69,0x65,0x77,0x50,0x72,0x6f,0x6a,0x65,0x63,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x6d,0x6f,0x64,0x65,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x78,0x34,0x20,0x6e,0x6f,0x72,0x6d,0x61,0x6c,0x4d,0x61,0x74,0x72, - 0x69,0x78,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x74, - 0x79,0x70,0x65,0x5f,0x53,0x6b,0x69,0x6e,0x55,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73, - 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20, - 0x6a,0x6f,0x69,0x6e,0x74,0x73,0x5b,0x31,0x32,0x38,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, - 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x4c,0x69,0x67,0x68,0x74,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x54,0x79,0x70,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x6f,0x6e,0x52,0x61,0x6e, - 0x67,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, - 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x20,0x63,0x6f,0x6e,0x65,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, - 0x74,0x20,0x74,0x79,0x70,0x65,0x5f,0x46,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x55, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x34,0x20,0x63,0x61,0x6d,0x65,0x72,0x61,0x50,0x6f,0x73,0x69,0x74, - 0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, - 0x61,0x6d,0x62,0x69,0x65,0x6e,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x62,0x61,0x73,0x65,0x43,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x65,0x6d,0x69,0x73,0x73,0x69, - 0x76,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x6d, - 0x61,0x74,0x65,0x72,0x69,0x61,0x6c,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x20,0x63,0x6f,0x75,0x6e,0x74,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x50,0x61,0x72, - 0x61,0x6d,0x73,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78, - 0x34,0x20,0x73,0x68,0x61,0x64,0x6f,0x77,0x4d,0x61,0x74,0x72,0x69,0x78,0x5b,0x38, - 0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x73,0x68, - 0x61,0x64,0x6f,0x77,0x49,0x6e,0x66,0x6f,0x5b,0x38,0x5d,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x4c,0x69,0x67,0x68,0x74,0x20,0x6c,0x69,0x67,0x68,0x74,0x73,0x5b,0x38,0x5d, - 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x76, - 0x6f,0x69,0x64,0x20,0x64,0x65,0x70,0x74,0x68,0x4d,0x61,0x69,0x6e,0x28,0x29,0x0a, - 0x7b,0x0a,0x7d,0x0a,0x0a, -}; - -static const SceneShaderT sceneShaderDepthMain = { "depthMain", _depthMainSPIRV, sizeof(_depthMainSPIRV), _depthMainDXIL, sizeof(_depthMainDXIL), _depthMainMSL, sizeof(_depthMainMSL) }; - -#endif diff --git a/src/singe.c b/src/singe.c index 15e32f0e4..40052b8bf 100644 --- a/src/singe.c +++ b/src/singe.c @@ -440,6 +440,7 @@ static FontT *_argFont(lua_State *L, const char *method, int32_t index); static int32_t _argInteger(lua_State *L, const char *method, int32_t index); static int32_t _argBody(lua_State *L, const char *method, int32_t index); static int32_t _argMaterial(lua_State *L, const char *method, int32_t index); +static int32_t _argMorph(lua_State *L, const char *method, int32_t node, int32_t index); static int32_t _argMesh(lua_State *L, const char *method, int32_t index); static int32_t _argNode(lua_State *L, const char *method, int32_t index); static int64_t _argInteger64(lua_State *L, const char *method, int32_t index); @@ -627,6 +628,8 @@ static int32_t apiMouseSetMode(lua_State *L); static int32_t apiNodeDelete(lua_State *L); static int32_t apiNodeFind(lua_State *L); static int32_t apiNodeGetChildren(lua_State *L); +static int32_t apiNodeGetMorph(lua_State *L); +static int32_t apiNodeGetMorphs(lua_State *L); static int32_t apiNodeGetName(lua_State *L); static int32_t apiNodeGetParent(lua_State *L); static int32_t apiNodeGetPosition(lua_State *L); @@ -639,6 +642,7 @@ static int32_t apiNodeMove(lua_State *L); static int32_t apiNodeNew(lua_State *L); static int32_t apiNodeRotate(lua_State *L); static int32_t apiNodeSetMesh(lua_State *L); +static int32_t apiNodeSetMorph(lua_State *L); static int32_t apiNodeSetName(lua_State *L); static int32_t apiNodeSetParent(lua_State *L); static int32_t apiNodeSetPosition(lua_State *L); @@ -658,6 +662,7 @@ static int32_t apiOverlayPlot(lua_State *L); static int32_t apiOverlayPrint(lua_State *L); static int32_t apiOverlaySetResolution(lua_State *L); static int32_t apiPhysicsRaycast(lua_State *L); +static int32_t apiPhysicsSet2D(lua_State *L); static int32_t apiPhysicsSetEnabled(lua_State *L); static int32_t apiPhysicsSetGravity(lua_State *L); static int32_t apiSceneEnable(lua_State *L); @@ -826,6 +831,25 @@ static int32_t _argInteger(lua_State *L, const char *method, int32_t index) { } +// A morph target of a node's mesh, by name or by number from 1, checked. +static int32_t _argMorph(lua_State *L, const char *method, int32_t node, int32_t index) { + int32_t target; + + if (lua_type(L, index) == LUA_TSTRING) { + target = meshFindMorph(nodeGetMesh(node), lua_tostring(L, index)); + if (target < 0) { + _luaDie(L, method, "Node %d has no morph target named %s.", node, lua_tostring(L, index)); + } + return target; + } + target = _argInteger(L, method, index) - 1; + if ((target < 0) || (target >= nodeGetMorphCount(node))) { + _luaDie(L, method, "Node %d has no morph target %d.", node, target + 1); + } + return target; +} + + // A node that carries a physics body, checked. static int32_t _argBody(lua_State *L, const char *method, int32_t index) { int32_t node = _argNode(L, method, index); @@ -3679,7 +3703,7 @@ static int32_t apiLightNew(lua_State *L) { } node = lightNew((LightTypeE)type, parent); if (node < 0) { - _luaDie(L, "lightNew", "3D is not available on this machine."); + _luaDie(L, "lightNew", "Unable to create the node."); } _luaTrace(L, "lightNew", "%d type %d under %d", node, type, parent); lua_pushinteger(L, node); @@ -4242,6 +4266,37 @@ static int32_t apiNodeGetChildren(lua_State *L) { } +// weight = nodeGetMorph(node, nameOrIndex) +static int32_t apiNodeGetMorph(lua_State *L) { + int32_t node; + int32_t target; + + _argCheck(L, "nodeGetMorph", 2, 2); + node = _argNode(L, "nodeGetMorph", 1); + target = _argMorph(L, "nodeGetMorph", node, 2); + lua_pushnumber(L, nodeGetMorphWeight(node, target)); + return 1; +} + + +// names = nodeGetMorphs(node): the mesh's morph target names, in order (empty strings when unnamed) +static int32_t apiNodeGetMorphs(lua_State *L) { + int32_t node; + int32_t count; + int32_t x; + + _argCheck(L, "nodeGetMorphs", 1, 1); + node = _argNode(L, "nodeGetMorphs", 1); + count = nodeGetMorphCount(node); + lua_createtable(L, count, 0); + for (x = 0; x < count; x++) { + lua_pushstring(L, meshGetMorphName(nodeGetMesh(node), x)); + lua_rawseti(L, -2, x + 1); + } + return 1; +} + + // name = nodeGetName(node) static int32_t apiNodeGetName(lua_State *L) { _argCheck(L, "nodeGetName", 1, 1); @@ -4342,7 +4397,7 @@ static int32_t apiNodeNew(lua_State *L) { } node = nodeNew(parent); if (node < 0) { - _luaDie(L, "nodeNew", "3D is not available on this machine."); + _luaDie(L, "nodeNew", "Unable to create the node."); } _luaTrace(L, "nodeNew", "%d under %d", node, parent); lua_pushinteger(L, node); @@ -4375,6 +4430,19 @@ static int32_t apiNodeSetMesh(lua_State *L) { } +// nodeSetMorph(node, nameOrIndex, weight): how much of a morph target shows, usually 0 to 1 +static int32_t apiNodeSetMorph(lua_State *L) { + int32_t node; + int32_t target; + + _argCheck(L, "nodeSetMorph", 3, 3); + node = _argNode(L, "nodeSetMorph", 1); + target = _argMorph(L, "nodeSetMorph", node, 2); + nodeSetMorphWeight(node, target, (float)_argNumber(L, "nodeSetMorph", 3)); + return 0; +} + + // nodeSetName(node, name) static int32_t apiNodeSetName(lua_State *L) { _argCheck(L, "nodeSetName", 2, 2); @@ -4788,6 +4856,14 @@ static int32_t apiPhysicsRaycast(lua_State *L) { } +// physicsSet2D(bool): bodies made from now on stay in the XY plane (2D games) +static int32_t apiPhysicsSet2D(lua_State *L) { + _argCheck(L, "physicsSet2D", 1, 1); + physicsSet2D(_argBoolean(L, "physicsSet2D", 1)); + return 0; +} + + // physicsSetEnabled(bool): pauses the simulation without losing it static int32_t apiPhysicsSetEnabled(lua_State *L) { _argCheck(L, "physicsSetEnabled", 1, 1); @@ -6427,6 +6503,8 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co lua_register(_global.luaContext, "nodeDelete", apiNodeDelete); // 3.00 lua_register(_global.luaContext, "nodeFind", apiNodeFind); // 3.00 lua_register(_global.luaContext, "nodeGetChildren", apiNodeGetChildren); // 3.00 + lua_register(_global.luaContext, "nodeGetMorph", apiNodeGetMorph); // 3.00 + lua_register(_global.luaContext, "nodeGetMorphs", apiNodeGetMorphs); // 3.00 lua_register(_global.luaContext, "nodeGetName", apiNodeGetName); // 3.00 lua_register(_global.luaContext, "nodeGetParent", apiNodeGetParent); // 3.00 lua_register(_global.luaContext, "nodeGetPosition", apiNodeGetPosition); // 3.00 @@ -6439,6 +6517,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co lua_register(_global.luaContext, "nodeNew", apiNodeNew); // 3.00 lua_register(_global.luaContext, "nodeRotate", apiNodeRotate); // 3.00 lua_register(_global.luaContext, "nodeSetMesh", apiNodeSetMesh); // 3.00 + lua_register(_global.luaContext, "nodeSetMorph", apiNodeSetMorph); // 3.00 lua_register(_global.luaContext, "nodeSetName", apiNodeSetName); // 3.00 lua_register(_global.luaContext, "nodeSetParent", apiNodeSetParent); // 3.00 lua_register(_global.luaContext, "nodeSetPosition", apiNodeSetPosition); // 3.00 @@ -6458,6 +6537,7 @@ void singe(SDL_Window *window, SDL_Renderer *renderer, SDL_GPUDevice *device, Co lua_register(_global.luaContext, "overlaySetResolution", apiOverlaySetResolution); // 2.00 lua_register(_global.luaContext, "physicsRaycast", apiPhysicsRaycast); // 3.00 + lua_register(_global.luaContext, "physicsSet2D", apiPhysicsSet2D); // 3.00 lua_register(_global.luaContext, "physicsSetEnabled", apiPhysicsSetEnabled); // 3.00 lua_register(_global.luaContext, "physicsSetGravity", apiPhysicsSetGravity); // 3.00 lua_register(_global.luaContext, "sceneEnable", apiSceneEnable); // 3.00 diff --git a/testScripts/Models/AnimatedMorphCube.glb b/testScripts/Models/AnimatedMorphCube.glb new file mode 100644 index 000000000..6acf17403 --- /dev/null +++ b/testScripts/Models/AnimatedMorphCube.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:214ee56160a50dbf22543a1d66dbf860986e87f0efac3d89feac1359d0e6aeab +size 6752 diff --git a/testScripts/Models/DragonModel.glb b/testScripts/Models/DragonModel.glb new file mode 100644 index 000000000..e51ddc35b --- /dev/null +++ b/testScripts/Models/DragonModel.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:459dfa78f2dacba3bea8d82352d97b3648847bccf936a9e85732e818a3759971 +size 131440 diff --git a/testScripts/Models/SingeText.glb b/testScripts/Models/SingeText.glb new file mode 100644 index 000000000..9446c38f2 --- /dev/null +++ b/testScripts/Models/SingeText.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:979ec317a81c7d47978e106cf29f59cfaa9855d11741ac3bc200fb1b92e6fbb6 +size 62136 diff --git a/testScripts/README.md b/testScripts/README.md index 405ac4b8c..37f0e6ee4 100644 --- a/testScripts/README.md +++ b/testScripts/README.md @@ -2,13 +2,15 @@ The Lua scripts used to verify the 3D scene and physics work, one per stage of PLAN.md sections 18 and 19, with everything they load: the Khronos -Duck, Fox and BoxAnimated sample models under Models/, and Box.png. The +Duck, Fox, BoxAnimated and AnimatedMorphCube sample models under Models/, Box.png and crate.png. The disc and the font are the engine's own, addressed as Singe/menuBackground.mkv and Singe/FreeSansBold.ttf like any engine asset. This directory is a complete Singe game directory. Run one from the singe directory with .builddir/Singe-v3.00-Linux-x86_64 -w -d data -v Singe/menuBackground.mkv testScripts/scene6.singe +(scene12 has no disc: give it -C 720x480 instead of -v) + or pack it (singe --pack testScripts testScripts.game) and run an entry with --entry N (the numbers follow games.dat). Every script takes a screenshot or two and quits by itself; the results are in screenshots/. @@ -26,3 +28,8 @@ screenshot or two and quits by itself; the results are in screenshots/. | scene9.singe | 9 | 19.2 | Physics: a crate stack, a ball, a kinematic paddle | | scene10.singe | 10 | 19.3 | Physics: a trigger volume, collision callbacks, a raycast pick | | scene11.singe | 11 | 19.4 | Physics: a hull down a ramp, a mesh torus, hinge, ball and slider joints | +| scene12.singe | 12 | 19.6 | 2D physics drawn with sprites; runs with no GPU (SDL_GPU_DRIVER=nothing) | +| scene13.singe | 13 | 18.8 | Morph targets: the AnimatedMorphCube, animated and driven from script | +| scene14.singe | 14 | - | The solid tangram dragon (Models/DragonModel.glb, a copy of assets/DragonModel.glb) turning under a sun with shadows | +| scene15.singe | 15 | - | The Singe logotype as solid letters (Models/SingeText.glb, a copy of assets/SingeText.glb) turning under a sun with shadows | +| scene16.singe | 16 | - | The dragon perched on the logotype, both models under one parent node, turning together | diff --git a/testScripts/crate.png b/testScripts/crate.png new file mode 100644 index 000000000..8ae5aefa8 --- /dev/null +++ b/testScripts/crate.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:853924469942946176165eab399d7257a29ec139a9b08a624f39017e5375b3b0 +size 627 diff --git a/testScripts/games.dat b/testScripts/games.dat index 24efbaadd..e97a95ba2 100644 --- a/testScripts/games.dat +++ b/testScripts/games.dat @@ -120,4 +120,58 @@ GAMES = { DEVELOPER = "Test", PUBLISHER = "Test", }, + { + TITLE = "Physics2D", + SCRIPT = "testScripts/scene12.singe", + DESCRIPTION = "2D physics drawn with sprites, no 3D or GPU needed.", + YEAR = 2026, + GENRE = "Test", + PLATFORM = "Singe", + DEVELOPER = "Test", + PUBLISHER = "Test", + }, + { + TITLE = "Morphs", + SCRIPT = "testScripts/scene13.singe", + VIDEO = "Singe/menuBackground.mkv", + DESCRIPTION = "Morph targets: the AnimatedMorphCube, animated and scripted.", + YEAR = 2026, + GENRE = "Test", + PLATFORM = "Singe", + DEVELOPER = "Test", + PUBLISHER = "Test", + }, + { + TITLE = "Dragon", + SCRIPT = "testScripts/scene14.singe", + VIDEO = "Singe/menuBackground.mkv", + DESCRIPTION = "The solid tangram dragon model (assets/DragonModel.glb) turning under a sun with shadows.", + YEAR = 2026, + GENRE = "Test", + PLATFORM = "Singe", + DEVELOPER = "Test", + PUBLISHER = "Test", + }, + { + TITLE = "Logo", + SCRIPT = "testScripts/scene15.singe", + VIDEO = "Singe/menuBackground.mkv", + DESCRIPTION = "The Singe logotype as solid letters (assets/SingeText.glb) turning under a sun with shadows.", + YEAR = 2026, + GENRE = "Test", + PLATFORM = "Singe", + DEVELOPER = "Test", + PUBLISHER = "Test", + }, + { + TITLE = "DragonOnLogo", + SCRIPT = "testScripts/scene16.singe", + VIDEO = "Singe/menuBackground.mkv", + DESCRIPTION = "The dragon perched on the Singe logotype, turning together under a sun with shadows.", + YEAR = 2026, + GENRE = "Test", + PLATFORM = "Singe", + DEVELOPER = "Test", + PUBLISHER = "Test", + }, } diff --git a/testScripts/scene12.singe b/testScripts/scene12.singe new file mode 100644 index 000000000..00e8182d1 --- /dev/null +++ b/testScripts/scene12.singe @@ -0,0 +1,70 @@ +-- 2D physics with no 3D at all: crates drawn as sprites fall and pile up in overlay coordinates. +-- Runs on any machine, GPU or not (try it with SDL_GPU_DRIVER=nothing). +local font = fontLoad("Singe/FreeSansBold.ttf", 24) +local box = spriteLoad("testScripts/crate.png") +local frames = 0 +local width, height = overlayGetWidth(), overlayGetHeight() + +fontSelect(font) +physicsSet2D(true) +physicsSetGravity(0, 900, 0) -- Pixels per second squared; overlay Y runs down. + +-- The floor and walls, static, sized in pixels. +local floor = nodeNew() +nodeSetPosition(floor, width / 2, height - 10, 0) +bodyNew(floor, BODY_STATIC, SHAPE_BOX, width, 20, 50) +local leftWall = nodeNew() +nodeSetPosition(leftWall, -10, height / 2, 0) +bodyNew(leftWall, BODY_STATIC, SHAPE_BOX, 20, height, 50) +local rightWall = nodeNew() +nodeSetPosition(rightWall, width + 10, height / 2, 0) +bodyNew(rightWall, BODY_STATIC, SHAPE_BOX, 20, height, 50) + +-- A slope the crates tumble off. +local slope = nodeNew() +nodeSetPosition(slope, width * 0.3, height * 0.55, 0) +nodeSetRotation(slope, 0, 0, 20) +bodyNew(slope, BODY_STATIC, SHAPE_BOX, width * 0.45, 12, 50) + +local size = spriteGetWidth(box) +local crates = {} +local function drop(i) + local crate = nodeNew() + nodeSetPosition(crate, width * 0.15 + (i % 5) * size * 0.6, -size * (1 + i * 0.5), 0) + nodeSetRotation(crate, 0, 0, i * 13) + bodyNew(crate, BODY_DYNAMIC, SHAPE_BOX, size, size, 50) + bodySetMass(crate, 1) + bodySetFriction(crate, 0.6) + bodySetBounce(crate, 0.2) + crates[#crates + 1] = crate +end +for i = 1, 14 do + drop(i) +end + +local resting = 0 +function onCollision(a, b, x, y, z, speed) +end + +function onOverlayUpdate() + frames = frames + 1 + overlayClear() + resting = 0 + for _, crate in ipairs(crates) do + local x, y = nodeGetPosition(crate) + local _, _, angle = nodeGetRotation(crate) + spriteRotate(box, angle) + spriteDraw(box, x, y, true) + if bodyIsResting(crate) then + resting = resting + 1 + end + end + fontPrint(10, 8, string.format("2D physics, frame %d, %d of %d resting", frames, resting, #crates)) + if frames == 40 or frames == 100 or frames == 220 then + singeScreenshot() + end + if frames == 240 then + singeQuit() + end + return OVERLAY_UPDATED +end diff --git a/testScripts/scene13.singe b/testScripts/scene13.singe new file mode 100644 index 000000000..6c10415a3 --- /dev/null +++ b/testScripts/scene13.singe @@ -0,0 +1,52 @@ +-- Morph targets: the Khronos AnimatedMorphCube. Left, its own animation blending the two +-- targets; right, the same model with weights set from the script. +local font = fontLoad("Singe/FreeSansBold.ttf", 28) +local frames = 0 + +fontSelect(font) +discPlay() +sceneEnable(true) +sceneSetBackground(18, 18, 30, 255) +sceneSetAmbient(70, 70, 85) + +local cubeModel = modelLoad("testScripts/Models/AnimatedMorphCube.glb") + +local animated = modelInstance(cubeModel) +nodeSetPosition(animated, -1.6, 0, 0) +nodeSetScale(animated, 0.9) +animationPlay(animated, 1, true) + +local scripted = modelInstance(cubeModel) +nodeSetPosition(scripted, 1.6, 0, 0) +nodeSetScale(scripted, 0.9) +local meshNode = nodeGetChildren(scripted)[1] +local names = nodeGetMorphs(meshNode) +debugPrint("morph targets: " .. #names) + +local sun = lightNew(LIGHT_DIRECTIONAL) +nodeSetPosition(sun, -3, 5, 4) +nodeLookAt(sun, 0, 0, 0) +lightSetIntensity(sun, 1.3) + +local camera = nodeNew() +nodeSetPosition(camera, 0, 2.2, 6) +nodeLookAt(camera, 0, 0, 0) +cameraSet(camera) + +function onOverlayUpdate() + frames = frames + 1 + -- The scripted cube sweeps target 1 up while target 2 comes down. + local t = (frames % 120) / 120 + nodeSetMorph(meshNode, 1, t) + nodeSetMorph(meshNode, 2, 1 - t) + nodeRotate(animated, 0, 0.8, 0) + nodeRotate(scripted, 0, -0.8, 0) + overlayClear() + fontPrint(20, 20, string.format("Morph targets, frame %d scripted: %.2f / %.2f", frames, nodeGetMorph(meshNode, 1), nodeGetMorph(meshNode, 2))) + if frames == 20 or frames == 80 or frames == 140 then + singeScreenshot() + end + if frames == 150 then + singeQuit() + end +end diff --git a/testScripts/scene14.singe b/testScripts/scene14.singe new file mode 100644 index 000000000..673c6e278 --- /dev/null +++ b/testScripts/scene14.singe @@ -0,0 +1,48 @@ +-- Dragon model test: the solid tangram dragon built by util/dragonModel.py and converted to glTF +-- by util/objToGlb.py, turning slowly under a sun with shadows, over the disc. +local font = fontLoad("Singe/FreeSansBold.ttf", 28) +local frames = 0 + +fontSelect(font) +discPlay() +sceneEnable(true) +sceneSetBackground(0, 0, 0, 0) +sceneSetAmbient(70, 70, 80) + +local dragonModel = modelLoad("testScripts/Models/DragonModel.glb") +local dragon = modelInstance(dragonModel) +nodeSetPosition(dragon, 0, -3.5, 0) +nodeSetRotation(dragon, 0, 35, 0) + +local stone = materialNew() +materialSetColor(stone, 120, 120, 130) +materialSetRoughness(stone, 0.9) +local floor = nodeNew() +nodeSetMesh(floor, meshBox(14, 0.2, 10), stone) +nodeSetPosition(floor, 0, -3.6, 0) + +local sun = lightNew(LIGHT_DIRECTIONAL) +nodeSetPosition(sun, -4, 8, 6) +nodeLookAt(sun, 0, 0, 0) +lightSetIntensity(sun, 1.6) +lightSetShadow(sun, true) + +local camera = nodeNew() +nodeSetPosition(camera, 0, 1.5, 12) +nodeLookAt(camera, 0, 0, 0) +cameraSet(camera) + +debugPrint("dragon parts " .. #nodeGetChildren(dragon)) + +function onOverlayUpdate() + frames = frames + 1 + nodeRotate(dragon, 0, 0.75, 0) + overlayClear() + fontPrint(20, 20, "Dragon model, frame " .. frames) + if frames == 30 or frames == 100 or frames == 170 then + singeScreenshot() + end + if frames == 180 then + singeQuit() + end +end diff --git a/testScripts/scene15.singe b/testScripts/scene15.singe new file mode 100644 index 000000000..49a20de08 --- /dev/null +++ b/testScripts/scene15.singe @@ -0,0 +1,48 @@ +-- Logo model test: the Singe logotype built by util/textModel.py and converted to glTF by +-- util/objToGlb.py, turning under a sun with shadows, over the disc. +local font = fontLoad("Singe/FreeSansBold.ttf", 28) +local frames = 0 + +fontSelect(font) +discPlay() +sceneEnable(true) +sceneSetBackground(0, 0, 0, 0) +sceneSetAmbient(70, 70, 80) + +local logoModel = modelLoad("testScripts/Models/SingeText.glb") +local logo = modelInstance(logoModel) +nodeSetPosition(logo, 0, -2.5, 0) +nodeSetRotation(logo, 0, -30, 0) + +local stone = materialNew() +materialSetColor(stone, 120, 120, 130) +materialSetRoughness(stone, 0.9) +local floor = nodeNew() +nodeSetMesh(floor, meshBox(16, 0.2, 10), stone) +nodeSetPosition(floor, 0, -2.6, 0) + +local sun = lightNew(LIGHT_DIRECTIONAL) +nodeSetPosition(sun, -4, 8, 6) +nodeLookAt(sun, 0, 0, 0) +lightSetIntensity(sun, 1.6) +lightSetShadow(sun, true) + +local camera = nodeNew() +nodeSetPosition(camera, 0, 2, 13) +nodeLookAt(camera, 0, 0, 0) +cameraSet(camera) + +debugPrint("logo letters " .. #nodeGetChildren(nodeGetChildren(logo)[1])) + +function onOverlayUpdate() + frames = frames + 1 + nodeRotate(logo, 0, 0.5, 0) + overlayClear() + fontPrint(20, 20, "Logo model, frame " .. frames) + if frames == 30 or frames == 100 or frames == 170 then + singeScreenshot() + end + if frames == 180 then + singeQuit() + end +end diff --git a/testScripts/scene16.singe b/testScripts/scene16.singe new file mode 100644 index 000000000..08fe3270c --- /dev/null +++ b/testScripts/scene16.singe @@ -0,0 +1,54 @@ +-- Dragon and logo together: the solid tangram dragon perched on top of the Singe logotype, the pair +-- turning as one under a sun with shadows, over the disc. +local font = fontLoad("Singe/FreeSansBold.ttf", 28) +local frames = 0 + +fontSelect(font) +discPlay() +sceneEnable(true) +sceneSetBackground(0, 0, 0, 0) +sceneSetAmbient(70, 70, 80) + +-- Both models rest on Y = 0 and are centred on X; the logo is 5 units tall at the top of the S. +local group = nodeNew() +nodeSetPosition(group, 0, -6, 0) +nodeSetRotation(group, 0, -25, 0) + +local logo = modelInstance(modelLoad("testScripts/Models/SingeText.glb")) +nodeSetParent(logo, group) + +local dragon = modelInstance(modelLoad("testScripts/Models/DragonModel.glb")) +nodeSetParent(dragon, group) +nodeSetPosition(dragon, 0, 4.85, 0) +nodeSetScale(dragon, 0.9) + +local stone = materialNew() +materialSetColor(stone, 120, 120, 130) +materialSetRoughness(stone, 0.9) +local floor = nodeNew() +nodeSetMesh(floor, meshBox(18, 0.2, 12), stone) +nodeSetPosition(floor, 0, -6.1, 0) + +local sun = lightNew(LIGHT_DIRECTIONAL) +nodeSetPosition(sun, -5, 10, 7) +nodeLookAt(sun, 0, 0, 0) +lightSetIntensity(sun, 1.6) +lightSetShadow(sun, true) + +local camera = nodeNew() +nodeSetPosition(camera, 0, 3, 15.5) +nodeLookAt(camera, 0, 0, 0) +cameraSet(camera) + +function onOverlayUpdate() + frames = frames + 1 + nodeRotate(group, 0, 0.4, 0) + overlayClear() + fontPrint(20, 20, "Dragon on logo, frame " .. frames) + if frames == 30 or frames == 100 or frames == 170 then + singeScreenshot() + end + if frames == 180 then + singeQuit() + end +end diff --git a/thirdparty/SDL_shadercross/CMakeLists.txt b/thirdparty/SDL_shadercross/CMakeLists.txt new file mode 100644 index 000000000..9d754034b --- /dev/null +++ b/thirdparty/SDL_shadercross/CMakeLists.txt @@ -0,0 +1,549 @@ +# CMake Project for SDL3_shadercross - Simple DirectMedia Layer Shader Cross Compiler +# Written by @thatcosmonaut +cmake_minimum_required(VERSION 3.22...4.0) + +# Version +set(MAJOR_VERSION 3) +set(MINOR_VERSION 0) +set(MICRO_VERSION 0) +set(SDL_REQUIRED_VERSION "3.1.3") + +# option() honors normal variables. +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + +project(SDL3_shadercross LANGUAGES C VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${MICRO_VERSION}") + +set(SHADERCROSS_MAINPROJECT ${SDL3_shadercross_IS_TOP_LEVEL}) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +include(CMakeDependentOption) +include("${CMAKE_CURRENT_LIST_DIR}/cmake/GetGitRevisionDescription.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/cmake/PrivateSdlFunctions.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/cmake/sdlcpu.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/cmake/sdlplatform.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/cmake/sdlmanpages.cmake") + +if(NOT (TARGET SDL3::Headers AND TARGET SDL3::SDL3 AND (TARGET SDL3::SDL3-static OR TARGET SDL3::SDL3-shared))) + find_package(SDL3 ${SDL_REQUIRED_VERSION}) +endif() + +if(TARGET SDL3::SDL3-shared) + set(SDLSHADERCROSS_SHARED_DEFAULT ON) + set(SDLSHADERCROSS_STATIC_DEFAULT OFF) +else() + set(SDLSHADERCROSS_SHARED_DEFAULT OFF) + set(SDLSHADERCROSS_STATIC_DEFAULT ON) +endif() + +# Options +option(SDLSHADERCROSS_DXC "Enable HLSL compilation via DXC" ON) +option(SDLSHADERCROSS_SHARED "Build shared SDL_shadercross library" ${SDLSHADERCROSS_SHARED_DEFAULT}) +option(SDLSHADERCROSS_STATIC "Build static SDL_shadercross library" ${SDLSHADERCROSS_STATIC_DEFAULT}) +option(SDLSHADERCROSS_SPIRVCROSS_SHARED "Link to shared library variants of dependencies" ON) +option(SDLSHADERCROSS_VENDORED "Use vendored dependencies" OFF) +option(SDLSHADERCROSS_CLI "Build command line executable" ON) +cmake_dependent_option(SDLSHADERCROSS_CLI_STATIC "Link CLI with static libraries" OFF "SDLSHADERCROSS_CLI;SDLSHADERCROSS_STATIC;TARGET SDL3::SDL3-static" OFF) +cmake_dependent_option(SDLSHADERCROSS_CLI_LEAKCHECK "Check shadercross for memory leaks" OFF "SDLSHADERCROSS_CLI;TARGET SDL3::SDL3_test" OFF) +option(SDLSHADERCROSS_WERROR "Enable Werror" OFF) +option(SDLSHADERCROSS_INSTALL "Enable installation" ${SHADERCROSS_MAINPROJECT}) +cmake_dependent_option(SDLSHADERCROSS_INSTALL_CPACK "Enable CPack installation" ${SHADERCROSS_MAINPROJECT} "SDLSHADERCROSS_INSTALL" OFF) +cmake_dependent_option(SDLSHADERCROSS_INSTALL_MAN "Install man pages for SDL3_shadercross" OFF "SDLSHADERCROSS_INSTALL" OFF) +cmake_dependent_option(SDLSHADERCROSS_INSTALL_RUNTIME "Download, build and install runtime dependencies" OFF "SDLSHADERCROSS_INSTALL" OFF) + +option(SDLSHADERCROSS_TESTS "Build unit tests?" OFF) +cmake_dependent_option(SDLSHADERCROSS_TESTS_TRACKMEM "Enable memory tracking in tests" OFF SDLSHADERCROSS_TESTS OFF) + +sdl_calculate_derived_version_variables(${MAJOR_VERSION} ${MINOR_VERSION} ${MICRO_VERSION}) +SDL_DetectTargetCPUArchitectures(SDL_CPU_NAMES) +SDL_DetectCMakePlatform() + +if(SDLSHADERCROSS_INSTALL) + include(GNUInstallDirs) +endif() + +if(NOT SDLSHADERCROSS_SHARED AND NOT SDLSHADERCROSS_STATIC) + message(FATAL_ERROR "SDLSHADERCROSS_SHARED and SDLSHADERCROSS_STATIC cannot be both disabled") +endif() + +# Platform Flags +if(APPLE) + set(CMAKE_MACOSX_RPATH ON) + set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0) +endif() + +if(NOT MSVC) + add_compile_options(-pedantic) # -Wno-strict-aliasing +endif() + +set(spirv_static_targets spirv-cross-c spirv-cross-glsl spirv-cross-hlsl spirv-cross-msl spirv-cross-cpp spirv-cross-reflect spirv-cross-core) + +set(pc_requires ) +set(vendored_targets ) +if(SDLSHADERCROSS_VENDORED) + set(SPIRV_CROSS_SKIP_INSTALL ON) + set(SPIRV_CROSS_CLI OFF) + set(SPIRV_CROSS_ENABLE_TESTS OFF) + if(SDLSHADERCROSS_SPIRVCROSS_SHARED) + set(SPIRV_CROSS_SHARED ON) + set(SPIRV_CROSS_STATIC OFF) + else() + set(SPIRV_CROSS_SHARED OFF) + set(SPIRV_CROSS_STATIC ON) + endif() + + sdl_check_project_in_subfolder(external/SPIRV-Cross SPIRV-Cross SDLSHADERCROSS_VENDORED) + set(SPIRV_CROSS_ENABLE_TESTS ON) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + add_subdirectory(external/SPIRV-Cross EXCLUDE_FROM_ALL) + + if(SDLSHADERCROSS_SPIRVCROSS_SHARED) + list(APPEND vendored_targets spirv-cross-c-shared) + list(APPEND pc_requires "spirv-cross-c-shared") + else() + enable_language(CXX) + if(SDLSHADERCROSS_STATIC) + foreach(extra IN LISTS spirv_static_targets) + if(TARGET ${extra}) + list(APPEND vendored_targets ${extra}) + list(APPEND pc_requires "${extra}") + endif() + endforeach() + endif() + endif() + + sdl_check_project_in_subfolder(external/SPIRV-Headers SPIRV-Headers SDLSHADERCROSS_VENDORED) + add_subdirectory(external/SPIRV-Headers EXCLUDE_FROM_ALL) + sdl_check_project_in_subfolder(external/SPIRV-Tools SPIRV-Tools SDLSHADERCROSS_VENDORED) + add_subdirectory(external/SPIRV-Tools EXCLUDE_FROM_ALL) + + sdl_check_project_in_subfolder(external/DirectXShaderCompiler DirectXShaderCompiler SDLSHADERCROSS_VENDORED) + if(MINGW) + set(CMAKE_C_COMPILER_ARCHITECTURE_ID "${SDL_CPU_NAMES}") + endif() + + if(SDLSHADERCROSS_DXC) + # LLVM dependencies of dxc does not support building as a shared library + set(BUILD_SHARED_LIBS OFF) + # Enabling HLSL_ENABLE_DEBUG_ITERATORS blocks DirectXShaderCompiler from adding /D_ITERATOR_DEBUG_LEVEL=0 + set(HLSL_ENABLE_DEBUG_ITERATORS ON) + set(DXC_COVERAGE OFF) + set(HLSL_INCLUDE_TESTS OFF) + set(LLVM_INCLUDE_TESTS OFF) + set(HLSL_DISABLE_SOURCE_GENERATION TRUE) + set(SPIRV_BUILD_TESTS FALSE) + include(external/DirectXShaderCompiler/cmake/caches/PredefinedParams.cmake) + add_subdirectory(external/DirectXShaderCompiler EXCLUDE_FROM_ALL) + add_library(DirectXShaderCompiler::dxcompiler ALIAS dxcompiler) + add_library(DirectXShaderCompiler::dxil ALIAS dxildll) + list(APPEND vendored_targets dxcompiler dxildll) + add_dependencies(dxcompiler dxildll) + endif() + + export(TARGETS ${vendored_targets} NAMESPACE "SDL3_shadercross::" FILE "SDL3_shadercross-vendored-targets.cmake") +else() + if(SDLSHADERCROSS_SPIRVCROSS_SHARED) + if(NOT TARGET spirv_cross_c_shared) + find_package(spirv_cross_c_shared REQUIRED) + endif() + else() + enable_language(CXX) + if(NOT TARGET spirv_cross_c) + find_package(spirv_cross_core QUIET) + find_package(spirv_cross_glsl QUIET) + find_package(spirv_cross_hlsl QUIET) + find_package(spirv_cross_msl QUIET) + find_package(spirv_cross_cpp QUIET) + find_package(spirv_cross_reflect QUIET) + find_package(spirv_cross_c REQUIRED) + endif() + endif() + + if(SDLSHADERCROSS_DXC) + set(DirectXShaderCompiler_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/external/DirectXShaderCompiler-binaries") + find_package(DirectXShaderCompiler REQUIRED) + endif() +endif() + +# Source lists +set(SOURCE_FILES + # Public Headers + include/SDL3_shadercross/SDL_shadercross.h + # Source Files + src/SDL_shadercross.c +) + +set(SDL3_shadercross_targets) +if(SDLSHADERCROSS_SHARED) + list(APPEND SDL3_shadercross_targets SDL3_shadercross-shared) + if(NOT TARGET SDL3::SDL3-shared) + find_package(SDL3 ${SDL_REQUIRED_VERSION} REQUIRED COMPONENTS SDL3-shared) + endif() + + add_library(SDL3_shadercross-shared SHARED ${SOURCE_FILES}) + add_library(SDL3_shadercross::SDL3_shadercross ALIAS SDL3_shadercross-shared) + + set_property(TARGET SDL3_shadercross-shared PROPERTY DEFINE_SYMBOL DLL_EXPORT) + sdl_target_link_option_version_file(SDL3_shadercross-shared "${CMAKE_CURRENT_SOURCE_DIR}/src/SDL_shadercross.sym") + sdl_target_link_options_no_undefined(SDL3_shadercross-shared) + + # Build flags + if(WIN32) + target_sources(SDL3_shadercross-shared PRIVATE "src/version.rc") + set_property(TARGET SDL3_shadercross-shared PROPERTY PREFIX "") + endif() + + # SDL3_shadercross folders as includes, for other targets to consume + target_include_directories(SDL3_shadercross-shared PUBLIC "$") + target_include_directories(SDL3_shadercross-shared PUBLIC "$") + + # Soname + set_target_properties(SDL3_shadercross-shared PROPERTIES + OUTPUT_NAME "SDL3_shadercross" + SOVERSION "${SO_VERSION_MAJOR}" + VERSION "${SO_VERSION}" + ) + + target_link_libraries(SDL3_shadercross-shared PRIVATE + SDL3::SDL3-shared + ) + export(TARGETS SDL3_shadercross-shared NAMESPACE "SDL3_shadercross::" FILE "SDL3_shadercross-shared-targets.cmake") +endif() + + +if(SDLSHADERCROSS_STATIC) + list(APPEND SDL3_shadercross_targets SDL3_shadercross-static) + if(NOT TARGET SDL3::Headers) + find_package(SDL3 ${SDL_REQUIRED_VERSION} REQUIRED COMPONENTS Headers) + endif() + + add_library(SDL3_shadercross-static STATIC ${SOURCE_FILES}) + add_library(SDL3_shadercross::SDL3_shadercross-static ALIAS SDL3_shadercross-static) + + if(NOT MSVC) + set_property(TARGET SDL3_shadercross-static PROPERTY OUTPUT_NAME "SDL3_shadercross") + endif() + + # SDL3_shadercross folders as includes, for other targets to consume + target_include_directories(SDL3_shadercross-static PUBLIC "$") + target_include_directories(SDL3_shadercross-static PUBLIC "$") + + target_link_libraries(SDL3_shadercross-static PUBLIC + SDL3::Headers + ) + export(TARGETS SDL3_shadercross-static NAMESPACE "SDL3_shadercross::" FILE "SDL3_shadercross-static-targets.cmake") +endif() + +# Copy dependencies of SDL3_shadercross-config.cmake to build directory. +# This allows usage of the package without installation. +file(COPY + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdlcpu.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindDirectXShaderCompiler.cmake" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}") + +foreach(target IN LISTS SDL3_shadercross_targets) + sdl_add_warning_options(${target} WARNING_AS_ERROR ${SDLSHADERCROSS_WERROR}) + target_compile_features(${target} PRIVATE c_std_99) + + if(SDLSHADERCROSS_DXC) + add_compile_definitions(SDL_SHADERCROSS_DXC) + endif() + + if(SDLSHADERCROSS_SPIRVCROSS_SHARED) + target_link_libraries(${target} PRIVATE spirv-cross-c-shared) + else() + target_link_libraries(${target} PRIVATE spirv-cross-c) + endif() + if(SDLSHADERCROSS_DXC) + target_link_libraries(${target} PRIVATE DirectXShaderCompiler::dxcompiler) + endif() + if(NOT SDLSHADERCROSS_SPIRVCROSS_SHARED) + # spirv-cross uses C++ + set_property(TARGET ${target} PROPERTY LINKER_LANGUAGE CXX) + endif() +endforeach() + +if(NOT TARGET SDL3_shadercross::SDL3_shadercross) + if(TARGET SDL3_shadercross-shared) + add_library(SDL3_shadercross::SDL3_shadercross ALIAS SDL3_shadercross-shared) + else() + add_library(SDL3_shadercross::SDL3_shadercross ALIAS SDL3_shadercross-static) + endif() +endif() + +if(SDLSHADERCROSS_CLI) + add_executable(shadercross src/cli.c) + sdl_add_warning_options(shadercross WARNING_AS_ERROR ${SDLSHADERCROSS_WERROR}) + sdl_target_link_options_no_undefined(shadercross) + + if(SDLSHADERCROSS_CLI_LEAKCHECK) + target_link_libraries(shadercross PRIVATE SDL3::SDL3_test) + target_compile_definitions(shadercross PRIVATE LEAKCHECK) + endif() + if(SDLSHADERCROSS_CLI_STATIC) + target_link_libraries(shadercross PRIVATE SDL3_shadercross::SDL3_shadercross-static) + target_link_libraries(shadercross PRIVATE SDL3::SDL3-static) + else() + if(NOT TARGET SDL3::SDL3) + find_package(SDL3 ${SDL_REQUIRED_VERSION} REQUIRED CONFIG COMPONENTS SDL3) + endif() + target_link_libraries(shadercross PRIVATE SDL3_shadercross::SDL3_shadercross) + target_link_libraries(shadercross PRIVATE SDL3::SDL3) + endif() +endif() + +if(SDLSHADERCROSS_INSTALL) + if(WIN32 AND NOT MINGW) + set(INSTALL_CMAKEDIR_ROOT_DEFAULT "cmake") + else() + set(INSTALL_CMAKEDIR_ROOT_DEFAULT "${CMAKE_INSTALL_LIBDIR}/cmake") + endif() + set(SDLSHADERCROSS_INSTALL_CMAKEDIR_ROOT "${INSTALL_CMAKEDIR_ROOT_DEFAULT}" CACHE STRING "Root folder where to install SDL3_shadercross cmake related files (SDL3_shadercross subfolder for MSVC projects)") + set(SDLSHADERCROSS_PKGCONFIG_INSTALLDIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + if(WIN32 AND NOT MINGW) + set(SDLSHADERCROSS_INSTALL_CMAKEDIR "${SDLSHADERCROSS_INSTALL_CMAKEDIR_ROOT}") + else() + set(SDLSHADERCROSS_INSTALL_CMAKEDIR "${SDLSHADERCROSS_INSTALL_CMAKEDIR_ROOT}/SDL3_shadercross") + endif() + + if(TARGET SDL3_shadercross-shared) + install(TARGETS SDL3_shadercross-shared EXPORT SDL3_shadercross-shared-export + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library + ) + install(EXPORT SDL3_shadercross-shared-export + FILE SDL3_shadercross-shared-targets.cmake + NAMESPACE SDL3_shadercross:: + DESTINATION "${SDLSHADERCROSS_INSTALL_CMAKEDIR}" + COMPONENT devel + ) + endif() + if(TARGET SDL3_shadercross-static) + install(TARGETS SDL3_shadercross-static EXPORT SDL3_shadercross-static-export + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library + ) + install(EXPORT SDL3_shadercross-static-export + FILE SDL3_shadercross-static-targets.cmake + NAMESPACE SDL3_shadercross:: + DESTINATION "${SDLSHADERCROSS_INSTALL_CMAKEDIR}" + COMPONENT devel + ) + endif() + if(vendored_targets) + install(TARGETS ${vendored_targets} EXPORT SDL3_shadercross-vendored + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT devel + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT library + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT library + ) + install(EXPORT SDL3_shadercross-vendored + FILE SDL3_shadercross-vendored-targets.cmake + NAMESPACE SDL3_shadercross::vendored:: + DESTINATION "${SDLSHADERCROSS_INSTALL_CMAKEDIR}" + COMPONENT devel + ) + endif() + install( + FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/SDL3_shadercross/SDL_shadercross.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/SDL3_shadercross" COMPONENT DEVEL + ) + if(SDLSHADERCROSS_CLI) + install(TARGETS shadercross RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + endif() + + include(CMakePackageConfigHelpers) + configure_package_config_file(cmake/SDL3_shadercrossConfig.cmake.in SDL3_shadercrossConfig.cmake + NO_SET_AND_CHECK_MACRO + INSTALL_DESTINATION "${SDLSHADERCROSS_INSTALL_CMAKEDIR}" + ) + write_basic_package_version_file("${PROJECT_BINARY_DIR}/SDL3_shadercrossConfigVersion.cmake" + COMPATIBILITY AnyNewerVersion + ) + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/SDL3_shadercrossConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/SDL3_shadercrossConfigVersion.cmake" + DESTINATION "${SDLSHADERCROSS_INSTALL_CMAKEDIR}" + COMPONENT devel + ) + if(NOT SDLSHADERCROSS_VENDORED) + install( + FILES + "cmake/sdlcpu.cmake" + "cmake/FindDirectXShaderCompiler.cmake" + DESTINATION "${SDLSHADERCROSS_INSTALL_CMAKEDIR}" + COMPONENT devel + ) + endif() + + if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(INCLUDEDIR_FOR_PKG_CONFIG "${CMAKE_INSTALL_INCLUDEDIR}") + else() + set(INCLUDEDIR_FOR_PKG_CONFIG "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + endif() + if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(LIBDIR_FOR_PKG_CONFIG "${CMAKE_INSTALL_LIBDIR}") + else() + set(LIBDIR_FOR_PKG_CONFIG "\${prefix}/${CMAKE_INSTALL_LIBDIR}") + endif() + + file(RELATIVE_PATH SDL_PATH_PREFIX_RELATIVE_TO_PKGCONFIG "${CMAKE_INSTALL_PREFIX}/${SDLSHADERCROSS_PKGCONFIG_INSTALLDIR}" "${CMAKE_INSTALL_PREFIX}") + string(REGEX REPLACE "[/]+$" "" SDL_PATH_PREFIX_RELATIVE_TO_PKGCONFIG "${SDL_PATH_PREFIX_RELATIVE_TO_PKGCONFIG}") + set(SDL_PKGCONFIG_PREFIX "\${pcfiledir}/${SDL_PATH_PREFIX_RELATIVE_TO_PKGCONFIG}") + if(NOT SDLSHADERCROSS_VENDORED) + if(SDLSHADERCROSS_SPIRVCROSS_SHARED) + set(PC_REQUIRES "spirv-cross-c-shared") + else() + set(PC_REQUIRES "spirv-cross-c") + endif() + endif() + set(PC_LIBS "-ldxcompiler") + configure_file(cmake/sdl3-shadercross.pc.in sdl3-shadercross.pc @ONLY) + + # Always install sdl3-shadercross.pc file: libraries might be different between config modes + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sdl3-shadercross.pc" + DESTINATION "${SDLSHADERCROSS_PKGCONFIG_INSTALLDIR}" COMPONENT devel) + + install(FILES "LICENSE.txt" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/licenses/${PROJECT_NAME}" + COMPONENT library + ) + if(SDLSHADERCROSS_INSTALL_CPACK) + if(MSVC) + set(CPACK_GENERATOR "ZIP") + else() + set(CPACK_GENERATOR "TGZ") + endif() + configure_file(cmake/CPackProjectConfig.cmake.in CPackProjectConfig.cmake @ONLY) + set(CPACK_PROJECT_CONFIG_FILE "${PROJECT_BINARY_DIR}/CPackProjectConfig.cmake") + # CPACK_SOURCE_PACKAGE_FILE_NAME must end with "-src" (so we can block creating a source archive) + set(CPACK_SOURCE_PACKAGE_FILE_NAME "SDL3_shadercross-${PROJECT_VERSION}-src") + set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/dist") + include(CPack) + endif() +endif() + +if(SDLSHADERCROSS_INSTALL_MAN) + sdl_get_git_revision_hash(SDLSHADERCROSS_REVISION) + SDL_generate_manpages( + HEADERS_DIR "${PROJECT_SOURCE_DIR}/include/SDL3_shadercross" + SYMBOL "SDL_ShaderCross_Init" + WIKIHEADERS_PL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/build-scripts/wikiheaders.pl" + REVISION "${SDLSHADERCROSS_REVISION}" + ) +endif() + +if(SDLSHADERCROSS_TESTS) + enable_testing() + add_subdirectory(test) +endif() + +if(SDLSHADERCROSS_INSTALL_RUNTIME) + set(chmod_0755 OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + if(APPLE) + set(rpath_origin "@executable_path") + else() + set(rpath_origin "\$ORIGIN") + endif() + + if(NOT SDLSHADERCROSS_VENDORED) + if(SDLSHADERCROSS_DXC) + install(IMPORTED_RUNTIME_ARTIFACTS DirectXShaderCompiler::dxcompiler DirectXShaderCompiler::dxil RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif() + if(SDLSHADERCROSS_SPIRVCROSS_SHARED) + install(IMPORTED_RUNTIME_ARTIFACTS spirv-cross-c-shared RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + if(WIN32) + install(CODE " + set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM \"windows+pe\") + set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL \"$,dumpbin,objdump>\") + set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND \"${CMAKE_OBJDUMP}\") + file(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR resolved + UNRESOLVED_DEPENDENCIES_VAR unresolved + DIRECTORIES \"$\" \"${CMAKE_INSTALL_PREFIX}${CMAKE_INSTALL_BINDIR}\" + LIBRARIES \"$\" + POST_EXCLUDE_REGEXES \".*[sS][yY][sS][tT][eE][mM]32.*\" \".*[aA][pP][iI]-[mM][sS].*\" \".*[aA][pP][iI]-[eE][xX][tT].*\" + ) + file(INSTALL DESTINATION \"\${CMAKE_INSTALL_PREFIX}/bin\" TYPE SHARED_LIBRARY FILES \${resolved}) + ") + endif() + endif() + endif() + + if(TARGET shadercross) + file(RELATIVE_PATH bin_to_lib "${CMAKE_INSTALL_FULL_BINDIR}" "${CMAKE_INSTALL_FULL_LIBDIR}") + set_property(TARGET shadercross PROPERTY INSTALL_RPATH "${rpath_origin}/${bin_to_lib}") + endif() + + # Install SDL3 + if(SDLSHADERCROSS_CLI AND NOT SDLSHADERCROSS_CLI_STATIC) + install(IMPORTED_RUNTIME_ARTIFACTS SDL3::SDL3-shared RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") + endif() + include(ExternalProject) + + if(NOT WIN32) + set(configure_CFLAGS "${CMAKE_C_FLAGS}") + if(APPLE) + foreach(osx_arch IN LISTS CMAKE_OSX_ARCHITECTURES) + string(APPEND configure_CFLAGS " -arch ${osx_arch}") + endforeach() + endif() + string(REPLACE ";" "$" genex_CMAKE_OSX_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}") + + ExternalProject_Add(spirv_headers + GIT_REPOSITORY "https://github.com/KhronosGroup/SPIRV-Headers.git" + GIT_TAG "main" + CMAKE_ARGS "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_INSTALL_LIBDIR=lib" "-DCMAKE_INSTALL_BINDIR=bin" "-DCMAKE_INSTALL_PREFIX=" "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DCMAKE_OSX_ARCHITECTURES=${genex_CMAKE_OSX_ARCHITECTURES}" "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" + BUILD_COMMAND "${CMAKE_COMMAND}" "--build" "" --config "Release" + INSTALL_COMMAND "${CMAKE_COMMAND}" "--install" "" --config "Release" + ) + ExternalProject_Get_property(spirv_headers INSTALL_DIR) + set(spirv_headers_install_dir "${INSTALL_DIR}") + + ExternalProject_Add(vulkan_headers + GIT_REPOSITORY "https://github.com/KhronosGroup/Vulkan-Headers" + GIT_TAG "main" + CMAKE_ARGS "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_INSTALL_LIBDIR=lib" "-DCMAKE_INSTALL_BINDIR=bin" "-DCMAKE_INSTALL_PREFIX=" "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DCMAKE_OSX_ARCHITECTURES=${genex_CMAKE_OSX_ARCHITECTURES}" "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" + BUILD_COMMAND "${CMAKE_COMMAND}" "--build" "" --config "Release" + INSTALL_COMMAND "${CMAKE_COMMAND}" "--install" "" --config "Release" + ) + ExternalProject_Get_property(vulkan_headers INSTALL_DIR) + set(vulkan_headers_install_dir "${INSTALL_DIR}") + + ExternalProject_Add(vulkan_loader + DEPENDS spirv_headers vulkan_headers + GIT_REPOSITORY "https://github.com/KhronosGroup/Vulkan-Loader.git" + GIT_TAG "main" + CMAKE_ARGS "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_INSTALL_LIBDIR=lib" "-DCMAKE_INSTALL_BINDIR=bin" "-DCMAKE_INSTALL_PREFIX=" "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DCMAKE_OSX_ARCHITECTURES=${genex_CMAKE_OSX_ARCHITECTURES}" "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}" "-DCMAKE_PREFIX_PATH=${vulkan_headers_install_dir}" + BUILD_COMMAND "${CMAKE_COMMAND}" "--build" "" --config "Release" + INSTALL_COMMAND "${CMAKE_COMMAND}" "--install" "" --config "Release" + ) + ExternalProject_Get_property(vulkan_loader INSTALL_DIR) + set(vulkan_loader_install_dir "${INSTALL_DIR}") + + find_package(BISON REQUIRED) + ExternalProject_Add(vkd3d + DEPENDS spirv_headers vulkan_headers vulkan_loader + URL "https://dl.winehq.org/vkd3d/source/vkd3d-2.0.tar.xz" + URL_HASH "SHA256=9ad29bb236808186a47ec66e853b21e6fca59b9dc62a9474b05d5e3eda2710ef" + DOWNLOAD_EXTRACT_TIMESTAMP "1" + CONFIGURE_COMMAND "sh" "/configure" "--prefix=" "--enable-tests=no" "--enable-demos=no" "--disable-doxygen-doc" "CFLAGS=-I${spirv_headers_install_dir}/include -I${vulkan_headers_install_dir}/include -I${vulkan_loader_install_dir}/include ${configure_CFLAGS}" "LDFLAGS=-L${vulkan_loader_install_dir}/lib" "BISON=${BISON_EXECUTABLE}" + BUILD_COMMAND "make" + INSTALL_COMMAND "make" "install" + ) + ExternalProject_Get_property(vkd3d SOURCE_DIR) + ExternalProject_Get_property(vkd3d INSTALL_DIR) + install(DIRECTORY "${INSTALL_DIR}/lib/" DESTINATION "${CMAKE_INSTALL_LIBDIR}" FILES_MATCHING PATTERN "*.so*" PERMISSIONS ${chmod_0755}) + install(DIRECTORY "${INSTALL_DIR}/lib/" DESTINATION "${CMAKE_INSTALL_LIBDIR}" FILES_MATCHING PATTERN "*.dylib*" PERMISSIONS ${chmod_0755}) + install(FILES "${SOURCE_DIR}/COPYING" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/licenses/vkd3d") + endif() + if(LINUX) + find_program(PATCHELF_BIN NAMES "patchelf" REQUIRED) + install(CODE "file(GLOB so_paths \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/*so*\")\n foreach(so_path \${so_paths})\n if(NOT IS_SYMLINK \${so_path})\n message(STATUS \"Adding \\\"\$ORIGIN\\\" to RPATH of \${so_path}\")\n execute_process(COMMAND ${PATCHELF_BIN} \"\${so_path}\" --add-rpath \"\$ORIGIN\")\n endif()\n endforeach()") + elseif(APPLE) + find_program(INT_BIN NAMES "install_name_tool" REQUIRED) + install(CODE "file(GLOB so_paths \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/*dylib*\")\n foreach(so_path \${so_paths})\n if(NOT IS_SYMLINK \${so_path})\n message(STATUS \"Adding \\\"\@loader_path\\\" to RPATH of \${so_path}\")\n execute_process(COMMAND ${INT_BIN} \"\${so_path}\" -add_rpath \"\@loader_path\")\n endif()\n endforeach()") + endif() +endif() diff --git a/thirdparty/SDL_shadercross/LICENSE.txt b/thirdparty/SDL_shadercross/LICENSE.txt new file mode 100644 index 000000000..b12e9a977 --- /dev/null +++ b/thirdparty/SDL_shadercross/LICENSE.txt @@ -0,0 +1,18 @@ +Copyright (C) 2024 Sam Lantinga + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + diff --git a/thirdparty/SDL_shadercross/cmake/CPackProjectConfig.cmake.in b/thirdparty/SDL_shadercross/cmake/CPackProjectConfig.cmake.in new file mode 100644 index 000000000..4c7ce7d56 --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/CPackProjectConfig.cmake.in @@ -0,0 +1,37 @@ +if(CPACK_PACKAGE_FILE_NAME MATCHES ".*-src$") + message(FATAL_ERROR "Creating source archives is not supported.") +endif() + +set(PROJECT_NAME "@PROJECT_NAME@") +set(PROJECT_VERSION "@PROJECT_VERSION@") +set(PROJECT_SOURCE_DIR "@PROJECT_SOURCE_DIR@") +set(SDL_CMAKE_PLATFORM "@SDL_CMAKE_PLATFORM@") +set(SDL_CPU_NAMES "@SDL_CPU_NAMES@") +list(SORT SDL_CPU_NAMES) + +string(TOLOWER "${SDL_CMAKE_PLATFORM}" SDL_CMAKE_PLATFORM) +string(TOLOWER "${SDL_CPU_NAMES}" SDL_CPU_NAMES) +if(lower_sdl_cmake_platform STREQUAL lower_sdl_cpu_names) + set(SDL_CPU_NAMES_WITH_DASHES) +endif() + +string(REPLACE ";" "-" SDL_CPU_NAMES_WITH_DASHES "${SDL_CPU_NAMES}") +if(SDL_CPU_NAMES_WITH_DASHES) + set(SDL_CPU_NAMES_WITH_DASHES "-${SDL_CPU_NAMES_WITH_DASHES}") +endif() + +set(MSVC @MSVC@) +set(MINGW @MINGW@) +if(MSVC) + set(SDL_CMAKE_PLATFORM "${SDL_CMAKE_PLATFORM}-VC") +elseif(MINGW) + set(SDL_CMAKE_PLATFORM "${SDL_CMAKE_PLATFORM}-mingw") +endif() + +set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}-${SDL_CMAKE_PLATFORM}${SDL_CPU_NAMES_WITH_DASHES}") + +if(CPACK_GENERATOR STREQUAL "DragNDrop") + set(CPACK_DMG_VOLUME_NAME "@PROJECT_NAME@ @PROJECT_VERSION@") + # FIXME: use pre-built/create .DS_Store through AppleScript (CPACK_DMG_DS_STORE/CPACK_DMG_DS_STORE_SETUP_SCRIPT) + set(CPACK_DMG_DS_STORE "${PROJECT_SOURCE_DIR}/Xcode/SDL/pkg-support/resources/SDL_DS_Store") +endif() diff --git a/thirdparty/SDL_shadercross/cmake/FindDirectXShaderCompiler.cmake b/thirdparty/SDL_shadercross/cmake/FindDirectXShaderCompiler.cmake new file mode 100644 index 000000000..55a8c6173 --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/FindDirectXShaderCompiler.cmake @@ -0,0 +1,60 @@ +set(required_vars) + +if(WIN32) + find_path(DirectXShaderCompiler_INCLUDE_PATH NAMES "dxcapi.h" PATH_SUFFIXES "inc" "windows/inc" HINTS ${DirectXShaderCompiler_ROOT}) + if(SDL_CPU_ARM64) + set(extra_bin_suffix "bin/arm64" "windows/bin/arm64") + set(extra_lib_suffix "lib/arm64" "windows/lib/arm64") + elseif(SDL_CPU_X86) + set(extra_bin_suffix "bin/x86" "windows/bin/x86") + set(extra_lib_suffix "lib/x86" "windows/lib/x86") + elseif(SDL_CPU_X64) + set(extra_bin_suffix "bin/x64" "windows/bin/x64") + set(extra_lib_suffix "lib/x64" "windows/lib/x64") + endif() + find_file(DirectXShaderCompiler_dxcompiler_BINARY NAMES "dxcompiler.dll" PATH_SUFFIXES "bin" ${extra_bin_suffix} HINTS ${DirectXShaderCompiler_ROOT}) + find_library(DirectXShaderCompiler_dxcompiler_LIBRARY NAMES "dxcompiler" "dxcompiler.lib" PATH_SUFFIXES "lib" ${extra_lib_suffix} HINTS ${DirectXShaderCompiler_ROOT}) + find_file(DirectXShaderCompiler_dxil_BINARY NAMES "dxil.dll" PATH_SUFFIXES "bin" ${extra_bin_suffix} HINTS ${DirectXShaderCompiler_ROOT}) + set(required_vars + DirectXShaderCompiler_INCLUDE_PATH + DirectXShaderCompiler_dxcompiler_BINARY + DirectXShaderCompiler_dxcompiler_LIBRARY + DirectXShaderCompiler_dxil_BINARY + ) +else() + find_path(DirectXShaderCompiler_INCLUDE_PATH NAMES "dxcapi.h" PATH_SUFFIXES "include" "include/dxc" "linux/include" "linux/include/dxc" HINTS ${DirectXShaderCompiler_ROOT}) + find_library(DirectXShaderCompiler_dxcompiler_LIBRARY NAMES "dxcompiler" PATH_SUFFIXES "lib" "linux/lib" HINTS ${DirectXShaderCompiler_ROOT}) + find_library(DirectXShaderCompiler_dxil_LIBRARY NAMES "dxil" PATH_SUFFIXES "lib" "linux/lib" HINTS ${DirectXShaderCompiler_ROOT}) + set(required_vars + DirectXShaderCompiler_INCLUDE_PATH + DirectXShaderCompiler_dxcompiler_LIBRARY + DirectXShaderCompiler_dxil_LIBRARY + ) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(DirectXShaderCompiler + REQUIRED_VARS ${required_vars} +) + +if(DirectXShaderCompiler_FOUND) + if(NOT TARGET DirectXShaderCompiler::dxcompiler) + add_library(DirectXShaderCompiler::dxcompiler IMPORTED SHARED) + set_property(TARGET DirectXShaderCompiler::dxcompiler PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${DirectXShaderCompiler_INCLUDE_PATH}") + if(WIN32) + set_property(TARGET DirectXShaderCompiler::dxcompiler PROPERTY IMPORTED_LOCATION "${DirectXShaderCompiler_dxcompiler_BINARY}") + set_property(TARGET DirectXShaderCompiler::dxcompiler PROPERTY IMPORTED_IMPLIB "${DirectXShaderCompiler_dxcompiler_LIBRARY}") + else() + set_property(TARGET DirectXShaderCompiler::dxcompiler PROPERTY IMPORTED_LOCATION "${DirectXShaderCompiler_dxcompiler_LIBRARY}") + endif() + endif() + if(NOT TARGET DirectXShaderCompiler::dxil) + add_library(DirectXShaderCompiler::dxil IMPORTED SHARED) + if(WIN32) + set_property(TARGET DirectXShaderCompiler::dxil PROPERTY IMPORTED_LOCATION "${DirectXShaderCompiler_dxil_BINARY}") + else() + set_property(TARGET DirectXShaderCompiler::dxil PROPERTY IMPORTED_LOCATION "${DirectXShaderCompiler_dxil_LIBRARY}") + endif() + endif() +endif() diff --git a/thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake b/thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 000000000..a08895c64 --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,284 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_describe_working_tree( [ ...]) +# +# Returns the results of git describe on the working tree (--dirty option), +# and adjusting the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2020 Ryan Pavlik +# http://academic.cleardefinition.com +# +# Copyright 2009-2013, Iowa State University. +# Copyright 2013-2020, Ryan Pavlik +# Copyright 2013-2020, Contributors +# SPDX-License-Identifier: BSL-1.0 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +# Function _git_find_closest_git_dir finds the next closest .git directory +# that is part of any directory in the path defined by _start_dir. +# The result is returned in the parent scope variable whose name is passed +# as variable _git_dir_var. If no .git directory can be found, the +# function returns an empty string via _git_dir_var. +# +# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and +# neither foo nor bar contain a file/directory .git. This will return +# C:/bla/.git +# +function(_git_find_closest_git_dir _start_dir _git_dir_var) + set(cur_dir "${_start_dir}") + set(git_dir "${_start_dir}/.git") + while(NOT EXISTS "${git_dir}") + # .git dir not found, search parent directories + set(git_previous_parent "${cur_dir}") + get_filename_component(cur_dir "${cur_dir}" DIRECTORY) + if(cur_dir STREQUAL git_previous_parent) + # We have reached the root directory, we are not in git + set(${_git_dir_var} + "" + PARENT_SCOPE) + return() + endif() + set(git_dir "${cur_dir}/.git") + endwhile() + set(${_git_dir_var} + "${git_dir}" + PARENT_SCOPE) +endfunction() + +function(get_git_head_revision _refspecvar _hashvar) + _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) + + if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) + else() + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) + endif() + if(NOT "${GIT_DIR}" STREQUAL "") + file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" + "${GIT_DIR}") + if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) + # We've gone above the CMake root dir. + set(GIT_DIR "") + endif() + endif() + if("${GIT_DIR}" STREQUAL "") + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # Check if the current source dir is a git submodule or a worktree. + # In both cases .git is a file instead of a directory. + # + if(NOT IS_DIRECTORY ${GIT_DIR}) + # The following git command will return a non empty string that + # points to the super project working tree if the current + # source dir is inside a git submodule. + # Otherwise the command will return an empty string. + # + execute_process( + COMMAND "${GIT_EXECUTABLE}" rev-parse + --show-superproject-working-tree + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT "${out}" STREQUAL "") + # If out is empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE + ${submodule}) + string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} + ABSOLUTE) + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + else() + # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree + file(READ ${GIT_DIR} worktree_ref) + # The .git directory contains a path to the worktree information directory + # inside the parent git repo of the worktree. + # + string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir + ${worktree_ref}) + string(STRIP ${git_worktree_dir} git_worktree_dir) + _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) + set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") + endif() + else() + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${HEAD_SOURCE_FILE}") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() + + #message(STATUS "Arguments to execute_process: ${ARGN}") + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_describe_working_tree _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE) + endif() +endfunction() diff --git a/thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake.in b/thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 000000000..116efc4e4 --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,43 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright 2009-2012, Iowa State University +# Copyright 2011-2015, Contributors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# SPDX-License-Identifier: BSL-1.0 + +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/thirdparty/SDL_shadercross/cmake/PrivateSdlFunctions.cmake b/thirdparty/SDL_shadercross/cmake/PrivateSdlFunctions.cmake new file mode 100644 index 000000000..8d71ede07 --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/PrivateSdlFunctions.cmake @@ -0,0 +1,363 @@ +# This file is shared amongst SDL_image/SDL_mixer/SDL_ttf + +include(CheckCCompilerFlag) +include(CheckCSourceCompiles) +include(CMakePushCheckState) + +macro(sdl_calculate_derived_version_variables MAJOR MINOR MICRO) + set(SO_VERSION_MAJOR "0") + set(SO_VERSION_MINOR "${MINOR_VERSION}") + set(SO_VERSION_MICRO "${MICRO_VERSION}") + set(SO_VERSION "${SO_VERSION_MAJOR}.${SO_VERSION_MINOR}.${SO_VERSION_MICRO}") + + if(MINOR MATCHES "[02468]$") + math(EXPR DYLIB_COMPAT_VERSION_MAJOR "100 * ${MINOR} + 1") + set(DYLIB_COMPAT_VERSION_MINOR "0") + math(EXPR DYLIB_CURRENT_VERSION_MAJOR "${DYLIB_COMPAT_VERSION_MAJOR}") + set(DYLIB_CURRENT_VERSION_MINOR "${MICRO}") + else() + math(EXPR DYLIB_COMPAT_VERSION_MAJOR "100 * ${MINOR} + ${MICRO} + 1") + set(DYLIB_COMPAT_VERSION_MINOR "0") + math(EXPR DYLIB_CURRENT_VERSION_MAJOR "${DYLIB_COMPAT_VERSION_MAJOR}") + set(DYLIB_CURRENT_VERSION_MINOR "0") + endif() + set(DYLIB_COMPAT_VERSION_MICRO "0") + set(DYLIB_CURRENT_VERSION_MICRO "0") + + set(DYLIB_CURRENT_VERSION "${DYLIB_CURRENT_VERSION_MAJOR}.${DYLIB_CURRENT_VERSION_MINOR}.${DYLIB_CURRENT_VERSION_MICRO}") + set(DYLIB_COMPAT_VERSION "${DYLIB_COMPAT_VERSION_MAJOR}.${DYLIB_COMPAT_VERSION_MINOR}.${DYLIB_COMPAT_VERSION_MICRO}") +endmacro() + +function(read_absolute_symlink DEST PATH) + file(READ_SYMLINK "${PATH}" p) + if(NOT IS_ABSOLUTE "${p}") + get_filename_component(pdir "${PATH}" DIRECTORY) + set(p "${pdir}/${p}") + endif() + get_filename_component(p "${p}" ABSOLUTE) + set("${DEST}" "${p}" PARENT_SCOPE) +endfunction() + +function(win32_implib_identify_dll DEST IMPLIB) + cmake_parse_arguments(ARGS "NOTFATAL" "" "" ${ARGN}) + if(CMAKE_DLLTOOL) + execute_process( + COMMAND "${CMAKE_DLLTOOL}" --identify "${IMPLIB}" + RESULT_VARIABLE retcode + OUTPUT_VARIABLE stdout + ERROR_VARIABLE stderr) + if(NOT retcode EQUAL 0) + if(NOT ARGS_NOTFATAL) + message(FATAL_ERROR "${CMAKE_DLLTOOL} failed.") + else() + set("${DEST}" "${DEST}-NOTFOUND" PARENT_SCOPE) + return() + endif() + endif() + string(STRIP "${stdout}" result) + set(${DEST} "${result}" PARENT_SCOPE) + elseif(MSVC) + get_filename_component(CMAKE_C_COMPILER_DIRECTORY "${CMAKE_C_COMPILER}" DIRECTORY CACHE) + find_program(CMAKE_DUMPBIN NAMES dumpbin PATHS "${CMAKE_C_COMPILER_DIRECTORY}") + if(CMAKE_DUMPBIN) + execute_process( + COMMAND "${CMAKE_DUMPBIN}" "-headers" "${IMPLIB}" + RESULT_VARIABLE retcode + OUTPUT_VARIABLE stdout + ERROR_VARIABLE stderr) + if(NOT retcode EQUAL 0) + if(NOT ARGS_NOTFATAL) + message(FATAL_ERROR "dumpbin failed.") + else() + set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) + return() + endif() + endif() + string(REGEX MATCH "DLL name[ ]+:[ ]+([^\n]+)\n" match "${stdout}") + if(NOT match) + if(NOT ARGS_NOTFATAL) + message(FATAL_ERROR "dumpbin did not find any associated dll for ${IMPLIB}.") + else() + set(${DEST} "${DEST}-NOTFOUND" PARENT_SCOPE) + return() + endif() + endif() + set(result "${CMAKE_MATCH_1}") + set(${DEST} "${result}" PARENT_SCOPE) + else() + message(FATAL_ERROR "Cannot find dumpbin, please set CMAKE_DUMPBIN cmake variable") + endif() + else() + if(NOT ARGS_NOTFATAL) + message(FATAL_ERROR "Don't know how to identify dll from import library. Set CMAKE_DLLTOOL (for mingw) or CMAKE_DUMPBIN (for MSVC)") + else() + set(${DEST} "${DEST}-NOTFOUND") + endif() + endif() +endfunction() + +function(get_actual_target) + set(dst "${ARGV0}") + set(target "${${dst}}") + set(input "${target}") + get_target_property(alias "${target}" ALIASED_TARGET) + while(alias) + set(target "${alias}") + get_target_property(alias "${target}" ALIASED_TARGET) + endwhile() + message(DEBUG "get_actual_target(\"${input}\") -> \"${target}\"") + set("${dst}" "${target}" PARENT_SCOPE) +endfunction() + +function(target_get_dynamic_library DEST TARGET) + set(result) + if(DEFINED ${DEST}) + if(NOT EXISTS "${${DEST}}") + message(FATAL_ERROR "${DEST}=${${DEST}} does not exist") + endif() + get_filename_component(filename ${${DEST}} NAME) + set(${DEST} ${filename} PARENT_SCOPE) + else() + get_actual_target(TARGET) + if(WIN32) + # Use the target dll of the import library + set(props_to_check IMPORTED_IMPLIB) + if(CMAKE_BUILD_TYPE) + list(APPEND props_to_check IMPORTED_IMPLIB_${CMAKE_BUILD_TYPE}) + endif() + list(APPEND props_to_check IMPORTED_LOCATION) + if(CMAKE_BUILD_TYPE) + list(APPEND props_to_check IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) + endif() + foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) + list(APPEND props_to_check IMPORTED_IMPLIB_${config_type}) + list(APPEND props_to_check IMPORTED_LOCATION_${config_type}) + endforeach() + + foreach(prop_to_check ${props_to_check}) + if(NOT result) + get_target_property(propvalue "${TARGET}" ${prop_to_check}) + if(propvalue AND EXISTS "${propvalue}") + win32_implib_identify_dll(result "${propvalue}" NOTFATAL) + endif() + endif() + endforeach() + else() + # 1. find the target library a file might be symbolic linking to + # 2. find all other files in the same folder that symolic link to it + # 3. sort all these files, and select the 1st item on Linux, and last on Macos + set(location_properties IMPORTED_LOCATION) + if(CMAKE_BUILD_TYPE) + list(APPEND location_properties IMPORTED_LOCATION_${CMAKE_BUILD_TYPE}) + endif() + foreach (config_type ${CMAKE_CONFIGURATION_TYPES} RELEASE DEBUG RELWITHDEBINFO MINSIZEREL) + list(APPEND location_properties IMPORTED_LOCATION_${config_type}) + endforeach() + if(APPLE) + set(valid_shared_library_regex "\\.[0-9]+\\.dylib$") + else() + set(valid_shared_library_regex "\\.so\\.([0-9.]+)?[0-9]") + endif() + foreach(location_property ${location_properties}) + if(NOT result) + get_target_property(library_path "${TARGET}" ${location_property}) + message(DEBUG "get_target_property(${TARGET} ${location_propert}) -> ${library_path}") + if(EXISTS "${library_path}") + get_filename_component(library_path "${library_path}" ABSOLUTE) + while (IS_SYMLINK "${library_path}") + read_absolute_symlink(library_path "${library_path}") + endwhile() + message(DEBUG "${TARGET} -> ${library_path}") + get_filename_component(libdir "${library_path}" DIRECTORY) + file(GLOB subfiles "${libdir}/*") + set(similar_files "${library_path}") + foreach(subfile ${subfiles}) + if(IS_SYMLINK "${subfile}") + read_absolute_symlink(subfile_target "${subfile}") + while(IS_SYMLINK "${subfile_target}") + read_absolute_symlink(subfile_target "${subfile_target}") + endwhile() + get_filename_component(subfile_target "${subfile_target}" ABSOLUTE) + if(subfile_target STREQUAL library_path AND subfile MATCHES "${valid_shared_library_regex}") + list(APPEND similar_files "${subfile}") + endif() + endif() + endforeach() + list(SORT similar_files) + message(DEBUG "files that are similar to \"${library_path}\"=${similar_files}") + if(APPLE) + list(REVERSE similar_files) + endif() + list(GET similar_files 0 item) + get_filename_component(result "${item}" NAME) + endif() + endif() + endforeach() + endif() + if(result) + string(TOLOWER "${result}" result_lower) + if(WIN32 OR OS2) + if(NOT result_lower MATCHES ".*dll") + message(FATAL_ERROR "\"${result}\" is not a .dll library") + endif() + elseif(APPLE) + if(NOT result_lower MATCHES ".*dylib.*") + message(FATAL_ERROR "\"${result}\" is not a .dylib shared library") + endif() + else() + if(NOT result_lower MATCHES ".*so.*") + message(FATAL_ERROR "\"${result}\" is not a .so shared library") + endif() + endif() + else() + get_target_property(target_type ${TARGET} TYPE) + if(target_type MATCHES "SHARED_LIBRARY|MODULE_LIBRARY") + # OK + elseif(target_type MATCHES "STATIC_LIBRARY|OBJECT_LIBRARY|INTERFACE_LIBRARY|EXECUTABLE") + message(SEND_ERROR "${TARGET} is not a shared library, but has type=${target_type}") + else() + message(WARNING "Unable to extract dynamic library from target=${TARGET}, type=${target_type}.") + endif() + # TARGET_SONAME_FILE is not allowed for DLL target platforms. + if(WIN32) + set(result "$") + else() + set(result "$") + endif() + endif() + set(${DEST} ${result} PARENT_SCOPE) + endif() +endfunction() + +function(sdl_check_project_in_subfolder relative_subfolder name vendored_option) + cmake_parse_arguments(ARG "" "FILE" "" ${ARGN}) + if(NOT ARG_FILE) + set(ARG_FILE "CMakeLists.txt") + endif() + if(NOT EXISTS "${PROJECT_SOURCE_DIR}/${relative_subfolder}/${ARG_FILE}") + message(FATAL_ERROR "Could not find ${ARG_FILE} for ${name} in ${relative_subfolder}.\n" + "Run the download script in the external folder, or re-configure with -D${vendored_option}=OFF to use system packages.") + endif() +endfunction() + +macro(sdl_check_linker_flag flag var) + # FIXME: Use CheckLinkerFlag module once cmake minimum version >= 3.18 + cmake_push_check_state(RESET) + set(CMAKE_REQUIRED_LINK_OPTIONS "${flag}") + check_c_source_compiles("int main() { return 0; }" ${var} FAIL_REGEX "(unsupported|syntax error|unrecognized option)") + cmake_pop_check_state() +endmacro() + +function(SDL_detect_linker) + if(CMAKE_VERSION VERSION_LESS 3.29) + if(NOT DEFINED SDL_CMAKE_C_COMPILER_LINKER_ID) + execute_process(COMMAND ${CMAKE_LINKER} -v OUTPUT_VARIABLE LINKER_OUTPUT ERROR_VARIABLE LINKER_OUTPUT) + string(REGEX REPLACE "[\r\n]" " " LINKER_OUTPUT "${LINKER_OUTPUT}") + if(LINKER_OUTPUT MATCHES ".*Microsoft.*") + set(linker MSVC) + else() + set(linker GNUlike) + endif() + message(STATUS "Linker identification: ${linker}") + set(SDL_CMAKE_C_COMPILER_LINKER_ID "${linker}" CACHE STRING "Linker identification") + mark_as_advanced(SDL_CMAKE_C_COMPILER_LINKER_ID) + endif() + set(CMAKE_C_COMPILER_LINKER_ID "${SDL_CMAKE_C_COMPILER_LINKER_ID}" PARENT_SCOPE) + endif() +endfunction() + +function(check_linker_support_version_script VAR) + SDL_detect_linker() + if(CMAKE_C_COMPILER_LINKER_ID MATCHES "^(MSVC)$") + set(LINKER_SUPPORTS_VERSION_SCRIPT FALSE) + else() + cmake_push_check_state(RESET) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dummy.sym" "n_0 {\n global:\n func;\n local: *;\n};\n") + list(APPEND CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/dummy.sym") + check_c_source_compiles("int func(void) {return 0;} int main(int argc,char*argv[]){(void)argc;(void)argv;return func();}" LINKER_SUPPORTS_VERSION_SCRIPT FAIL_REGEX "(unsupported|syntax error|unrecognized option)") + cmake_pop_check_state() + endif() + set(${VAR} "${LINKER_SUPPORTS_VERSION_SCRIPT}" PARENT_SCOPE) +endfunction() + +function(sdl_target_link_options_no_undefined TARGET) + if(NOT MSVC AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*") + if(CMAKE_C_COMPILER_ID MATCHES "AppleClang") + target_link_options(${TARGET} PRIVATE "-Wl,-undefined,error") + else() + sdl_check_linker_flag("-Wl,--no-undefined" HAVE_WL_NO_UNDEFINED) + if(HAVE_WL_NO_UNDEFINED AND NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") AND WIN32)) + target_link_options(${TARGET} PRIVATE "-Wl,--no-undefined") + endif() + endif() + endif() +endfunction() + +function(sdl_target_link_option_version_file TARGET VERSION_SCRIPT) + check_linker_support_version_script(HAVE_WL_VERSION_SCRIPT) + if(HAVE_WL_VERSION_SCRIPT) + target_link_options(${TARGET} PRIVATE "-Wl,--version-script=${VERSION_SCRIPT}") + set_property(TARGET ${TARGET} APPEND PROPERTY LINK_DEPENDS "${VERSION_SCRIPT}") + else() + if(LINUX OR ANDROID) + message(FATAL_ERROR "Linker does not support '-Wl,--version-script=xxx.sym'. This is required on the current host platform.") + endif() + endif() +endfunction() + +function(sdl_add_warning_options TARGET) + cmake_parse_arguments(ARGS "" "WARNING_AS_ERROR" "" ${ARGN}) + if(MSVC) + target_compile_options(${TARGET} PRIVATE /W2) + else() + target_compile_options(${TARGET} PRIVATE -Wall -Wextra -Wno-unused-parameter) + endif() + if(ARGS_WARNING_AS_ERROR) + if(MSVC) + target_compile_options(${TARGET} PRIVATE /WX) + else() + target_compile_options(${TARGET} PRIVATE -Werror) + endif() + endif() +endfunction() + +function(sdl_no_deprecated_errors TARGET) + check_c_compiler_flag(-Wno-error=deprecated-declarations HAVE_WNO_ERROR_DEPRECATED_DECLARATIONS) + if(HAVE_WNO_ERROR_DEPRECATED_DECLARATIONS) + target_compile_options(${TARGET} PRIVATE "-Wno-error=deprecated-declarations") + endif() +endfunction() + +function(sdl_get_git_revision_hash VARNAME) + set("${VARNAME}" "" CACHE STRING "${PROJECT_NAME} revision") + set(revision "${${VARNAME}}") + if(NOT revision) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt") + # If VERSION.txt exists, it contains the SDL version + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt" revision_version) + string(STRIP "${revision_version}" revision_version) + else() + # If VERSION.txt does not exist, use git to calculate a version + git_describe(revision_version) + if(NOT revision_version) + set(revision_version "${PROJECT_VERSION}-no-vcs") + endif() + endif() + set(revision "${revision_version}") + endif() + set("${VARNAME}" "${revision}" PARENT_SCOPE) +endfunction() + +function(SDL_install_pdb TARGET DIRECTORY) + get_property(type TARGET ${TARGET} PROPERTY TYPE) + if(type MATCHES "^(SHARED_LIBRARY|EXECUTABLE)$") + install(FILES $ DESTINATION "${DIRECTORY}" OPTIONAL) + elseif(type STREQUAL "STATIC_LIBRARY") + # FIXME: Use $= @SDL_REQUIRED_VERSION@ +Libs: -L${libdir} -lSDL3_shadercross +Requires.private: @PC_REQUIRES@ +Libs.private: @PC_LIBS@ +Cflags: -I${includedir} diff --git a/thirdparty/SDL_shadercross/cmake/sdlcpu.cmake b/thirdparty/SDL_shadercross/cmake/sdlcpu.cmake new file mode 100644 index 000000000..a27e7329c --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/sdlcpu.cmake @@ -0,0 +1,158 @@ +function(SDL_DetectTargetCPUArchitectures DETECTED_ARCHS) + + set(known_archs EMSCRIPTEN ARM32 ARM64 ARM64EC LOONGARCH64 POWERPC32 POWERPC64 RISCV32 RISCV64 X86 X64) + + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + foreach(known_arch IN LISTS known_archs) + set(SDL_CPU_${known_arch} "0" PARENT_SCOPE) + endforeach() + set(detected_archs) + foreach(osx_arch IN LISTS CMAKE_OSX_ARCHITECTURES) + if(osx_arch STREQUAL "x86_64") + set(SDL_CPU_X64 "1" PARENT_SCOPE) + list(APPEND detected_archs "X64") + elseif(osx_arch STREQUAL "arm64") + set(SDL_CPU_ARM64 "1" PARENT_SCOPE) + list(APPEND detected_archs "ARM64") + endif() + endforeach() + set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE) + return() + endif() + + set(detected_archs) + foreach(known_arch IN LISTS known_archs) + if(SDL_CPU_${known_arch}) + list(APPEND detected_archs "${known_arch}") + endif() + endforeach() + + if(detected_archs) + set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE) + return() + endif() + + set(arch_check_ARM32 "defined(__arm__) || defined(_M_ARM)") + set(arch_check_ARM64 "defined(__aarch64__) || defined(_M_ARM64)") + set(arch_check_ARM64EC "defined(_M_ARM64EC)") + set(arch_check_EMSCRIPTEN "defined(__EMSCRIPTEN__)") + set(arch_check_LOONGARCH64 "defined(__loongarch64)") + set(arch_check_POWERPC32 "(defined(__PPC__) || defined(__powerpc__)) && !defined(__powerpc64__)") + set(arch_check_POWERPC64 "defined(__PPC64__) || defined(__powerpc64__)") + set(arch_check_RISCV32 "defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 32") + set(arch_check_RISCV64 "defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64") + set(arch_check_X86 "defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ||defined( __i386) || defined(_M_IX86)") + set(arch_check_X64 "(defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)) && !defined(_M_ARM64EC)") + + set(src_vars "") + set(src_main "") + foreach(known_arch IN LISTS known_archs) + set(detected_${known_arch} "0") + + string(APPEND src_vars " +#if ${arch_check_${known_arch}} +#define ARCH_${known_arch} \"1\" +#else +#define ARCH_${known_arch} \"0\" +#endif +const char *arch_${known_arch} = \"INFO<${known_arch}=\" ARCH_${known_arch} \">\"; +") + string(APPEND src_main " + result += arch_${known_arch}[argc];") + endforeach() + + set(src_arch_detect "${src_vars} +int main(int argc, char *argv[]) { + int result = 0; + (void)argv; +${src_main} + return result; +}") + + if(CMAKE_C_COMPILER) + set(ext ".c") + elseif(CMAKE_CXX_COMPILER) + set(ext ".cpp") + else() + enable_language(C) + set(ext ".c") + endif() + set(path_src_arch_detect "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/SDL_detect_arch${ext}") + file(WRITE "${path_src_arch_detect}" "${src_arch_detect}") + set(path_dir_arch_detect "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/SDL_detect_arch") + set(path_bin_arch_detect "${path_dir_arch_detect}/bin") + + set(detected_archs) + + set(msg "Detecting Target CPU Architecture") + message(STATUS "${msg}") + + include(CMakePushCheckState) + + set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY") + + cmake_push_check_state(RESET) + try_compile(SDL_CPU_CHECK_ALL + "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeTmp/SDL_detect_arch" + SOURCES "${path_src_arch_detect}" + COPY_FILE "${path_bin_arch_detect}" + ) + cmake_pop_check_state() + if(NOT SDL_CPU_CHECK_ALL) + message(STATUS "${msg} - ") + message(WARNING "Failed to compile source detecting the target CPU architecture") + else() + set(re "INFO<([A-Z0-9]+)=([01])>") + file(STRINGS "${path_bin_arch_detect}" infos REGEX "${re}") + + foreach(info_arch_01 IN LISTS infos) + string(REGEX MATCH "${re}" A "${info_arch_01}") + if(NOT "${CMAKE_MATCH_1}" IN_LIST known_archs) + message(WARNING "Unknown architecture: \"${CMAKE_MATCH_1}\"") + continue() + endif() + set(arch "${CMAKE_MATCH_1}") + set(arch_01 "${CMAKE_MATCH_2}") + set(detected_${arch} "${arch_01}") + endforeach() + + foreach(known_arch IN LISTS known_archs) + if(detected_${known_arch}) + list(APPEND detected_archs ${known_arch}) + endif() + endforeach() + endif() + + if(detected_archs) + foreach(known_arch IN LISTS known_archs) + set("SDL_CPU_${known_arch}" "${detected_${known_arch}}" CACHE BOOL "Detected architecture ${known_arch}") + endforeach() + message(STATUS "${msg} - ${detected_archs}") + else() + include(CheckCSourceCompiles) + cmake_push_check_state(RESET) + foreach(known_arch IN LISTS known_archs) + if(NOT detected_archs) + set(cache_variable "SDL_CPU_${known_arch}") + set(test_src " + int main(int argc, char *argv[]) { + #if ${arch_check_${known_arch}} + return 0; + #else + choke + #endif + } + ") + check_c_source_compiles("${test_src}" "${cache_variable}") + if(${cache_variable}) + set(SDL_CPU_${known_arch} "1" CACHE BOOL "Detected architecture ${known_arch}") + set(detected_archs ${known_arch}) + else() + set(SDL_CPU_${known_arch} "0" CACHE BOOL "Detected architecture ${known_arch}") + endif() + endif() + endforeach() + cmake_pop_check_state() + endif() + set("${DETECTED_ARCHS}" "${detected_archs}" PARENT_SCOPE) +endfunction() diff --git a/thirdparty/SDL_shadercross/cmake/sdlmanpages.cmake b/thirdparty/SDL_shadercross/cmake/sdlmanpages.cmake new file mode 100644 index 000000000..dc3ebb6bb --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/sdlmanpages.cmake @@ -0,0 +1,68 @@ +include(CMakeParseArguments) +include(GNUInstallDirs) + +function(SDL_generate_manpages) + cmake_parse_arguments(ARG "" "RESULT_VARIABLE;NAME;BUILD_DOCDIR;HEADERS_DIR;SOURCE_DIR;SYMBOL;OPTION_FILE;WIKIHEADERS_PL_PATH;REVISION" "" ${ARGN}) + + set(wikiheaders_extra_args) + + if(NOT ARG_NAME) + set(ARG_NAME "${PROJECT_NAME}") + endif() + + if(NOT ARG_SOURCE_DIR) + set(ARG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + if(NOT ARG_OPTION_FILE) + set(ARG_OPTION_FILE "${PROJECT_SOURCE_DIR}/.wikiheaders-options") + endif() + + if(NOT ARG_HEADERS_DIR) + message(FATAL_ERROR "Missing required HEADERS_DIR argument") + endif() + + # FIXME: get rid of SYMBOL and let the perl script figure out the dependencies + if(NOT ARG_SYMBOL) + message(FATAL_ERROR "Missing required SYMBOL argument") + endif() + + if(ARG_REVISION) + list(APPEND wikiheaders_extra_args "--rev=${ARG_REVISION}") + endif() + + if(NOT ARG_BUILD_DOCDIR) + set(ARG_BUILD_DOCDIR "${CMAKE_CURRENT_BINARY_DIR}/docs") + endif() + set(BUILD_WIKIDIR "${ARG_BUILD_DOCDIR}/wiki") + set(BUILD_MANDIR "${ARG_BUILD_DOCDIR}/man") + + find_package(Perl) + file(GLOB HEADER_FILES "${ARG_HEADERS_DIR}/*.h") + + set(result FALSE) + + if(PERL_FOUND AND EXISTS "${ARG_WIKIHEADERS_PL_PATH}") + add_custom_command( + OUTPUT "${BUILD_WIKIDIR}/${ARG_SYMBOL}.md" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${BUILD_WIKIDIR}" + COMMAND "${PERL_EXECUTABLE}" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_SOURCE_DIR}" "${BUILD_WIKIDIR}" "--options=${ARG_OPTION_FILE}" --copy-to-wiki ${wikiheaders_extra_args} + DEPENDS ${HEADER_FILES} "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_OPTION_FILE}" + COMMENT "Generating ${ARG_NAME} wiki markdown files" + ) + add_custom_command( + OUTPUT "${BUILD_MANDIR}/man3/${ARG_SYMBOL}.3" + COMMAND "${PERL_EXECUTABLE}" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_SOURCE_DIR}" "${BUILD_WIKIDIR}" "--options=${ARG_OPTION_FILE}" "--manpath=${BUILD_MANDIR}" --copy-to-manpages ${wikiheaders_extra_args} + DEPENDS "${BUILD_WIKIDIR}/${ARG_SYMBOL}.md" "${ARG_WIKIHEADERS_PL_PATH}" "${ARG_OPTION_FILE}" + COMMENT "Generating ${ARG_NAME} man pages" + ) + add_custom_target(${ARG_NAME}-docs ALL DEPENDS "${BUILD_MANDIR}/man3/${ARG_SYMBOL}.3") + + install(DIRECTORY "${BUILD_MANDIR}/" DESTINATION "${CMAKE_INSTALL_MANDIR}") + set(result TRUE) + endif() + + if(ARG_RESULT_VARIABLE) + set(${ARG_RESULT_VARIABLE} ${result} PARENT_SCOPE) + endif() +endfunction() diff --git a/thirdparty/SDL_shadercross/cmake/sdlplatform.cmake b/thirdparty/SDL_shadercross/cmake/sdlplatform.cmake new file mode 100644 index 000000000..ebb2077fb --- /dev/null +++ b/thirdparty/SDL_shadercross/cmake/sdlplatform.cmake @@ -0,0 +1,106 @@ +macro(SDL_DetectCMakePlatform) + set(SDL_CMAKE_PLATFORM ) + # Get the platform + if(WIN32) + set(SDL_CMAKE_PLATFORM Windows) + elseif(PSP) + set(SDL_CMAKE_PLATFORM psp) + elseif(APPLE) + if(CMAKE_SYSTEM_NAME MATCHES ".*Darwin.*") + set(SDL_CMAKE_PLATFORM Darwin) + elseif(CMAKE_SYSTEM_NAME MATCHES ".*MacOS.*") + set(SDL_CMAKE_PLATFORM MacosX) + elseif(CMAKE_SYSTEM_NAME MATCHES ".*tvOS.*") + set(SDL_CMAKE_PLATFORM tvOS) + elseif(CMAKE_SYSTEM_NAME MATCHES ".*iOS.*") + set(SDL_CMAKE_PLATFORM iOS) + endif() + elseif(CMAKE_SYSTEM_NAME MATCHES "Haiku.*") + set(SDL_CMAKE_PLATFORM Haiku) + elseif(NINTENDO_3DS) + set(SDL_CMAKE_PLATFORM n3ds) + elseif(PS2) + set(SDL_CMAKE_PLATFORM ps2) + elseif(VITA) + set(SDL_CMAKE_PLATFORM Vita) + elseif(CMAKE_SYSTEM_NAME MATCHES ".*Linux") + set(SDL_CMAKE_PLATFORM Linux) + elseif(CMAKE_SYSTEM_NAME MATCHES "kFreeBSD.*") + set(SDL_CMAKE_PLATFORM FreeBSD) + elseif(CMAKE_SYSTEM_NAME MATCHES "kNetBSD.*|NetBSD.*") + set(SDL_CMAKE_PLATFORM NetBSD) + elseif(CMAKE_SYSTEM_NAME MATCHES "kOpenBSD.*|OpenBSD.*") + set(SDL_CMAKE_PLATFORM OpenBSD) + elseif(CMAKE_SYSTEM_NAME MATCHES ".*GNU.*") + set(SDL_CMAKE_PLATFORM GNU) + elseif(CMAKE_SYSTEM_NAME MATCHES ".*BSDI.*") + set(SDL_CMAKE_PLATFORM BSDi) + elseif(CMAKE_SYSTEM_NAME MATCHES "DragonFly.*|FreeBSD") + set(SDL_CMAKE_PLATFORM FreeBSD) + elseif(CMAKE_SYSTEM_NAME MATCHES "SYSV5.*") + set(SDL_CMAKE_PLATFORM SYSV5) + elseif(CMAKE_SYSTEM_NAME MATCHES "Solaris.*|SunOS.*") + set(SDL_CMAKE_PLATFORM Solaris) + elseif(CMAKE_SYSTEM_NAME MATCHES "HP-UX.*") + set(SDL_CMAKE_PLATFORM HPUX) + elseif(CMAKE_SYSTEM_NAME MATCHES "AIX.*") + set(SDL_CMAKE_PLATFORM AIX) + elseif(CMAKE_SYSTEM_NAME MATCHES "Minix.*") + set(SDL_CMAKE_PLATFORM Minix) + elseif(CMAKE_SYSTEM_NAME MATCHES "Android.*") + set(SDL_CMAKE_PLATFORM Android) + elseif(CMAKE_SYSTEM_NAME MATCHES "Emscripten.*") + set(SDL_CMAKE_PLATFORM Emscripten) + elseif(CMAKE_SYSTEM_NAME MATCHES "QNX.*") + set(SDL_CMAKE_PLATFORM QNX) + elseif(CMAKE_SYSTEM_NAME MATCHES "BeOS.*") + message(FATAL_ERROR "BeOS support has been removed as of SDL 2.0.2.") + endif() + + if(SDL_CMAKE_PLATFORM) + string(TOUPPER "${SDL_CMAKE_PLATFORM}" _upper_platform) + set(${_upper_platform} TRUE) + else() + set(SDL_CMAKE_PLATFORM} "unknown") + endif() +endmacro() + +function(SDL_DetectCPUArchitecture) + set(sdl_cpu_names) + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + foreach(osx_arch ${CMAKE_OSX_ARCHITECTURES}) + if(osx_arch STREQUAL "x86_64") + list(APPEND sdl_cpu_names "x64") + elseif(osx_arch STREQUAL "arm64") + list(APPEND sdl_cpu_names "arm64") + endif() + endforeach() + endif() + + set(sdl_known_archs x64 x86 arm64 arm32 emscripten powerpc64 powerpc32 loongarch64) + if(NOT sdl_cpu_names) + set(found FALSE) + foreach(sdl_known_arch ${sdl_known_archs}) + if(NOT found) + string(TOUPPER "${sdl_known_arch}" sdl_known_arch_upper) + set(var_name "SDL_CPU_${sdl_known_arch_upper}") + check_cpu_architecture(${sdl_known_arch} ${var_name}) + if(${var_name}) + list(APPEND sdl_cpu_names ${sdl_known_arch}) + set(found TRUE) + endif() + endif() + endforeach() + endif() + + foreach(sdl_known_arch ${sdl_known_archs}) + string(TOUPPER "${sdl_known_arch}" sdl_known_arch_upper) + set(var_name "SDL_CPU_${sdl_known_arch_upper}") + if(sdl_cpu_names MATCHES "(^|;)${sdl_known_arch}($|;)") # FIXME: use if(IN_LIST) + set(${var_name} 1 PARENT_SCOPE) + else() + set(${var_name} 0 PARENT_SCOPE) + endif() + endforeach() + set(SDL_CPU_NAMES ${sdl_cpu_names} PARENT_SCOPE) +endfunction() diff --git a/thirdparty/SDL_shadercross/include/SDL3_shadercross/SDL_shadercross.h b/thirdparty/SDL_shadercross/include/SDL3_shadercross/SDL_shadercross.h new file mode 100644 index 000000000..f3ea5c65c --- /dev/null +++ b/thirdparty/SDL_shadercross/include/SDL3_shadercross/SDL_shadercross.h @@ -0,0 +1,356 @@ +/* + Simple DirectMedia Layer Shader Cross Compiler + Copyright (C) 2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_SHADERCROSS_H +#define SDL_SHADERCROSS_H + +#include +#include +#include + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Printable format: "%d.%d.%d", MAJOR, MINOR, MICRO + */ +#define SDL_SHADERCROSS_MAJOR_VERSION 3 +#define SDL_SHADERCROSS_MINOR_VERSION 0 +#define SDL_SHADERCROSS_MICRO_VERSION 0 + +typedef enum SDL_ShaderCross_IOVarType { + SDL_SHADERCROSS_IOVAR_TYPE_UNKNOWN, + SDL_SHADERCROSS_IOVAR_TYPE_INT8, + SDL_SHADERCROSS_IOVAR_TYPE_UINT8, + SDL_SHADERCROSS_IOVAR_TYPE_INT16, + SDL_SHADERCROSS_IOVAR_TYPE_UINT16, + SDL_SHADERCROSS_IOVAR_TYPE_INT32, + SDL_SHADERCROSS_IOVAR_TYPE_UINT32, + SDL_SHADERCROSS_IOVAR_TYPE_INT64, + SDL_SHADERCROSS_IOVAR_TYPE_UINT64, + SDL_SHADERCROSS_IOVAR_TYPE_FLOAT16, + SDL_SHADERCROSS_IOVAR_TYPE_FLOAT32, + SDL_SHADERCROSS_IOVAR_TYPE_FLOAT64 +} SDL_ShaderCross_IOVarType; + +typedef enum SDL_ShaderCross_ShaderStage +{ + SDL_SHADERCROSS_SHADERSTAGE_VERTEX, + SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT, + SDL_SHADERCROSS_SHADERSTAGE_COMPUTE +} SDL_ShaderCross_ShaderStage; + +typedef struct SDL_ShaderCross_IOVarMetadata { + char *name; /**< The UTF-8 name of the variable. */ + Uint32 location; /**< The location of the variable. */ + SDL_ShaderCross_IOVarType vector_type; /**< The vector type of the variable. */ + Uint32 vector_size; /**< The number of components in the vector type of the variable. */ +} SDL_ShaderCross_IOVarMetadata; + +typedef struct SDL_ShaderCross_GraphicsShaderResourceInfo +{ + Uint32 num_samplers; /**< The number of samplers defined in the shader. */ + Uint32 num_storage_textures; /**< The number of storage textures defined in the shader. */ + Uint32 num_storage_buffers; /**< The number of storage buffers defined in the shader. */ + Uint32 num_uniform_buffers; /**< The number of uniform buffers defined in the shader. */ +} SDL_ShaderCross_GraphicsShaderResourceInfo; + +typedef struct SDL_ShaderCross_GraphicsShaderMetadata +{ + SDL_ShaderCross_GraphicsShaderResourceInfo resource_info; /**< Sub-struct containing the resource info of the shader. */ + Uint32 num_inputs; /**< The number of inputs defined in the shader. */ + SDL_ShaderCross_IOVarMetadata *inputs; /**< The inputs defined in the shader. */ + Uint32 num_outputs; /**< The number of outputs defined in the shader. */ + SDL_ShaderCross_IOVarMetadata *outputs; /**< The outputs defined in the shader. */ +} SDL_ShaderCross_GraphicsShaderMetadata; + +typedef struct SDL_ShaderCross_ComputePipelineMetadata +{ + Uint32 num_samplers; /**< The number of samplers defined in the shader. */ + Uint32 num_readonly_storage_textures; /**< The number of readonly storage textures defined in the shader. */ + Uint32 num_readonly_storage_buffers; /**< The number of readonly storage buffers defined in the shader. */ + Uint32 num_readwrite_storage_textures; /**< The number of read-write storage textures defined in the shader. */ + Uint32 num_readwrite_storage_buffers; /**< The number of read-write storage buffers defined in the shader. */ + Uint32 num_uniform_buffers; /**< The number of uniform buffers defined in the shader. */ + Uint32 threadcount_x; /**< The number of threads in the X dimension. */ + Uint32 threadcount_y; /**< The number of threads in the Y dimension. */ + Uint32 threadcount_z; /**< The number of threads in the Z dimension. */ +} SDL_ShaderCross_ComputePipelineMetadata; + +typedef struct SDL_ShaderCross_SPIRV_Info +{ + const Uint8 *bytecode; /**< The SPIRV bytecode. */ + size_t bytecode_size; /**< The length of the SPIRV bytecode. */ + const char *entrypoint; /**< The entry point function name for the shader in UTF-8. */ + SDL_ShaderCross_ShaderStage shader_stage; /**< The shader stage to transpile the shader with. */ + + SDL_PropertiesID props; /**< A properties ID for extensions. Should be 0 if no extensions are needed. */ +} SDL_ShaderCross_SPIRV_Info; + +#define SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN "SDL_shadercross.spirv.debug.enable" +#define SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING "SDL_shadercross.spirv.debug.name" +#define SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN "SDL_shadercross.spirv.cull_unused_bindings" + +#define SDL_SHADERCROSS_PROP_SPIRV_PSSL_COMPATIBILITY_BOOLEAN "SDL_shadercross.spirv.pssl.compatibility" +#define SDL_SHADERCROSS_PROP_SPIRV_MSL_VERSION_STRING "SDL_shadercross.spirv.msl.version" +#define SDL_SHADERCROSS_PROP_HLSL_SKIP_SPIRV_ROUNDTRIP_BOOLEAN "SDL_shadercross.hlsl.skip_spirv_roundtrip" + +typedef struct SDL_ShaderCross_HLSL_Define +{ + char *name; /**< The define name. */ + char *value; /**< An optional value for the define. Can be NULL. */ +} SDL_ShaderCross_HLSL_Define; + +typedef struct SDL_ShaderCross_HLSL_Info +{ + const char *source; /**< The HLSL source code for the shader. */ + const char *entrypoint; /**< The entry point function name for the shader in UTF-8. */ + const char *include_dir; /**< The include directory for shader code. Optional, can be NULL. */ + SDL_ShaderCross_HLSL_Define *defines; /**< An array of defines. Optional, can be NULL. If not NULL, must be terminated with a fully NULL define struct. */ + SDL_ShaderCross_ShaderStage shader_stage; /**< The shader stage to compile the shader with. */ + + SDL_PropertiesID props; /**< A properties ID for extensions. Should be 0 if no extensions are needed. */ +} SDL_ShaderCross_HLSL_Info; + +/** + * Initializes SDL_shadercross + * + * \threadsafety This should only be called once, from a single thread. + * \returns true on success, false otherwise. + */ +extern SDL_DECLSPEC bool SDLCALL SDL_ShaderCross_Init(void); +/** + * De-initializes SDL_shadercross + * + * \threadsafety This should only be called once, from a single thread. + */ +extern SDL_DECLSPEC void SDLCALL SDL_ShaderCross_Quit(void); + +/** + * Get the supported shader formats that SPIRV cross-compilation can output + * + * \threadsafety It is safe to call this function from any thread. + * \returns GPU shader formats supported by SPIRV cross-compilation. + */ +extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_ShaderCross_GetSPIRVShaderFormats(void); + +/** + * Transpile to MSL code from SPIRV code. + * + * You must SDL_free the returned string once you are done with it. + * + * These are the optional properties that can be used: + * + * - `SDL_SHADERCROSS_PROP_SPIRV_MSL_VERSION_STRING`: specifies the MSL version that should be emitted. Defaults to 1.2.0. + * + * \param info a struct describing the shader to transpile. + * \returns an SDL_malloc'd string containing MSL code. + */ +extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileMSLFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info); + +/** + * Transpile to HLSL code from SPIRV code. + * + * You must SDL_free the returned string once you are done with it. + * + * These are the optional properties that can be used: + * + * - `SDL_SHADERCROSS_PROP_SPIRV_PSSL_COMPATIBILITY_BOOLEAN`: generates PSSL-compatible shader. + * + * \param info a struct describing the shader to transpile. + * \returns an SDL_malloc'd string containing HLSL code. + */ +extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_TranspileHLSLFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info); + +/** + * Compile DXBC bytecode from SPIRV code. + * + * You must SDL_free the returned buffer once you are done with it. + * + * \param info a struct describing the shader to transpile. + * \param size filled in with the bytecode buffer size. + * \returns an SDL_malloc'd buffer containing DXBC bytecode. + */ +extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info, + size_t *size); + +/** + * Compile DXIL bytecode from SPIRV code. + * + * You must SDL_free the returned buffer once you are done with it. + * + * \param info a struct describing the shader to transpile. + * \param size filled in with the bytecode buffer size. + * \returns an SDL_malloc'd buffer containing DXIL bytecode. + */ +extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXILFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info, + size_t *size); + +/** + * Compile an SDL GPU shader from SPIRV code. If your shader source is HLSL, you should obtain SPIR-V bytecode from SDL_ShaderCross_CompileSPIRVFromHLSL(). + * + * \param device the SDL GPU device. + * \param info a struct describing the shader to transpile. + * \param resource_info a struct describing resource info of the shader. Can be obtained from SDL_ShaderCross_ReflectGraphicsSPIRV(). + * \param props a properties object filled in with extra shader metadata. + * \returns a compiled SDL_GPUShader. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC SDL_GPUShader * SDLCALL SDL_ShaderCross_CompileGraphicsShaderFromSPIRV( + SDL_GPUDevice *device, + const SDL_ShaderCross_SPIRV_Info *info, + const SDL_ShaderCross_GraphicsShaderResourceInfo *resource_info, + SDL_PropertiesID props); + +/** + * Compile an SDL GPU compute pipeline from SPIRV code. If your shader source is HLSL, you should obtain SPIR-V bytecode from SDL_ShaderCross_CompileSPIRVFromHLSL(). + * + * \param device the SDL GPU device. + * \param info a struct describing the shader to transpile. + * \param metadata a struct describing shader metadata. Can be obtained from SDL_ShaderCross_ReflectComputeSPIRV(). + * \param props a properties object filled in with extra shader metadata. + * \returns a compiled SDL_GPUComputePipeline. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC SDL_GPUComputePipeline * SDLCALL SDL_ShaderCross_CompileComputePipelineFromSPIRV( + SDL_GPUDevice *device, + const SDL_ShaderCross_SPIRV_Info *info, + const SDL_ShaderCross_ComputePipelineMetadata *metadata, + SDL_PropertiesID props); + +/** + * Reflect graphics shader info from SPIRV code. If your shader source is HLSL, you should obtain SPIR-V bytecode from SDL_ShaderCross_CompileSPIRVFromHLSL(). This must be freed with SDL_free() when you are done with the metadata. + * + * \param bytecode the SPIRV bytecode. + * \param bytecode_size the length of the SPIRV bytecode. + * \param props a properties object filled in with extra shader metadata, provided by the user. + * \returns A metadata struct on success, NULL otherwise. The struct must be free'd when it is no longer needed. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC SDL_ShaderCross_GraphicsShaderMetadata * SDLCALL SDL_ShaderCross_ReflectGraphicsSPIRV( + const Uint8 *bytecode, + size_t bytecode_size, + SDL_PropertiesID props); + +/** + * Reflect compute pipeline info from SPIRV code. If your shader source is HLSL, you should obtain SPIR-V bytecode from SDL_ShaderCross_CompileSPIRVFromHLSL(). This must be freed with SDL_free() when you are done with the metadata. + * + * \param bytecode the SPIRV bytecode. + * \param bytecode_size the length of the SPIRV bytecode. + * \param props a properties object filled in with extra shader metadata, provided by the user. + * \returns A metadata struct on success, NULL otherwise. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC SDL_ShaderCross_ComputePipelineMetadata * SDLCALL SDL_ShaderCross_ReflectComputeSPIRV( + const Uint8 *bytecode, + size_t bytecode_size, + SDL_PropertiesID props); + +/** + * Get the supported shader formats that HLSL cross-compilation can output + * + * \returns GPU shader formats supported by HLSL cross-compilation. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC SDL_GPUShaderFormat SDLCALL SDL_ShaderCross_GetHLSLShaderFormats(void); + +/** + * Compile to DXBC bytecode from HLSL code via a SPIRV-Cross round trip. + * + * You must SDL_free the returned buffer once you are done with it. + * + * These are the optional properties that can be used: + * + * - `SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN`: allows debug info to be emitted when relevant. Should only be used with debugging tools like Renderdoc. + * - `SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING`: a UTF-8 name to be used with the shader. Relevant for use with debugging tools like Renderdoc. + * - `SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN`: When true, indicates that the compiler should cull unused shader resources. This behavior is disabled by default. + * - `SDL_SHADERCROSS_PROP_HLSL_SKIP_SPIRV_ROUNDTRIP_BOOLEAN`: When true, the SPIRV roundtrip is skipped. This behavior is disabled by default. Do not use this property if your shader uses Structured Buffers. + * + * \param info a struct describing the shader to transpile. + * \param size filled in with the bytecode buffer size. + * \returns an SDL_malloc'd buffer containing DXBC bytecode. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXBCFromHLSL( + const SDL_ShaderCross_HLSL_Info *info, + size_t *size); + +/** + * Compile to DXIL bytecode from HLSL code via a SPIRV-Cross round trip. + * + * You must SDL_free the returned buffer once you are done with it. + * + * These are the optional properties that can be used: + * + * - `SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN`: allows debug info to be emitted when relevant. Should only be used with debugging tools like Renderdoc. + * - `SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING`: a UTF-8 name to be used with the shader. Relevant for use with debugging tools like Renderdoc. + * - `SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN`: when true, indicates that the compiler should cull unused shader resources. This behavior is disabled by default. + * - `SDL_SHADERCROSS_PROP_HLSL_SKIP_SPIRV_ROUNDTRIP_BOOLEAN`: when true, the SPIRV roundtrip is skipped. This behavior is disabled by default. Do not use this property if your shader uses Structured Buffers. + * + * \param info a struct describing the shader to transpile. + * \param size filled in with the bytecode buffer size. + * \returns an SDL_malloc'd buffer containing DXIL bytecode. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileDXILFromHLSL( + const SDL_ShaderCross_HLSL_Info *info, + size_t *size); + +/** + * Compile to SPIRV bytecode from HLSL code. + * + * You must SDL_free the returned buffer once you are done with it. + * + * These are the optional properties that can be used: + * + * - `SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN`: allows debug info to be emitted when relevant. Should only be used with debugging tools like Renderdoc. + * - `SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING`: a UTF-8 name to be used with the shader. Relevant for use with debugging tools like Renderdoc. + * - `SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN`: when true, indicates that the compiler should cull unused shader resources. This behavior is disabled by default. + * + * \param info a struct describing the shader to transpile. + * \param size filled in with the bytecode buffer size. + * \returns an SDL_malloc'd buffer containing SPIRV bytecode. + * + * \threadsafety It is safe to call this function from any thread. + */ +extern SDL_DECLSPEC void * SDLCALL SDL_ShaderCross_CompileSPIRVFromHLSL( + const SDL_ShaderCross_HLSL_Info *info, + size_t *size); + +#ifdef __cplusplus +} +#endif +#include + +#endif /* SDL_SHADERCROSS_H */ diff --git a/thirdparty/SDL_shadercross/src/SDL_shadercross.c b/thirdparty/SDL_shadercross/src/SDL_shadercross.c new file mode 100644 index 000000000..a4b2e70b9 --- /dev/null +++ b/thirdparty/SDL_shadercross/src/SDL_shadercross.c @@ -0,0 +1,2699 @@ +/* + Simple DirectMedia Layer Shader Cross Compiler + Copyright (C) 2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include + +/* Constants */ +#define MAX_DEFINES 64 +#define MAX_DEFINE_STRING_LENGTH 256 + +/* Upper round X to multiple of V. V must be power of 2. */ +#define SDL_upper_multiple_power2(X, V) (((X) + (V) - 1) & ~((V) - 1)) + +/* Win32 Type Definitions */ + +typedef int HRESULT; +typedef const void *LPCVOID; +typedef size_t SIZE_T; +typedef const char *LPCSTR; +typedef unsigned int UINT; +typedef unsigned long ULONG; +typedef void *LPVOID; +typedef void *REFIID; + +/* DXIL via DXC */ +#ifdef SDL_SHADERCROSS_DXC + +/* dxcompiler Type Definitions */ +typedef int BOOL; +typedef void *REFCLSID; +typedef wchar_t *LPCWSTR; +typedef void IDxcBlobEncoding; /* hack, unused */ +typedef void IDxcBlobWide; /* hack, unused */ +typedef void IDxcIncludeHandler; /* hack, unused */ + +/* Unlike vkd3d-utils, libdxcompiler.so does not use msabi */ +#if !defined(_WIN32) +#define __stdcall +#endif + +/* Compiler Interface, _technically_ unofficial but it's MS C++, come on */ +typedef enum DXC_OUT_KIND +{ + DXC_OUT_NONE = 0, + DXC_OUT_OBJECT = 1, + DXC_OUT_ERRORS = 2, + DXC_OUT_PDB = 3, + DXC_OUT_SHADER_HASH = 4, + DXC_OUT_DISASSEMBLY = 5, + DXC_OUT_HLSL = 6, + DXC_OUT_TEXT = 7, + DXC_OUT_REFLECTION = 8, + DXC_OUT_ROOT_SIGNATURE = 9, + DXC_OUT_EXTRA_OUTPUTS = 10, + DXC_OUT_REMARKS = 11, + DXC_OUT_TIME_REPORT = 12, + DXC_OUT_TIME_TRACE = 13, + DXC_OUT_LAST = DXC_OUT_TIME_TRACE, + DXC_OUT_NUM_ENUMS, + // DXC_OUT_FORCE_DWORD = 0xFFFFFFFF +} DXC_OUT_KIND; + +#define DXC_CP_UTF8 65001 +#define DXC_CP_UTF16 1200 +#define DXC_CP_UTF32 12000 +/* This is for binary, ANSI-text, or to tell the compiler to try autodetecting UTF using the BOM */ +#define DXC_CP_ACP 0 + +typedef struct DxcBuffer +{ + LPCVOID Ptr; + SIZE_T Size; + UINT Encoding; +} DxcBuffer; + +/* *INDENT-OFF* */ // clang-format off + +static Uint8 IID_IDxcBlob[] = { + 0x08, 0xFB, 0xA5, 0x8B, + 0x95, 0x51, + 0xE2, 0x40, + 0xAC, + 0x58, + 0x0D, + 0x98, + 0x9C, + 0x3A, + 0x01, + 0x02 +}; +typedef struct IDxcBlob IDxcBlob; +typedef struct IDxcBlobVtbl +{ + HRESULT(__stdcall *QueryInterface)(IDxcBlob *This, REFIID riid, void **ppvObject); + ULONG(__stdcall *AddRef)(IDxcBlob *This); + ULONG(__stdcall *Release)(IDxcBlob *This); + + LPVOID(__stdcall *GetBufferPointer)(IDxcBlob *This); + SIZE_T(__stdcall *GetBufferSize)(IDxcBlob *This); +} IDxcBlobVtbl; +struct IDxcBlob +{ + IDxcBlobVtbl *lpVtbl; +}; + +static Uint8 IID_IDxcBlobUtf8[] = { + 0xC9, 0x36, 0xA6, 0x3D, + 0x71, 0xBA, + 0x24, 0x40, + 0xA3, + 0x01, + 0x30, + 0xCB, + 0xF1, + 0x25, + 0x30, + 0x5B +}; +typedef struct IDxcBlobUtf8 IDxcBlobUtf8; +typedef struct IDxcBlobUtf8Vtbl +{ + HRESULT(__stdcall *QueryInterface)(IDxcBlobUtf8 *This, REFIID riid, void **ppvObject); + ULONG(__stdcall *AddRef)(IDxcBlobUtf8 *This); + ULONG(__stdcall *Release)(IDxcBlobUtf8 *This); + + LPVOID(__stdcall *GetBufferPointer)(IDxcBlobUtf8 *This); + SIZE_T(__stdcall *GetBufferSize)(IDxcBlobUtf8 *This); + + HRESULT(__stdcall *GetEncoding)(IDxcBlobUtf8 *This, BOOL *pKnown, Uint32 *pCodePage); + + LPCSTR(__stdcall *GetStringPointer)(IDxcBlobUtf8 *This); + SIZE_T(__stdcall *GetStringLength)(IDxcBlobUtf8 *This); +} IDxcBlobUtf8Vtbl; +struct IDxcBlobUtf8 +{ + IDxcBlobUtf8Vtbl *lpVtbl; +}; + +static Uint8 IID_IDxcResult[] = { + 0xDA, 0x6C, 0x34, 0x58, + 0xE7, 0xDD, + 0x97, 0x44, + 0x94, + 0x61, + 0x6F, + 0x87, + 0xAF, + 0x5E, + 0x06, + 0x59 +}; +typedef struct IDxcResult IDxcResult; +typedef struct IDxcResultVtbl +{ + HRESULT(__stdcall *QueryInterface)(IDxcResult *This, REFIID riid, void **ppvObject); + ULONG(__stdcall *AddRef)(IDxcResult *This); + ULONG(__stdcall *Release)(IDxcResult *This); + + HRESULT(__stdcall *GetStatus)(IDxcResult *This, HRESULT *pStatus); + HRESULT(__stdcall *GetResult)(IDxcResult *This, IDxcBlob **ppResult); + HRESULT(__stdcall *GetErrorBuffer)(IDxcResult *This, IDxcBlobEncoding **ppErrors); + + BOOL(__stdcall *HasOutput)(IDxcResult *This, DXC_OUT_KIND dxcOutKind); + HRESULT(__stdcall *GetOutput)( + IDxcResult *This, + DXC_OUT_KIND dxcOutKind, + REFIID iid, + void **ppvObject, + IDxcBlobWide **ppOutputName + ); + Uint32(__stdcall *GetNumOutputs)(IDxcResult *This); + DXC_OUT_KIND(__stdcall *GetOutputByIndex)(IDxcResult *This, Uint32 Index); + DXC_OUT_KIND(__stdcall *PrimaryOutput)(IDxcResult *This); +} IDxcResultVtbl; +struct IDxcResult +{ + IDxcResultVtbl *lpVtbl; +}; + +static struct +{ + Uint32 Data1; + Uint16 Data2; + Uint16 Data3; + Uint8 Data4[8]; +} CLSID_DxcCompiler = { + .Data1 = 0x73e22d93, + .Data2 = 0xe6ce, + .Data3 = 0x47f3, + .Data4 = { 0xb5, 0xbf, 0xf0, 0x66, 0x4f, 0x39, 0xc1, 0xb0 } +}; +static Uint8 IID_IDxcCompiler3[] = { + 0x87, 0x46, 0x8B, 0x22, + 0x6A, 0x5A, + 0x30, 0x47, + 0x90, + 0x0C, + 0x97, + 0x02, + 0xB2, + 0x20, + 0x3F, + 0x54 +}; +typedef struct IDxcCompiler3 IDxcCompiler3; +typedef struct IDxcCompiler3Vtbl +{ + HRESULT(__stdcall *QueryInterface)(IDxcCompiler3 *This, REFIID riid, void **ppvObject); + ULONG(__stdcall *AddRef)(IDxcCompiler3 *This); + ULONG(__stdcall *Release)(IDxcCompiler3 *This); + + HRESULT(__stdcall *Compile)( + IDxcCompiler3 *This, + const DxcBuffer *pSource, + LPCWSTR *pArguments, + Uint32 argCount, + IDxcIncludeHandler *pIncludeHandler, + REFIID riid, + LPVOID *ppResult + ); + + HRESULT(__stdcall *Disassemble)( + IDxcCompiler3 *This, + const DxcBuffer *pObject, + REFIID riid, + LPVOID *ppResult + ); +} IDxcCompiler3Vtbl; +struct IDxcCompiler3 +{ + const IDxcCompiler3Vtbl *lpVtbl; +}; + +// We need all this DxcUtils garbage for DXC include dir support. Thanks Microsoft! +typedef struct IMalloc IMalloc; +typedef struct IStream IStream; +typedef struct DxcDefine DxcDefine; +typedef struct IDxcCompilerArgs IDxcCompilerArgs; + +static struct +{ + Uint32 Data1; + Uint16 Data2; + Uint16 Data3; + Uint8 Data4[8]; +} CLSID_DxcUtils = { + .Data1 = 0x6245d6af, + .Data2 = 0x66e0, + .Data3 = 0x48fd, + .Data4 = {0x80, 0xb4, 0x4d, 0x27, 0x17, 0x96, 0x74, 0x8c}}; +static Uint8 IID_IDxcUtils[] = { + 0xcb, 0xc4, 0x05, 0x46, + 0x19, 0x20, + 0x2a, 0x49, + 0xad, + 0xa4, + 0x65, + 0xf2, + 0x0b, + 0xb7, + 0xd6, + 0x7f +}; +typedef struct IDxcUtilsVtbl +{ + HRESULT (__stdcall *QueryInterface)(void *pSelf, REFIID riid, void **ppvObject); + ULONG (__stdcall *AddRef)(void *pSelf); + ULONG (__stdcall *Release)(void *pSelf); + + HRESULT (__stdcall *CreateBlobFromBlob)(void *pSelf, IDxcBlob *pBlob, UINT offset, UINT length, IDxcBlob **ppResult); + HRESULT (__stdcall *CreateBlobFromPinned)(void *pSelf, LPCVOID pData, UINT size, UINT codePage, IDxcBlobEncoding **pBlobEncoding); + HRESULT (__stdcall *MoveToBlob)(void *pSelf, LPCVOID pData, IMalloc *pIMalloc, UINT size, UINT codePage, IDxcBlobEncoding **pBlobEncoding); + HRESULT (__stdcall *CreateBlob)(void *pSelf, LPCVOID pData, UINT size, UINT codePage, IDxcBlobEncoding **pBlobEncoding); + HRESULT (__stdcall *LoadFile)(void *pSelf, LPCWSTR pFileName, UINT *pCodePage, IDxcBlobEncoding **pBlobEncoding); + HRESULT (__stdcall *CreateReadOnlyStreamFromBlob)(void *pSelf, IDxcBlob *pBlob, IStream **ppStream); + HRESULT (__stdcall *CreateDefaultIncludeHandler)(void *pSelf, IDxcIncludeHandler **ppResult); + HRESULT (__stdcall *GetBlobAsUtf8)(void *pSelf, IDxcBlob *pBlob, IDxcBlobUtf8 **pBlobEncoding); + HRESULT (__stdcall *GetBlobAsWide)(void *pSelf, IDxcBlob *pBlob, IDxcBlobWide **pBlobEncoding); + HRESULT (__stdcall *GetDxilContainerPart)(void *pSelf, const DxcBuffer *pShader, UINT DxcPart, void **ppPartData, UINT *pPartSizeInBytes); + HRESULT (__stdcall *CreateReflection)(void *pSelf, const DxcBuffer *pData, REFIID iid, void **ppvReflection); + HRESULT (__stdcall *BuildArguments)(void *pSelf, LPCWSTR pSourceName, LPCWSTR pEntryPoint, LPCWSTR pTargetProfile, LPCWSTR *pArguments, UINT argCount, const DxcDefine *pDefines, UINT defineCount, IDxcCompilerArgs **ppArgs); + HRESULT (__stdcall *GetPDBContents)(void *pSelf, IDxcBlob *pPDBBlob, IDxcBlob **ppHash, IDxcBlob **ppContainer); +} IDxcUtilsVtbl; + +typedef struct IDxcUtils IDxcUtils; +struct IDxcUtils +{ + const IDxcUtilsVtbl *lpVtbl; +}; + +/* *INDENT-ON* */ // clang-format on + +/* DXCompiler */ +#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) || defined(SDL_PLATFORM_WINDOWS) +extern HRESULT __stdcall DxcCreateInstance(REFCLSID rclsid, REFIID riid, LPVOID* ppv); +#else +extern HRESULT DxcCreateInstance(REFCLSID rclsid, REFIID riid, LPVOID *ppv); +#endif + +#endif /* SDL_SHADERCROSS_DXC */ + +static void *SDL_ShaderCross_INTERNAL_CompileUsingDXC( + const SDL_ShaderCross_HLSL_Info *info, + bool spirv, + size_t *size) // filled in with number of bytes of returned buffer +{ +#ifdef SDL_SHADERCROSS_DXC + DxcBuffer source; + IDxcResult *dxcResult; + IDxcBlob *blob; + IDxcBlobUtf8 *errors; + size_t entryPointLength = SDL_utf8strlen(info->entrypoint) + 1; + wchar_t *entryPointUtf16 = NULL; + size_t includeDirLength = 0; + wchar_t *includeDirUtf16 = NULL; + wchar_t *nameUtf16 = NULL; + wchar_t **defineStringsUtf16 = NULL; + size_t numDefineStrings = 0; + HRESULT ret; + + /* Non-static DxcInstance, since the functions we call on it are not thread-safe */ + IDxcCompiler3 *dxcInstance = NULL; + IDxcUtils *utils = NULL; + IDxcIncludeHandler *includeHandler = NULL; + + DxcCreateInstance( + &CLSID_DxcCompiler, + IID_IDxcCompiler3, + (void **)&dxcInstance); + + DxcCreateInstance( + &CLSID_DxcUtils, + &IID_IDxcUtils, + (void **)(&utils)); + + if (dxcInstance == NULL) { + SDL_SetError("%s", "Could not create DXC instance!"); + return NULL; + } + + if (utils == NULL) { + SDL_SetError("%s", "Could not create DXC utils instance!"); + dxcInstance->lpVtbl->Release(dxcInstance); + return NULL; + } + + utils->lpVtbl->CreateDefaultIncludeHandler(utils, &includeHandler); + if (includeHandler == NULL) { + SDL_SetError("%s", "Failed to create a default include handler!"); + dxcInstance->lpVtbl->Release(dxcInstance); + utils->lpVtbl->Release(utils); + return NULL; + } + + entryPointUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", info->entrypoint, entryPointLength); + if (entryPointUtf16 == NULL) { + SDL_SetError("%s", "Failed to convert entrypoint to WCHAR_T!"); + dxcInstance->lpVtbl->Release(dxcInstance); + utils->lpVtbl->Release(utils); + return NULL; + } + + if (info->defines != NULL) { + for (Uint32 i = 0; i < MAX_DEFINES; i += 1) { + if (info->defines[i].name == NULL) { + break; + } + numDefineStrings += 1; + } + } + + char defineString[MAX_DEFINE_STRING_LENGTH]; + defineStringsUtf16 = SDL_malloc(sizeof(LPCWSTR) * numDefineStrings); + for (Uint32 i = 0; i < numDefineStrings; i += 1) { + if (info->defines[i].value == NULL) { + SDL_snprintf(defineString, MAX_DEFINE_STRING_LENGTH, "-D%s=%s", info->defines[i].name, "1"); + } else { + SDL_snprintf(defineString, MAX_DEFINE_STRING_LENGTH, "-D%s=%s", info->defines[i].name, info->defines[i].value); + } + + defineStringsUtf16[i] = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", defineString, MAX_DEFINE_STRING_LENGTH); + } + + LPCWSTR *args = SDL_malloc(sizeof(LPCWSTR) * (numDefineStrings + 13)); + Uint32 argCount = 0; + + for (Uint32 i = 0; i < numDefineStrings; i += 1) { + args[argCount++] = defineStringsUtf16[i]; + } + + args[argCount++] = (LPCWSTR)L"-E"; + args[argCount++] = (LPCWSTR)entryPointUtf16; + + if (info->include_dir != NULL) { + includeDirLength = SDL_utf8strlen(info->include_dir) + 1; + includeDirUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", info->include_dir, includeDirLength); + + if (includeDirUtf16 == NULL) { + SDL_SetError("%s", "Failed to convert include dir to WCHAR_T!"); + dxcInstance->lpVtbl->Release(dxcInstance); + utils->lpVtbl->Release(utils); + SDL_free(entryPointUtf16); + for (Uint32 i = 0; i < numDefineStrings; i += 1) { + SDL_free(defineStringsUtf16[i]); + } + return NULL; + } + args[argCount++] = (LPCWSTR)L"-I"; + args[argCount++] = includeDirUtf16; + } + + source.Ptr = info->source; + source.Size = SDL_strlen(info->source) + 1; + source.Encoding = DXC_CP_ACP; + + if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) { + args[argCount++] = (LPCWSTR)L"-T"; + args[argCount++] = (LPCWSTR)L"vs_6_0"; + } else if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) { + args[argCount++] = (LPCWSTR)L"-T"; + args[argCount++] = (LPCWSTR)L"ps_6_0"; + } else { // compute + args[argCount++] = (LPCWSTR)L"-T"; + args[argCount++] = (LPCWSTR)L"cs_6_0"; + } + + if (spirv) { + args[argCount++] = (LPCWSTR)L"-spirv"; + args[argCount++] = (LPCWSTR)L"-fspv-flatten-resource-arrays"; + + if (!SDL_GetBooleanProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN, false)) + { + args[argCount++] = (LPCWSTR)L"-fspv-preserve-bindings"; + } + + args[argCount++] = (LPCWSTR)L"-fspv-preserve-interface"; + } + + if (SDL_GetBooleanProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN, false)) { + if (spirv) { + // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#debugging + args[argCount++] = (LPCWSTR)L"-fspv-debug=vulkan-with-source"; + } else { + // https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SourceLevelDebuggingHLSL.rst#command-line-options + args[argCount++] = (LPCWSTR)L"-Zi"; + } + } + + if (SDL_HasProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING)) { + const char *debugName = SDL_GetStringProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, NULL); + nameUtf16 = (wchar_t *)SDL_iconv_string("WCHAR_T", "UTF-8", debugName, SDL_utf8strlen(debugName) + 1); + if (nameUtf16 != NULL) { + args[argCount++] = nameUtf16; // a bare string inserted into the arguments is treated as the source file name + } + } + +#if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) + args[argCount++] = L"-D__XBOX_DISABLE_PRECOMPILE=1"; +#endif + + ret = dxcInstance->lpVtbl->Compile( + dxcInstance, + &source, + args, + argCount, + includeHandler, + IID_IDxcResult, + (void **)&dxcResult); + + SDL_free(args); + SDL_free(entryPointUtf16); + for (Uint32 i = 0; i < numDefineStrings; i += 1) { + SDL_free(defineStringsUtf16[i]); + } + SDL_free(defineStringsUtf16); + if (includeDirUtf16 != NULL) { + SDL_free(includeDirUtf16); + } + if (nameUtf16 != NULL) { + SDL_free(nameUtf16); + } + + if (ret < 0) { + SDL_SetError("IDxcShaderCompiler3::Compile failed: %X", ret); + dxcInstance->lpVtbl->Release(dxcInstance); + utils->lpVtbl->Release(utils); + return NULL; + } else if (dxcResult == NULL) { + SDL_SetError("%s", "HLSL compilation failed with no IDxcResult"); + dxcInstance->lpVtbl->Release(dxcInstance); + utils->lpVtbl->Release(utils); + return NULL; + } + + ret = dxcResult->lpVtbl->GetOutput(dxcResult, + DXC_OUT_OBJECT, + IID_IDxcBlob, + (void **)&blob, + NULL); + + HRESULT retStatus; + if (ret < 0 || dxcResult->lpVtbl->GetStatus(dxcResult, &retStatus) < 0 || retStatus < 0 ) { + // Compilation failed, display errors + dxcResult->lpVtbl->GetOutput( + dxcResult, + DXC_OUT_ERRORS, + IID_IDxcBlobUtf8, + (void **)&errors, + NULL); + + if (errors != NULL && errors->lpVtbl->GetBufferSize(errors) != 0) { + SDL_SetError( + "HLSL compilation failed: %s", + (char *)errors->lpVtbl->GetBufferPointer(errors)); + } else { + SDL_SetError("%s", "Compilation failed with unknown error"); + } + + // teardown + dxcResult->lpVtbl->Release(dxcResult); + dxcInstance->lpVtbl->Release(dxcInstance); + utils->lpVtbl->Release(utils); + return NULL; + } + + // If compilation succeeded, but there are errors, those are warnings + dxcResult->lpVtbl->GetOutput( + dxcResult, + DXC_OUT_ERRORS, + IID_IDxcBlobUtf8, + (void **)&errors, + NULL); + + if (errors != NULL && errors->lpVtbl->GetBufferSize(errors) != 0) { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "HLSL compiled with warnings: %s", + (char *)errors->lpVtbl->GetBufferPointer(errors)); + } + + *size = blob->lpVtbl->GetBufferSize(blob); + void *buffer = SDL_malloc(*size); + SDL_memcpy(buffer, blob->lpVtbl->GetBufferPointer(blob), *size); + + blob->lpVtbl->Release(blob); + dxcResult->lpVtbl->Release(dxcResult); + dxcInstance->lpVtbl->Release(dxcInstance); + utils->lpVtbl->Release(utils); + + return buffer; +#else + SDL_SetError("%s", "Shadercross was not built with DXC support, cannot compile using DXC!"); + return NULL; +#endif /* SDL_SHADERCROSS_DXC */ +} + +void *SDL_ShaderCross_CompileDXILFromHLSL( + const SDL_ShaderCross_HLSL_Info *info, + size_t *size) +{ + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + +#if SDL_PLATFORM_GDK + return SDL_ShaderCross_INTERNAL_CompileUsingDXC(info, false, size); +#else + + if (SDL_GetBooleanProperty(info->props, SDL_SHADERCROSS_PROP_HLSL_SKIP_SPIRV_ROUNDTRIP_BOOLEAN, false)) { + return SDL_ShaderCross_INTERNAL_CompileUsingDXC(info, false, size); + } + + // Roundtrip to SPIR-V to support things like Structured Buffers. + size_t spirvSize; + void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL( + info, + &spirvSize); + + if (spirv == NULL) { + return NULL; + } + + SDL_ShaderCross_SPIRV_Info spirvInfo; + spirvInfo.bytecode = spirv; + spirvInfo.bytecode_size = spirvSize; + spirvInfo.entrypoint = info->entrypoint; + spirvInfo.shader_stage = info->shader_stage; + spirvInfo.props = info->props; + + void *translatedSource = SDL_ShaderCross_TranspileHLSLFromSPIRV( + &spirvInfo); + + SDL_free(spirv); + if (translatedSource == NULL) { + return NULL; + } + + SDL_ShaderCross_HLSL_Info translatedHlslInfo; + SDL_memcpy(&translatedHlslInfo, info, sizeof(SDL_ShaderCross_HLSL_Info)); + translatedHlslInfo.source = translatedSource; + + void *result = SDL_ShaderCross_INTERNAL_CompileUsingDXC( + &translatedHlslInfo, + false, + size); + SDL_free(translatedSource); + return result; +#endif +} + +void *SDL_ShaderCross_CompileSPIRVFromHLSL( + const SDL_ShaderCross_HLSL_Info *info, + size_t *size) +{ + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + return SDL_ShaderCross_INTERNAL_CompileUsingDXC( + info, + true, + size); +} + +/* DXBC via FXC */ + +/* d3dcompiler Type Definitions */ +typedef void D3D_SHADER_MACRO; /* hack, unused */ +typedef void ID3DInclude; /* hack, unused */ + +/* Dynamic Library / Linking */ +#ifdef D3DCOMPILER_DLL +#undef D3DCOMPILER_DLL +#endif +#if defined(_WIN32) +#define D3DCOMPILER_DLL "d3dcompiler_47.dll" +#elif defined(__APPLE__) +#define D3DCOMPILER_DLL "libvkd3d-utils.1.dylib" +#else +#define D3DCOMPILER_DLL "libvkd3d-utils.so.1" +#endif + +#ifdef SDL_ELF_NOTE_DLOPEN +SDL_ELF_NOTE_DLOPEN( + "dxbc", + "Create DXBC shaders from HLSL", + SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, + D3DCOMPILER_DLL +) +#endif + +/* __stdcall declaration, largely taken from vkd3d_windows.h */ +#ifndef _WIN32 +#ifdef __stdcall +#undef __stdcall +#endif +#if defined(__x86_64__) || defined(__arm64__) +#define __stdcall __attribute__((ms_abi)) +#else +#if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || defined(__APPLE__) +#define __stdcall __attribute__((__stdcall__)) __attribute__((__force_align_arg_pointer__)) +#else +#define __stdcall __attribute__((__stdcall__)) +#endif +#endif +#endif + +/* ID3DBlob definition, used by both D3DCompiler and DXCompiler */ +typedef struct ID3DBlob ID3DBlob; +typedef struct ID3DBlobVtbl +{ + HRESULT(__stdcall *QueryInterface) + (ID3DBlob *This, REFIID riid, void **ppvObject); + ULONG(__stdcall *AddRef) + (ID3DBlob *This); + ULONG(__stdcall *Release) + (ID3DBlob *This); + LPVOID(__stdcall *GetBufferPointer) + (ID3DBlob *This); + SIZE_T(__stdcall *GetBufferSize) + (ID3DBlob *This); +} ID3DBlobVtbl; +struct ID3DBlob +{ + const ID3DBlobVtbl *lpVtbl; +}; +#define ID3D10Blob ID3DBlob + +/* D3DCompiler */ +static SDL_SharedObject *d3dcompiler_dll = NULL; + +typedef HRESULT(__stdcall *pfn_D3DCompile)( + LPCVOID pSrcData, + SIZE_T SrcDataSize, + LPCSTR pSourceName, + const D3D_SHADER_MACRO *pDefines, + ID3DInclude *pInclude, + LPCSTR pEntrypoint, + LPCSTR pTarget, + UINT Flags1, + UINT Flags2, + ID3DBlob **ppCode, + ID3DBlob **ppErrorMsgs); + +static pfn_D3DCompile SDL_D3DCompile = NULL; + +// FIXME: includes and defines +static ID3DBlob *SDL_ShaderCross_INTERNAL_CompileDXBC( + const char *hlslSource, + const char *entrypoint, + const char *shaderProfile, + bool enableDebug) +{ + ID3DBlob *blob; + ID3DBlob *errorBlob; + HRESULT ret; + + if (SDL_D3DCompile == NULL) { + SDL_SetError("%s", "Could not load D3DCompile!"); + return NULL; + } + + ret = SDL_D3DCompile( + hlslSource, + SDL_strlen(hlslSource), + NULL, + NULL, + NULL, + entrypoint, + shaderProfile, + enableDebug ? 1 : 0, // D3DCOMPILE_DEBUG = 1 + 0, + &blob, + &errorBlob); + + if (ret < 0) { + if (errorBlob != NULL) { + SDL_SetError( + "HLSL compilation failed: %s", + (char *)errorBlob->lpVtbl->GetBufferPointer(errorBlob)); + } else { + SDL_SetError("HLSL compilation failed for an unknown reason."); + } + return NULL; + } + + return blob; +} + +void *SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL( + const SDL_ShaderCross_HLSL_Info *info, + bool enableRoundtrip, + size_t *size) // filled in with number of bytes of returned buffer +{ + char *transpiledSource = NULL; + + if (enableRoundtrip) { + // Need to roundtrip to SM 5.1 + size_t spirv_size; + void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL( + info, + &spirv_size); + + if (spirv == NULL) { + return NULL; + } + + SDL_ShaderCross_SPIRV_Info spirvInfo; + spirvInfo.bytecode = spirv; + spirvInfo.bytecode_size = spirv_size; + spirvInfo.entrypoint = info->entrypoint; + spirvInfo.shader_stage = info->shader_stage; + spirvInfo.props = info->props; + + transpiledSource = SDL_ShaderCross_TranspileHLSLFromSPIRV( + &spirvInfo); + SDL_free(spirv); + + if (transpiledSource == NULL) { + return NULL; + } + } + + const char *shaderProfile; + if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) { + shaderProfile = "vs_5_1"; + } else if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) { + shaderProfile = "ps_5_1"; + } else { // compute + shaderProfile = "cs_5_1"; + } + + ID3DBlob *blob = SDL_ShaderCross_INTERNAL_CompileDXBC( + transpiledSource != NULL ? transpiledSource : info->source, + info->entrypoint, + shaderProfile, + SDL_GetBooleanProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN, false)); + + if (blob == NULL) { + SDL_free(transpiledSource); + *size = 0; + return NULL; + } + + *size = blob->lpVtbl->GetBufferSize(blob); + void *buffer = SDL_malloc(*size); + SDL_memcpy(buffer, blob->lpVtbl->GetBufferPointer(blob), *size); + blob->lpVtbl->Release(blob); + + if (transpiledSource != NULL) { + SDL_free(transpiledSource); + } + + return buffer; +} + +// Returns raw byte buffer +void *SDL_ShaderCross_CompileDXBCFromHLSL( + const SDL_ShaderCross_HLSL_Info *info, + size_t *size) // filled in with number of bytes of returned buffer +{ + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + return SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL( + info, + !SDL_GetBooleanProperty(info->props, SDL_SHADERCROSS_PROP_HLSL_SKIP_SPIRV_ROUNDTRIP_BOOLEAN, false), + size); +} + +#include + +#define SPVC_ERROR(func) \ + SDL_SetError(#func " failed: %s", spvc_context_get_last_error_string(context)) + +static int parse_version_number(const char* str) +{ + unsigned major, minor, patch; + if (SDL_sscanf(str, "%u.%u.%u", &major, &minor, &patch) == 3) { + return (major * 10000) + (minor) * 100 + patch; + } + return -1; +} + +typedef struct SPIRVTranspileContext { + spvc_context context; + const char *translated_source; + const char *cleansed_entrypoint; +} SPIRVTranspileContext; + +static void SDL_ShaderCross_INTERNAL_DestroyTranspileContext( + SPIRVTranspileContext *context) +{ + spvc_context_destroy(context->context); + SDL_free(context); +} + +static SPIRVTranspileContext *SDL_ShaderCross_INTERNAL_TranspileFromSPIRV( + spvc_backend backend, + unsigned shadermodel, // only used for HLSL + SDL_ShaderCross_ShaderStage shaderStage, // only used for MSL + const Uint8 *code, + size_t codeSize, + const char *entrypoint, + SDL_PropertiesID props +) { + spvc_result result; + spvc_context context = NULL; + spvc_parsed_ir ir = NULL; + spvc_compiler compiler = NULL; + spvc_compiler_options options = NULL; + SPIRVTranspileContext *transpileContext = NULL; + const char *translated_source; + const char *cleansed_entrypoint; + + /* Create the SPIRV-Cross context */ + result = spvc_context_create(&context); + if (result < 0) { + SDL_SetError("spvc_context_create failed: %X", result); + return NULL; + } + + /* Parse the SPIR-V into IR */ + result = spvc_context_parse_spirv(context, (const SpvId *)code, codeSize / sizeof(SpvId), &ir); + if (result < 0) { + SPVC_ERROR(spvc_context_parse_spirv); + spvc_context_destroy(context); + return NULL; + } + + /* Create the cross-compiler */ + result = spvc_context_create_compiler(context, backend, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler); + if (result < 0) { + SPVC_ERROR(spvc_context_create_compiler); + spvc_context_destroy(context); + return NULL; + } + + /* Set up the cross-compiler options */ + result = spvc_compiler_create_compiler_options(compiler, &options); + if (result < 0) { + SPVC_ERROR(spvc_compiler_create_compiler_options); + spvc_context_destroy(context); + return NULL; + } + + if (backend == SPVC_BACKEND_HLSL) { + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL, shadermodel); + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV, 1); + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS, 1); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_HLSL_USE_ENTRY_POINT_NAME, !SDL_GetBooleanProperty(props, SDL_SHADERCROSS_PROP_SPIRV_PSSL_COMPATIBILITY_BOOLEAN, false)); + spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT, true); + } + + SpvExecutionModel executionModel; + if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_VERTEX) { + executionModel = SpvExecutionModelVertex; + } else if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT) { + executionModel = SpvExecutionModelFragment; + } else { // compute + if (backend == SPVC_BACKEND_HLSL) { + executionModel = SpvExecutionModelKernel; + } else { + executionModel = SpvExecutionModelGLCompute; + } + } + + if (backend == SPVC_BACKEND_MSL) { + const char *_mslVersion = SDL_GetStringProperty(props, SDL_SHADERCROSS_PROP_SPIRV_MSL_VERSION_STRING, "1.2.0"); + int mslVersion = parse_version_number(_mslVersion); + if (mslVersion == - 1) { + SDL_SetError("failed to parse MSL version string \"%s\"", _mslVersion); + spvc_context_destroy(context); + return NULL; + } + spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_MSL_VERSION, mslVersion); + } + + // MSL doesn't have descriptor sets, so we have to set up index remapping + if (backend == SPVC_BACKEND_MSL && shaderStage != SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) { + spvc_resources resources; + spvc_reflected_resource *reflected_resources; + size_t num_texture_samplers; + size_t num_storage_textures; + size_t num_storage_buffers; + size_t num_uniform_buffers; + size_t num_separate_samplers = 0; + size_t num_separate_images = 0; + + spvc_msl_resource_binding_2 bufferBindings[32]; + SDL_zeroa(bufferBindings); + Uint32 numBufferBindings = 0; + + spvc_msl_resource_binding_2 textureBindings[32]; + SDL_zeroa(textureBindings); + Uint32 numTextureBindings = 0; + + spvc_set active_variables; + result = spvc_compiler_get_active_interface_variables(compiler, &active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_get_active_interface_variables); + spvc_context_destroy(context); + return NULL; + } + + result = spvc_compiler_create_shader_resources_for_active_variables(compiler, &resources, active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_create_shader_resources_for_active_variables); + spvc_context_destroy(context); + return NULL; + } + + // Combined texture-samplers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_texture_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // If source is HLSL, we might have separate images and samplers + if (num_texture_samplers == 0) { + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + num_texture_samplers = num_separate_samplers; + } + + for (size_t i = 0; i < num_texture_samplers; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0 || descriptor_set_index == 2)) { + SDL_SetError("%s", "Descriptor set index for graphics texture-sampler must be 0 or 2!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // Storage textures + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_textures); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + for (size_t i = 0; i < num_storage_textures; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0 || descriptor_set_index == 2)) { + SDL_SetError("%s", "Descriptor set index for graphics storage texture must be 0 or 2!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // If source is HLSL, storage images might be marked as separate images + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_images); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // We only want to iterate the images that don't have an associated sampler + for (size_t i = num_separate_samplers; i < num_separate_images; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0 || descriptor_set_index == 2)) { + SDL_SetError("%s", "Descriptor set index for graphics storage texture must be 0 or 2!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // Uniform buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_uniform_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + for (size_t i = 0; i < num_uniform_buffers; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 1 || descriptor_set_index == 3)) { + SDL_SetError("%s", "Descriptor set index for graphics uniform buffer must be 1 or 3!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + bufferBindings[numBufferBindings].stage = executionModel; + bufferBindings[numBufferBindings].desc_set = descriptor_set_index; + bufferBindings[numBufferBindings].binding = binding_index; + bufferBindings[numBufferBindings].count = 1; + // assign binding index after we have collected all resources + + numBufferBindings += 1; + } + + // Storage buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + for (size_t i = 0; i < num_storage_buffers; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0 || descriptor_set_index == 2)) { + SDL_SetError("%s", "Descriptor set index for graphics storage buffer must be 0 or 2!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + bufferBindings[numBufferBindings].stage = executionModel; + bufferBindings[numBufferBindings].desc_set = descriptor_set_index; + bufferBindings[numBufferBindings].binding = binding_index; + bufferBindings[numBufferBindings].count = 1; + // assign binding index after we have collected all resources + + numBufferBindings += 1; + } + + // Textures come first so we can just use the binding slot + for (Uint32 i = 0; i < numTextureBindings; i += 1) { + textureBindings[i].msl_texture = textureBindings[i].binding; + textureBindings[i].msl_sampler = textureBindings[i].binding; + result = spvc_compiler_msl_add_resource_binding_2(compiler, &textureBindings[i]); + } + + if (result < 0) { + SPVC_ERROR(spvc_compiler_msl_add_resource_binding_2); + spvc_context_destroy(context); + return NULL; + } + + // Calculate number of uniform buffers + Uint32 uniformBufferCount = 0; + + for (Uint32 i = 0; i < numBufferBindings; i += 1) { + if (bufferBindings[i].desc_set == 1 || bufferBindings[i].desc_set == 3) { + uniformBufferCount += 1; + } + } + + // Calculate resource indices + for (Uint32 i = 0; i < numBufferBindings; i += 1) { + if (bufferBindings[i].desc_set == 1 || bufferBindings[i].desc_set == 3) { + // Uniform buffers are alone in the descriptor set + bufferBindings[i].msl_buffer = bufferBindings[i].binding; + } else { + // Subtract by the texture count because the textures precede the storage buffers in the descriptor set + bufferBindings[i].msl_buffer = uniformBufferCount + (bufferBindings[i].binding - numTextureBindings); + } + + result = spvc_compiler_msl_add_resource_binding_2(compiler, &bufferBindings[i]); + + if (result < 0) { + SPVC_ERROR(spvc_compiler_msl_add_resource_binding_2); + spvc_context_destroy(context); + return NULL; + } + } + } + + if (backend == SPVC_BACKEND_MSL && shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) { + spvc_resources resources; + spvc_reflected_resource *reflected_resources; + size_t num_texture_samplers; + size_t num_storage_textures; // total storage textures + size_t num_storage_buffers; // total storage buffers + size_t num_uniform_buffers; + size_t num_separate_samplers = 0; + size_t num_separate_images = 0; + + spvc_msl_resource_binding_2 bufferBindings[32]; + Uint32 numBufferBindings = 0; + + spvc_msl_resource_binding_2 textureBindings[32]; + Uint32 numTextureBindings = 0; + + spvc_set active_variables; + result = spvc_compiler_get_active_interface_variables(compiler, &active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_get_active_interface_variables); + spvc_context_destroy(context); + return NULL; + } + + result = spvc_compiler_create_shader_resources_for_active_variables(compiler, &resources, active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_create_shader_resources_for_active_variables); + spvc_context_destroy(context); + return NULL; + } + + // Combined texture-samplers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_texture_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // If source is HLSL, we might have separate images and samplers + if (num_texture_samplers == 0) { + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + num_texture_samplers = num_separate_samplers; + } + + for (size_t i = 0; i < num_texture_samplers; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (descriptor_set_index != 0) { + SDL_SetError("%s", "Descriptor set index for compute texture-sampler must be 0!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // Readonly storage textures + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_textures); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + for (size_t i = 0; i < num_storage_textures; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0 || descriptor_set_index == 1)) { + SDL_SetError("%s", "Descriptor set index for compute storage texture must be 0 or 1!"); + spvc_context_destroy(context); + return NULL; + } + + // Skip readwrite textures + if (descriptor_set_index != 0) { continue; } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // If source is HLSL, storage images might be marked as separate images + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_images); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // We only want to iterate the images that don't have an associated sampler + for (size_t i = num_separate_samplers; i < num_separate_images; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0 || descriptor_set_index == 1)) { + SDL_SetError("%s", "Descriptor set index for compute storage texture must be 0 or 1!"); + spvc_context_destroy(context); + return NULL; + } + + // Skip readwrite textures + if (descriptor_set_index != 0) { continue; } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // Readwrite storage textures + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_textures); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + for (size_t i = 0; i < num_storage_textures; i += 1) { + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + + // Skip readonly textures + if (descriptor_set_index != 1) { continue; } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // If source is HLSL, storage images might be marked as separate images + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_images); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // We only want to iterate the images that don't have an associated sampler + for (size_t i = num_separate_samplers; i < num_separate_images; i += 1) { + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + + // Skip readonly textures + if (descriptor_set_index != 1) { continue; } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + textureBindings[numTextureBindings].stage = executionModel; + textureBindings[numTextureBindings].desc_set = descriptor_set_index; + textureBindings[numTextureBindings].binding = binding_index; + textureBindings[numTextureBindings].count = 1; + // assign binding index after we have collected all resources + + numTextureBindings += 1; + } + + // Uniform buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_uniform_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + for (size_t i = 0; i < num_uniform_buffers; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (descriptor_set_index != 2) { + SDL_SetError("%s", "Descriptor set index for compute uniform buffer must be 2!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + bufferBindings[numBufferBindings].stage = executionModel; + bufferBindings[numBufferBindings].desc_set = descriptor_set_index; + bufferBindings[numBufferBindings].binding = binding_index; + bufferBindings[numBufferBindings].count = 1; + // assign binding index after we have collected all resources + + numBufferBindings += 1; + } + + // Storage buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // Readonly storage buffers + for (size_t i = 0; i < num_storage_buffers; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return NULL; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0|| descriptor_set_index == 1)) { + SDL_SetError("%s", "Descriptor set index for compute storage buffer must be 0 or 1!"); + spvc_context_destroy(context); + return NULL; + } + + // Skip readwrite buffers + if (descriptor_set_index != 0) { continue; } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + bufferBindings[numBufferBindings].stage = executionModel; + bufferBindings[numBufferBindings].desc_set = descriptor_set_index; + bufferBindings[numBufferBindings].binding = binding_index; + bufferBindings[numBufferBindings].count = 1; + // assign binding index after we have collected all resources + + numBufferBindings += 1; + } + + // Readwrite storage buffers + for (size_t i = 0; i < num_storage_buffers; i += 1) { + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + + // Skip readonly buffers + if (descriptor_set_index != 1) { continue; } + + unsigned int binding_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding); + + bufferBindings[numBufferBindings].stage = executionModel; + bufferBindings[numBufferBindings].desc_set = descriptor_set_index; + bufferBindings[numBufferBindings].binding = binding_index; + bufferBindings[numBufferBindings].count = 1; + // assign binding index after we have collected all resources + + numBufferBindings += 1; + } + + // Calculate binding offsets + + Uint32 readonlyTextureCount = 0; + Uint32 readwriteTextureCount = 0; + + for (Uint32 i = 0; i < numTextureBindings; i += 1) { + if (textureBindings[i].desc_set == 0) { + readonlyTextureCount += 1; + } else if (textureBindings[i].desc_set == 1) { + readwriteTextureCount += 1; + } + } + + Uint32 uniformBufferCount = 0; + Uint32 readonlyBufferCount = 0; + + for (Uint32 i = 0; i < numBufferBindings; i += 1) { + if (bufferBindings[i].desc_set == 0) { + readonlyBufferCount += 1; + } else if (bufferBindings[i].desc_set == 2) { + uniformBufferCount += 1; + } + } + + // Calculate resource indices + + for (Uint32 i = 0; i < numTextureBindings; i += 1) { + if (textureBindings[i].desc_set == 0) { + // readonly textures + textureBindings[i].msl_texture = textureBindings[i].binding; + textureBindings[i].msl_sampler = textureBindings[i].binding; + } else { + // readwrite textures + textureBindings[i].msl_texture = readonlyTextureCount + textureBindings[i].binding; + textureBindings[i].msl_sampler = readonlyTextureCount + textureBindings[i].binding; + } + result = spvc_compiler_msl_add_resource_binding_2(compiler, &textureBindings[i]); + if (result < 0) { + SPVC_ERROR(spvc_compiler_msl_add_resource_binding_2); + spvc_context_destroy(context); + return NULL; + } + } + + for (Uint32 i = 0; i < numBufferBindings; i += 1) { + if (bufferBindings[i].desc_set == 0) { + // Subtract by the readonly texture count because they precede readonly buffers in the descriptor set + bufferBindings[i].msl_buffer = uniformBufferCount + (bufferBindings[i].binding - readonlyTextureCount); + } else if (bufferBindings[i].desc_set == 1) { + // Subtract by the readwrite texture count because they precede readwrite buffers in the descriptor set + bufferBindings[i].msl_buffer = uniformBufferCount + readonlyBufferCount + (bufferBindings[i].binding - readwriteTextureCount); + } else { + // Uniform buffers are alone in the descriptor set + bufferBindings[i].msl_buffer = bufferBindings[i].binding; + } + result = spvc_compiler_msl_add_resource_binding_2(compiler, &bufferBindings[i]); + + if (result < 0) { + SPVC_ERROR(spvc_compiler_msl_add_resource_binding_2); + spvc_context_destroy(context); + return NULL; + } + } + } + + result = spvc_compiler_install_compiler_options(compiler, options); + if (result < 0) { + SPVC_ERROR(spvc_compiler_install_compiler_options); + spvc_context_destroy(context); + return NULL; + } + + /* Compile to the target shader language */ + result = spvc_compiler_compile(compiler, &translated_source); + if (result < 0) { + SPVC_ERROR(spvc_compiler_compile); + spvc_context_destroy(context); + return NULL; + } + + if (backend == SPVC_BACKEND_MSL) { + // Metal doesn't allow a "main" entrypoint, so determine the "cleansed" entrypoint name (e.g. main -> main0 on MSL) + cleansed_entrypoint = spvc_compiler_get_cleansed_entry_point_name( + compiler, + entrypoint, + spvc_compiler_get_execution_model(compiler)); + } else { + cleansed_entrypoint = entrypoint; + } + + transpileContext = SDL_malloc(sizeof(SPIRVTranspileContext)); + transpileContext->context = context; + transpileContext->cleansed_entrypoint = cleansed_entrypoint; + transpileContext->translated_source = translated_source; + return transpileContext; +} + +size_t SDL_ShaderCross_INTERNAL_GetIOVarsStringLength( + spvc_reflected_resource* reflected_resources, + size_t num_vars) +{ + size_t total_string_size = 0; + for (size_t i = 0; i < num_vars; i++) { + spvc_reflected_resource* resource = &reflected_resources[i]; + total_string_size += SDL_strlen(resource->name) + 1; + } + return total_string_size; +} + +void SDL_ShaderCross_INTERNAL_GetIOVars( + spvc_compiler compiler, + spvc_reflected_resource* reflected_resources, + size_t num_vars, + SDL_ShaderCross_IOVarMetadata* vars, + char *name_buffer +) { + size_t name_buffer_offset = 0; + for (size_t i = 0; i < num_vars; i++) { + SDL_ShaderCross_IOVarMetadata* var = &vars[i]; + spvc_reflected_resource* resource = &reflected_resources[i]; + spvc_type type = spvc_compiler_get_type_handle(compiler, resource->base_type_id); + + switch (spvc_type_get_basetype(type)) { + case SPVC_BASETYPE_INT8: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_INT8; + break; + case SPVC_BASETYPE_UINT8: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_UINT8; + break; + case SPVC_BASETYPE_INT16: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_INT16; + break; + case SPVC_BASETYPE_UINT16: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_UINT16; + break; + case SPVC_BASETYPE_INT32: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_INT32; + break; + case SPVC_BASETYPE_UINT32: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_UINT32; + break; + case SPVC_BASETYPE_INT64: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_INT64; + break; + case SPVC_BASETYPE_UINT64: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_UINT64; + break; + case SPVC_BASETYPE_FP16: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_FLOAT16; + break; + case SPVC_BASETYPE_FP32: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_FLOAT32; + break; + case SPVC_BASETYPE_FP64: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_FLOAT64; + break; + default: + var->vector_type = SDL_SHADERCROSS_IOVAR_TYPE_UNKNOWN; + break; + } + + Uint32 vector_size = spvc_type_get_vector_size(type); + var->vector_size = vector_size; + + var->name = name_buffer + name_buffer_offset; + size_t length_name = SDL_strlen(resource->name) + 1; + SDL_memcpy(var->name, resource->name, length_name); + name_buffer_offset += length_name; + var->location = spvc_compiler_get_decoration(compiler, resource->id, SpvDecorationLocation); + } +} + +// Acquire metadata from SPIRV bytecode. +// TODO: validate descriptor sets +SDL_ShaderCross_GraphicsShaderMetadata * SDL_ShaderCross_ReflectGraphicsSPIRV( + const Uint8 *code, + size_t codeSize, + SDL_PropertiesID metadataProps +) { + spvc_result result; + spvc_context context = NULL; + spvc_parsed_ir ir = NULL; + spvc_compiler compiler = NULL; + size_t num_texture_samplers = 0; + size_t num_storage_textures = 0; + size_t num_storage_buffers = 0; + size_t num_uniform_buffers = 0; + size_t string_length_input; + size_t num_inputs = 0; + size_t string_length_output; + size_t num_outputs = 0; + size_t num_separate_samplers = 0; // HLSL edge case + size_t num_separate_images = 0; // HLSL edge case + (void) metadataProps; + + if (code == NULL) { + SDL_InvalidParamError("code"); + return NULL; + } + + /* Create the SPIRV-Cross context */ + result = spvc_context_create(&context); + if (result < 0) { + SDL_SetError("spvc_context_create failed: %X", result); + return NULL; + } + + /* Parse the SPIR-V into IR */ + result = spvc_context_parse_spirv(context, (const SpvId *)code, codeSize / sizeof(SpvId), &ir); + if (result < 0) { + SPVC_ERROR(spvc_context_parse_spirv); + spvc_context_destroy(context); + return NULL; + } + + /* Create a reflection-only compiler */ + result = spvc_context_create_compiler(context, SPVC_BACKEND_NONE, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler); + if (result < 0) { + SPVC_ERROR(spvc_context_create_compiler); + spvc_context_destroy(context); + return NULL; + } + + spvc_resources resources; + spvc_reflected_resource *reflected_resources; + + spvc_set active_variables; + result = spvc_compiler_get_active_interface_variables(compiler, &active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_get_active_interface_variables); + spvc_context_destroy(context); + return NULL; + } + + result = spvc_compiler_create_shader_resources_for_active_variables(compiler, &resources, active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_create_shader_resources_for_active_variables); + spvc_context_destroy(context); + return NULL; + } + + // Combined texture-samplers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_texture_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // If source is HLSL, we might have separate images and samplers + if (num_texture_samplers == 0) { + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + num_texture_samplers = num_separate_samplers; + } + + // Storage textures + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_textures); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // If source is HLSL, storage images might be marked as separate images + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_images); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + // The number of storage textures is the number of separate images minus the number of samplers. + num_storage_textures += (num_separate_images - num_separate_samplers); + + // Storage buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // Uniform buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_uniform_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + + // Inputs (stage 1: count number of inputs, and name lengths) + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STAGE_INPUT, + (const spvc_reflected_resource **)&reflected_resources, + &num_inputs); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + string_length_input = SDL_ShaderCross_INTERNAL_GetIOVarsStringLength(reflected_resources, num_inputs); + + // Outputs (stage 1: count number of outputs, and name lengths) + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STAGE_OUTPUT, + (const spvc_reflected_resource **)&reflected_resources, + &num_outputs); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return NULL; + } + string_length_output = SDL_ShaderCross_INTERNAL_GetIOVarsStringLength(reflected_resources, num_outputs); + + size_t offset_inputs = SDL_upper_multiple_power2(sizeof(SDL_ShaderCross_GraphicsShaderMetadata), sizeof(size_t)); + size_t offset_outputs = offset_inputs + num_inputs * sizeof(SDL_ShaderCross_IOVarMetadata); + size_t offset_inputnames = offset_outputs + num_outputs * sizeof(SDL_ShaderCross_IOVarMetadata); + size_t offset_outputnames = offset_inputnames + string_length_input; + + char *allocMemory = SDL_malloc(offset_outputnames + string_length_output); + if (!allocMemory) { + spvc_context_destroy(context); + return NULL; + } + + SDL_ShaderCross_GraphicsShaderMetadata *allocMetadata = (SDL_ShaderCross_GraphicsShaderMetadata *)allocMemory; + allocMetadata->inputs = (SDL_ShaderCross_IOVarMetadata *)(allocMemory + offset_inputs); + allocMetadata->outputs = (SDL_ShaderCross_IOVarMetadata *)(allocMemory + offset_outputs); + + // Inputs (stage 2: fill in inputs) + size_t num_inputs_run2 = 0; + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STAGE_INPUT, + (const spvc_reflected_resource **)&reflected_resources, + &num_inputs_run2); + if (result < 0 || num_inputs != num_inputs_run2) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + SDL_free(allocMemory); + return false; + } + SDL_ShaderCross_INTERNAL_GetIOVars(compiler, reflected_resources, num_inputs, allocMetadata->inputs, allocMemory + offset_inputnames); + + // Inputs (stage 2: fill in outputs) + size_t num_outputs_run2 = 0; + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STAGE_OUTPUT, + (const spvc_reflected_resource **)&reflected_resources, + &num_outputs_run2); + if (result < 0 || num_outputs != num_outputs_run2) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + SDL_free(allocMemory); + return false; + } + SDL_ShaderCross_INTERNAL_GetIOVars(compiler, reflected_resources, num_outputs, allocMetadata->outputs, allocMemory + offset_outputnames); + spvc_context_destroy(context); + + allocMetadata->resource_info.num_samplers = num_texture_samplers; + allocMetadata->resource_info.num_storage_textures = num_storage_textures; + allocMetadata->resource_info.num_storage_buffers = num_storage_buffers; + allocMetadata->resource_info.num_uniform_buffers = num_uniform_buffers; + allocMetadata->num_inputs = num_inputs; + allocMetadata->num_outputs = num_outputs; + + return allocMetadata; +} + +SDL_ShaderCross_ComputePipelineMetadata * SDL_ShaderCross_ReflectComputeSPIRV( + const Uint8 *bytecode, + size_t bytecodeSize, + SDL_PropertiesID metadataProps +) { + spvc_result result; + spvc_context context = NULL; + spvc_parsed_ir ir = NULL; + spvc_compiler compiler = NULL; + size_t num_texture_samplers = 0; + size_t num_readonly_storage_textures = 0; + size_t num_readonly_storage_buffers = 0; + size_t num_readwrite_storage_textures = 0; + size_t num_readwrite_storage_buffers = 0; + size_t num_uniform_buffers = 0; + + size_t num_storage_textures = 0; + size_t num_storage_buffers = 0; + size_t num_separate_samplers = 0; // HLSL edge case + size_t num_separate_images = 0; // HLSL edge case + + (void) metadataProps; + + if (bytecode == NULL) { + SDL_InvalidParamError("bytecode"); + return NULL; + } + + /* Create the SPIRV-Cross context */ + result = spvc_context_create(&context); + if (result < 0) { + SDL_SetError("spvc_context_create failed: %X", result); + return false; + } + + /* Parse the SPIR-V into IR */ + result = spvc_context_parse_spirv(context, (const SpvId *)bytecode, bytecodeSize / sizeof(SpvId), &ir); + if (result < 0) { + SPVC_ERROR(spvc_context_parse_spirv); + spvc_context_destroy(context); + return false; + } + + /* Create a reflection-only compiler */ + result = spvc_context_create_compiler(context, SPVC_BACKEND_NONE, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler); + if (result < 0) { + SPVC_ERROR(spvc_context_create_compiler); + spvc_context_destroy(context); + return false; + } + + spvc_resources resources; + spvc_reflected_resource *reflected_resources; + + spvc_set active_variables; + result = spvc_compiler_get_active_interface_variables(compiler, &active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_get_active_interface_variables); + spvc_context_destroy(context); + return false; + } + + result = spvc_compiler_create_shader_resources_for_active_variables(compiler, &resources, active_variables); + if (result < 0) { + SPVC_ERROR(spvc_compiler_create_shader_resources_for_active_variables); + spvc_context_destroy(context); + return false; + } + + // Combined texture-samplers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SAMPLED_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_texture_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return false; + } + + // If source is HLSL, we might have separate images and samplers + if (num_texture_samplers == 0) { + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_samplers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return false; + } + num_texture_samplers = num_separate_samplers; + } + + // Storage textures + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_textures); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return false; + } + + for (size_t i = 0; i < num_storage_textures; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return false; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + + if (descriptor_set_index == 0) { + num_readonly_storage_textures += 1; + } else if (descriptor_set_index == 1) { + num_readwrite_storage_textures += 1; + } else { + SDL_SetError("%s", "Descriptor set index for compute storage texture must be 0 or 1!"); + spvc_context_destroy(context); + return false; + } + } + + // If source is HLSL, readonly storage images might be marked as separate images + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE, + (const spvc_reflected_resource **)&reflected_resources, + &num_separate_images); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return false; + } + + // The number of storage textures is the number of separate images minus the number of samplers. + num_storage_textures += (num_separate_images - num_separate_samplers); + + for (size_t i = num_separate_samplers; i < num_separate_images; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return false; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + + if (descriptor_set_index == 0) { + num_readonly_storage_textures += 1; + } else if (descriptor_set_index == 1) { + num_readwrite_storage_textures += 1; + } else { + SDL_SetError("%s", "Descriptor set index for compute storage texture must be 0 or 1!"); + spvc_context_destroy(context); + return false; + } + } + + // Storage buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_STORAGE_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_storage_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return false; + } + + // Readonly storage buffers + for (size_t i = 0; i < num_storage_buffers; i += 1) { + if (!spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet) || !spvc_compiler_has_decoration(compiler, reflected_resources[i].id, SpvDecorationBinding)) { + SDL_SetError("%s", "Shader resources must have descriptor set and binding index!"); + spvc_context_destroy(context); + return false; + } + + unsigned int descriptor_set_index = spvc_compiler_get_decoration(compiler, reflected_resources[i].id, SpvDecorationDescriptorSet); + if (!(descriptor_set_index == 0 || descriptor_set_index == 1)) { + SDL_SetError("%s", "Descriptor set index for compute storage buffer must be 0 or 1!"); + spvc_context_destroy(context); + return false; + } + + if (descriptor_set_index == 0) { + num_readonly_storage_buffers += 1; + } else if (descriptor_set_index == 1) { + num_readwrite_storage_buffers += 1; + } else { + SDL_SetError("%s", "Descriptor set index for compute storage buffer must be 0 or 1!"); + spvc_context_destroy(context); + return false; + } + } + + // Uniform buffers + result = spvc_resources_get_resource_list_for_type( + resources, + SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, + (const spvc_reflected_resource **)&reflected_resources, + &num_uniform_buffers); + if (result < 0) { + SPVC_ERROR(spvc_resources_get_resource_list_for_type); + spvc_context_destroy(context); + return false; + } + + // Threadcount + SDL_ShaderCross_ComputePipelineMetadata *metadata = SDL_malloc(sizeof(SDL_ShaderCross_ComputePipelineMetadata)); + if (!metadata) { + return NULL; + } + metadata->threadcount_x = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 0); + metadata->threadcount_y = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 1); + metadata->threadcount_z = spvc_compiler_get_execution_mode_argument_by_index(compiler, SpvExecutionModeLocalSize, 2); + + spvc_context_destroy(context); + + metadata->num_samplers = num_texture_samplers; + metadata->num_readonly_storage_textures = num_readonly_storage_textures; + metadata->num_readonly_storage_buffers = num_readonly_storage_buffers; + metadata->num_readwrite_storage_textures = num_readwrite_storage_textures; + metadata->num_readwrite_storage_buffers = num_readwrite_storage_buffers; + metadata->num_uniform_buffers = num_uniform_buffers; + return metadata; +} + +static void *SDL_ShaderCross_INTERNAL_CompileFromSPIRV( + SDL_GPUDevice *device, + const SDL_ShaderCross_SPIRV_Info *info, + SDL_GPUShaderFormat targetFormat, + SDL_PropertiesID metadataProps +) { + spvc_backend backend; + unsigned shadermodel = 0; + + if (targetFormat == SDL_GPU_SHADERFORMAT_DXBC) { + backend = SPVC_BACKEND_HLSL; + shadermodel = 51; + } else if (targetFormat == SDL_GPU_SHADERFORMAT_DXIL) { + backend = SPVC_BACKEND_HLSL; + shadermodel = 60; + } else if (targetFormat == SDL_GPU_SHADERFORMAT_MSL) { + backend = SPVC_BACKEND_MSL; + } else { + SDL_SetError("SDL_ShaderCross_INTERNAL_CompileFromSPIRV: Unexpected SDL_GPUBackend"); + return NULL; + } + + SPIRVTranspileContext *transpileContext = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV( + backend, + shadermodel, + info->shader_stage, + info->bytecode, + info->bytecode_size, + info->entrypoint, + info->props); + + if (transpileContext == NULL) { + return NULL; + } + + void *shaderObject = NULL; + + if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) { + SDL_ShaderCross_ComputePipelineMetadata *pipelineInfo = SDL_ShaderCross_ReflectComputeSPIRV( + info->bytecode, + info->bytecode_size, + metadataProps); + SDL_GPUComputePipelineCreateInfo createInfo; + + createInfo.entrypoint = transpileContext->cleansed_entrypoint; + createInfo.format = targetFormat; + createInfo.num_samplers = pipelineInfo->num_samplers; + createInfo.num_readonly_storage_textures = pipelineInfo->num_readonly_storage_textures; + createInfo.num_readonly_storage_buffers = pipelineInfo->num_readonly_storage_buffers; + createInfo.num_readwrite_storage_textures = pipelineInfo->num_readwrite_storage_textures; + createInfo.num_readwrite_storage_buffers = pipelineInfo->num_readwrite_storage_buffers; + createInfo.num_uniform_buffers = pipelineInfo->num_uniform_buffers; + createInfo.threadcount_x = pipelineInfo->threadcount_x; + createInfo.threadcount_y = pipelineInfo->threadcount_y; + createInfo.threadcount_z = pipelineInfo->threadcount_z; + + createInfo.props = 0; + + const char *debugName = SDL_GetStringProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, NULL); + if (debugName) { + createInfo.props = SDL_CreateProperties(); + SDL_SetStringProperty(createInfo.props, SDL_PROP_GPU_COMPUTEPIPELINE_CREATE_NAME_STRING, debugName); + } + + SDL_ShaderCross_HLSL_Info hlslInfo; + hlslInfo.source = transpileContext->translated_source; + hlslInfo.entrypoint = transpileContext->cleansed_entrypoint; + hlslInfo.include_dir = NULL; + hlslInfo.defines = NULL; + hlslInfo.shader_stage = SDL_SHADERCROSS_SHADERSTAGE_COMPUTE; + hlslInfo.props = info->props; + + if (targetFormat == SDL_GPU_SHADERFORMAT_DXBC) { + createInfo.code = SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL( + &hlslInfo, + false, + &createInfo.code_size); + } else if (targetFormat == SDL_GPU_SHADERFORMAT_DXIL) { + createInfo.code = SDL_ShaderCross_CompileDXILFromHLSL( + &hlslInfo, + &createInfo.code_size); + } else { // MSL + createInfo.code = (const Uint8 *)transpileContext->translated_source; + createInfo.code_size = SDL_strlen(transpileContext->translated_source) + 1; + } + + shaderObject = SDL_CreateGPUComputePipeline(device, &createInfo); + + if (createInfo.props != 0) { + SDL_DestroyProperties(createInfo.props); + } + SDL_free(pipelineInfo); + } else { + SDL_GPUShaderCreateInfo createInfo; + SDL_ShaderCross_GraphicsShaderMetadata *shaderInfo = + SDL_ShaderCross_ReflectGraphicsSPIRV( + info->bytecode, + info->bytecode_size, + metadataProps); + + if (shaderInfo == NULL) { + SDL_ShaderCross_INTERNAL_DestroyTranspileContext(transpileContext); + return NULL; + } + createInfo.entrypoint = transpileContext->cleansed_entrypoint; + createInfo.format = targetFormat; + createInfo.stage = (SDL_GPUShaderStage)info->shader_stage; + createInfo.num_samplers = shaderInfo->resource_info.num_samplers; + createInfo.num_storage_textures = shaderInfo->resource_info.num_storage_textures; + createInfo.num_storage_buffers = shaderInfo->resource_info.num_storage_buffers; + createInfo.num_uniform_buffers = shaderInfo->resource_info.num_uniform_buffers; + + createInfo.props = 0; + + const char *debugName = SDL_GetStringProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, NULL); + if (debugName) { + createInfo.props = SDL_CreateProperties(); + SDL_SetStringProperty(createInfo.props, SDL_PROP_GPU_SHADER_CREATE_NAME_STRING, debugName); + } + + SDL_ShaderCross_HLSL_Info hlslInfo; + hlslInfo.source = transpileContext->translated_source; + hlslInfo.entrypoint = transpileContext->cleansed_entrypoint; + hlslInfo.include_dir = NULL; + hlslInfo.defines = NULL; + hlslInfo.shader_stage = info->shader_stage; + hlslInfo.props = info->props; + + if (targetFormat == SDL_GPU_SHADERFORMAT_DXBC) { + createInfo.code = SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL( + &hlslInfo, + false, + &createInfo.code_size); + } else if (targetFormat == SDL_GPU_SHADERFORMAT_DXIL) { + createInfo.code = SDL_ShaderCross_CompileDXILFromHLSL( + &hlslInfo, + &createInfo.code_size); + } else { // MSL + createInfo.code = (const Uint8 *)transpileContext->translated_source; + createInfo.code_size = SDL_strlen(transpileContext->translated_source) + 1; + } + + shaderObject = SDL_CreateGPUShader(device, &createInfo); + + if (createInfo.props != 0) { + SDL_DestroyProperties(createInfo.props); + } + + SDL_free(shaderInfo); + } + + SDL_ShaderCross_INTERNAL_DestroyTranspileContext(transpileContext); + return shaderObject; +} + +void *SDL_ShaderCross_TranspileMSLFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info) +{ + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV( + SPVC_BACKEND_MSL, + 0, + info->shader_stage, + info->bytecode, + info->bytecode_size, + info->entrypoint, + info->props + ); + + if (context == NULL) { + return NULL; + } + + size_t length = SDL_strlen(context->translated_source) + 1; + char *result = SDL_malloc(length); + SDL_strlcpy(result, context->translated_source, length); + + SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context); + return result; +} + +void *SDL_ShaderCross_TranspileHLSLFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info) +{ + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV( + SPVC_BACKEND_HLSL, + SDL_GetBooleanProperty(info->props, SDL_SHADERCROSS_PROP_SPIRV_PSSL_COMPATIBILITY_BOOLEAN, false) ? 50 : 60, + info->shader_stage, + info->bytecode, + info->bytecode_size, + info->entrypoint, + info->props + ); + + if (context == NULL) { + return NULL; + } + + size_t length = SDL_strlen(context->translated_source) + 1; + char *result = SDL_malloc(length); + SDL_strlcpy(result, context->translated_source, length); + + SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context); + return result; +} + +void *SDL_ShaderCross_CompileDXBCFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info, + size_t *size) +{ + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV( + SPVC_BACKEND_HLSL, + 51, + info->shader_stage, + info->bytecode, + info->bytecode_size, + info->entrypoint, + info->props); + + if (context == NULL) { + return NULL; + } + + SDL_ShaderCross_HLSL_Info hlslInfo; + hlslInfo.source = context->translated_source; + hlslInfo.entrypoint = context->cleansed_entrypoint; + hlslInfo.include_dir = NULL; + hlslInfo.defines = NULL; + hlslInfo.shader_stage = info->shader_stage; + hlslInfo.props = info->props; + + void *result = SDL_ShaderCross_INTERNAL_CompileDXBCFromHLSL( + &hlslInfo, + false, + size); + + SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context); + return result; +} + +void *SDL_ShaderCross_CompileDXILFromSPIRV( + const SDL_ShaderCross_SPIRV_Info *info, + size_t *size) +{ +#ifndef SDL_SHADERCROSS_DXC + SDL_SetError("%s", "Shadercross was not compiled with DXC support, cannot compile to SPIR-V!"); + return NULL; +#endif + + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + SPIRVTranspileContext *context = SDL_ShaderCross_INTERNAL_TranspileFromSPIRV( + SPVC_BACKEND_HLSL, + 60, + info->shader_stage, + info->bytecode, + info->bytecode_size, + info->entrypoint, + info->props); + + if (context == NULL) { + return NULL; + } + + SDL_ShaderCross_HLSL_Info hlslInfo; + hlslInfo.source = context->translated_source; + hlslInfo.entrypoint = context->cleansed_entrypoint; + hlslInfo.include_dir = NULL; + hlslInfo.defines = NULL; + hlslInfo.shader_stage = info->shader_stage; + hlslInfo.props = info->props; + + void *result = SDL_ShaderCross_INTERNAL_CompileUsingDXC( + &hlslInfo, + false, + size); + + SDL_ShaderCross_INTERNAL_DestroyTranspileContext(context); + return result; +} + +static void *SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV( + SDL_GPUDevice *device, + const SDL_ShaderCross_SPIRV_Info *info, + const void *metadata, + SDL_PropertiesID metadataProps) +{ + SDL_GPUShaderFormat format; + + SDL_GPUShaderFormat shader_formats = SDL_GetGPUShaderFormats(device); + + if (shader_formats & SDL_GPU_SHADERFORMAT_SPIRV) { + if (info->shader_stage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) { + SDL_GPUComputePipelineCreateInfo createInfo; + SDL_ShaderCross_ComputePipelineMetadata *pipelineMetadata = (SDL_ShaderCross_ComputePipelineMetadata *)metadata; + createInfo.code = info->bytecode; + createInfo.code_size = info->bytecode_size; + createInfo.entrypoint = info->entrypoint; + createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV; + createInfo.num_samplers = pipelineMetadata->num_samplers; + createInfo.num_readonly_storage_textures = pipelineMetadata->num_readonly_storage_textures; + createInfo.num_readonly_storage_buffers = pipelineMetadata->num_readonly_storage_buffers; + createInfo.num_readwrite_storage_textures = pipelineMetadata->num_readwrite_storage_textures; + createInfo.num_readwrite_storage_buffers = pipelineMetadata->num_readwrite_storage_buffers; + createInfo.num_uniform_buffers = pipelineMetadata->num_uniform_buffers; + createInfo.threadcount_x = pipelineMetadata->threadcount_x; + createInfo.threadcount_y = pipelineMetadata->threadcount_y; + createInfo.threadcount_z = pipelineMetadata->threadcount_z; + + createInfo.props = 0; + + const char *debugName = SDL_GetStringProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, NULL); + if (debugName) { + createInfo.props = SDL_CreateProperties(); + SDL_SetStringProperty(createInfo.props, SDL_PROP_GPU_COMPUTEPIPELINE_CREATE_NAME_STRING, debugName); + } + + SDL_GPUComputePipeline *result = SDL_CreateGPUComputePipeline(device, &createInfo); + + if (createInfo.props != 0) { + SDL_DestroyProperties(createInfo.props); + } + + return result; + } else { + SDL_GPUShaderCreateInfo createInfo; + SDL_ShaderCross_GraphicsShaderResourceInfo *resourceInfo = (SDL_ShaderCross_GraphicsShaderResourceInfo *)metadata; + + createInfo.code = info->bytecode; + createInfo.code_size = info->bytecode_size; + createInfo.entrypoint = info->entrypoint; + createInfo.format = SDL_GPU_SHADERFORMAT_SPIRV; + createInfo.stage = (SDL_GPUShaderStage)info->shader_stage; + createInfo.num_samplers = resourceInfo->num_samplers; + createInfo.num_storage_textures = resourceInfo->num_storage_textures; + createInfo.num_storage_buffers = resourceInfo->num_storage_buffers; + createInfo.num_uniform_buffers = resourceInfo->num_uniform_buffers; + + createInfo.props = 0; + + const char *debugName = SDL_GetStringProperty(info->props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, NULL); + if (debugName) { + createInfo.props = SDL_CreateProperties(); + SDL_SetStringProperty(createInfo.props, SDL_PROP_GPU_SHADER_CREATE_NAME_STRING, debugName); + } + + SDL_GPUShader *result = SDL_CreateGPUShader(device, &createInfo); + + if (createInfo.props != 0) { + SDL_DestroyProperties(createInfo.props); + } + + return result; + } + } else if (shader_formats & SDL_GPU_SHADERFORMAT_MSL) { + format = SDL_GPU_SHADERFORMAT_MSL; + } else { + if ((shader_formats & SDL_GPU_SHADERFORMAT_DXBC) && SDL_D3DCompile != NULL) { + format = SDL_GPU_SHADERFORMAT_DXBC; + } +#ifdef SDL_SHADERCROSS_DXC + else if (shader_formats & SDL_GPU_SHADERFORMAT_DXIL) { + format = SDL_GPU_SHADERFORMAT_DXIL; + } +#endif + else { + SDL_SetError("SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV: Unexpected SDL_GPUBackend"); + return NULL; + } + } + + return SDL_ShaderCross_INTERNAL_CompileFromSPIRV( + device, + info, + format, + metadataProps); +} + +SDL_GPUShader *SDL_ShaderCross_CompileGraphicsShaderFromSPIRV( + SDL_GPUDevice *device, + const SDL_ShaderCross_SPIRV_Info *info, + const SDL_ShaderCross_GraphicsShaderResourceInfo *resourceInfo, + SDL_PropertiesID props) +{ + if (device == NULL) { + SDL_InvalidParamError("device"); + return NULL; + } + + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + if (resourceInfo == NULL) { + SDL_InvalidParamError("metadata"); + return NULL; + } + + return (SDL_GPUShader *)SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV( + device, + info, + (void*) resourceInfo, + props); +} + +SDL_GPUComputePipeline *SDL_ShaderCross_CompileComputePipelineFromSPIRV( + SDL_GPUDevice *device, + const SDL_ShaderCross_SPIRV_Info *info, + const SDL_ShaderCross_ComputePipelineMetadata *metadata, + SDL_PropertiesID props) +{ + if (device == NULL) { + SDL_InvalidParamError("device"); + return NULL; + } + + if (info == NULL) { + SDL_InvalidParamError("info"); + return NULL; + } + + if (metadata == NULL) { + SDL_InvalidParamError("metadata"); + return NULL; + } + + return (SDL_GPUComputePipeline *)SDL_ShaderCross_INTERNAL_CreateShaderFromSPIRV( + device, + info, + (void*) metadata, + props); +} + +bool SDL_ShaderCross_Init(void) +{ + d3dcompiler_dll = SDL_LoadObject(D3DCOMPILER_DLL); + + if (d3dcompiler_dll != NULL) { + SDL_D3DCompile = (pfn_D3DCompile)SDL_LoadFunction(d3dcompiler_dll, "D3DCompile"); + + if (SDL_D3DCompile == NULL) { + SDL_UnloadObject(d3dcompiler_dll); + d3dcompiler_dll = NULL; + } + } + + return true; +} + +void SDL_ShaderCross_Quit(void) +{ + if (d3dcompiler_dll != NULL) { + SDL_UnloadObject(d3dcompiler_dll); + d3dcompiler_dll = NULL; + + SDL_D3DCompile = NULL; + } +} + +SDL_GPUShaderFormat SDL_ShaderCross_GetSPIRVShaderFormats(void) +{ + /* SPIRV and MSL can always be output as-is with no preprocessing since we require SPIRV-Cross */ + SDL_GPUShaderFormat supportedFormats = SDL_GPU_SHADERFORMAT_SPIRV | SDL_GPU_SHADERFORMAT_MSL; + + /* SPIRV-Cross + DXC allows us to cross-compile to HLSL, then compile to DXIL */ +#ifdef SDL_SHADERCROSS_DXC + supportedFormats |= SDL_GPU_SHADERFORMAT_DXIL; +#endif + + /* SPIRV-Cross + FXC allows us to cross-compile to HLSL, then compile to DXBC */ + if (d3dcompiler_dll != NULL) { + supportedFormats |= SDL_GPU_SHADERFORMAT_DXBC; + } + + return supportedFormats; +} + +SDL_GPUShaderFormat SDL_ShaderCross_GetHLSLShaderFormats(void) +{ + SDL_GPUShaderFormat supportedFormats = 0; + + /* DXC allows compilation from HLSL to SPIRV */ +#ifdef SDL_SHADERCROSS_DXC + supportedFormats |= SDL_ShaderCross_GetSPIRVShaderFormats(); +#endif + + /* FXC allows compilation of HLSL to DXBC */ + if (d3dcompiler_dll != NULL) { + supportedFormats |= SDL_GPU_SHADERFORMAT_DXBC; + } + + return supportedFormats; +} diff --git a/thirdparty/SDL_shadercross/src/SDL_shadercross.sym b/thirdparty/SDL_shadercross/src/SDL_shadercross.sym new file mode 100644 index 000000000..9b0cec561 --- /dev/null +++ b/thirdparty/SDL_shadercross/src/SDL_shadercross.sym @@ -0,0 +1,19 @@ +SDL3_shadercross_0.0.0 { + global: + SDL_ShaderCross_Init; + SDL_ShaderCross_Quit; + SDL_ShaderCross_GetSPIRVShaderFormats; + SDL_ShaderCross_TranspileMSLFromSPIRV; + SDL_ShaderCross_TranspileHLSLFromSPIRV; + SDL_ShaderCross_CompileDXBCFromSPIRV; + SDL_ShaderCross_CompileDXILFromSPIRV; + SDL_ShaderCross_CompileGraphicsShaderFromSPIRV; + SDL_ShaderCross_CompileComputePipelineFromSPIRV; + SDL_ShaderCross_GetHLSLShaderFormats; + SDL_ShaderCross_CompileDXBCFromHLSL; + SDL_ShaderCross_CompileDXILFromHLSL; + SDL_ShaderCross_CompileSPIRVFromHLSL; + SDL_ShaderCross_ReflectGraphicsSPIRV; + SDL_ShaderCross_ReflectComputeSPIRV; + local: *; +}; diff --git a/thirdparty/SDL_shadercross/src/cli.c b/thirdparty/SDL_shadercross/src/cli.c new file mode 100644 index 000000000..a141cc562 --- /dev/null +++ b/thirdparty/SDL_shadercross/src/cli.c @@ -0,0 +1,824 @@ +/* + Simple DirectMedia Layer Shader Cross Compiler + Copyright (C) 2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#include +#include +#include +#ifdef LEAKCHECK +#include +#endif + +// We can emit HLSL and JSON as a destination, so let's redefine the shader format enum. +typedef enum ShaderCross_DestinationFormat { + SHADERFORMAT_INVALID, + SHADERFORMAT_SPIRV, + SHADERFORMAT_DXBC, + SHADERFORMAT_DXIL, + SHADERFORMAT_MSL, + SHADERFORMAT_HLSL, + SHADERFORMAT_JSON +} ShaderCross_ShaderFormat; + +void print_help(void) +{ + int column_width = 32; + SDL_Log("Usage: shadercross [options]"); + SDL_Log("Required options:\n"); + SDL_Log(" %-*s %s", column_width, "-s | --source ", "Source language format. May be inferred from the filename. Values: [SPIRV, HLSL]"); + SDL_Log(" %-*s %s", column_width, "-d | --dest ", "Destination format. May be inferred from the filename. Values: [DXBC, DXIL, MSL, SPIRV, HLSL, JSON]"); + SDL_Log(" %-*s %s", column_width, "-t | --stage ", "Shader stage. May be inferred from the filename. Values: [vertex, fragment, compute]"); + SDL_Log(" %-*s %s", column_width, "-e | --entrypoint ", "Entrypoint function name. Default: \"main\"."); + SDL_Log(" %-*s %s", column_width, "-o | --output ", "Output file."); + SDL_Log("\n"); + SDL_Log("Optional options:\n"); + SDL_Log(" %-*s %s", column_width, "-I | --include ", "HLSL include directory. Only used with HLSL source."); + SDL_Log(" %-*s %s", column_width, "-D[=]", "HLSL define. Only used with HLSL source. Can be repeated."); + SDL_Log(" %-*s %s", column_width, "", "If = is omitted the define will be treated as equal to 1."); + SDL_Log(" %-*s %s", column_width, "--msl-version ", "Target MSL version. Only used when transpiling to MSL. The default is 1.2.0."); + SDL_Log(" %-*s %s", column_width, "-c | --cull", "Allow the compiler to cull unused resource bindings. This may lead to surprising binding behavior so be careful when enabling this!"); + SDL_Log(" %-*s %s", column_width, "-g | --debug", "Generate debug information when possible. Shaders are valid only when graphics debuggers are attached."); + SDL_Log(" %-*s %s", column_width, "-p | --pssl", "Generate PSSL-compatible shader. Destination format should be HLSL."); +} + +static const char* io_var_type_to_string(SDL_ShaderCross_IOVarType io_var_type, Uint32 vector_size) +{ + switch (io_var_type) { + case SDL_SHADERCROSS_IOVAR_TYPE_INT8: + switch (vector_size) { + case 1: return "byte"; + case 2: return "byte2"; + case 3: return "byte3"; + case 4: return "byte4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_UINT8: + switch (vector_size) { + case 1: return "ubyte"; + case 2: return "ubyte2"; + case 3: return "ubyte3"; + case 4: return "ubyte4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_INT16: + switch (vector_size) { + case 1: return "short"; + case 2: return "short2"; + case 3: return "short3"; + case 4: return "short4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_UINT16: + switch (vector_size) { + case 1: return "ushort"; + case 2: return "ushort2"; + case 3: return "ushort3"; + case 4: return "ushort4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_INT32: + switch (vector_size) { + case 1: return "int"; + case 2: return "int2"; + case 3: return "int3"; + case 4: return "int4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_UINT32: + switch (vector_size) { + case 1: return "uint"; + case 2: return "uint2"; + case 3: return "uint3"; + case 4: return "uint4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_INT64: + switch (vector_size) { + case 1: return "long"; + case 2: return "long2"; + case 3: return "long3"; + case 4: return "long4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_UINT64: + switch (vector_size) { + case 1: return "ulong"; + case 2: return "ulong2"; + case 3: return "ulong3"; + case 4: return "ulong4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_FLOAT16: + switch (vector_size) { + case 1: return "half"; + case 2: return "half2"; + case 3: return "half3"; + case 4: return "half4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_FLOAT32: + switch (vector_size) { + case 1: return "float"; + case 2: return "float2"; + case 3: return "float3"; + case 4: return "float4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_FLOAT64: + switch (vector_size) { + case 1: return "double"; + case 2: return "double2"; + case 3: return "double3"; + case 4: return "double4"; + default: break; + } + break; + case SDL_SHADERCROSS_IOVAR_TYPE_UNKNOWN: + default: break; + } + + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown IO variable type: vector_type=%u vector_size=%u", io_var_type, vector_size); + return "unknown"; +} + +void write_graphics_reflect_json(SDL_IOStream *outputIO, SDL_ShaderCross_GraphicsShaderMetadata *info) +{ + SDL_IOprintf( + outputIO, + "{ \"samplers\": %u, \"storage_textures\": %u, \"storage_buffers\": %u, \"uniform_buffers\": %u, ", + info->resource_info.num_samplers, + info->resource_info.num_storage_textures, + info->resource_info.num_storage_buffers, + info->resource_info.num_uniform_buffers + ); + + SDL_IOprintf(outputIO, "\"inputs\": ["); + for (Uint32 i = 0; i < info->num_inputs; i++) { + const SDL_ShaderCross_IOVarMetadata* input = &info->inputs[i]; + SDL_IOprintf(outputIO, "{ \"name\": \"%s\", \"type\": \"%s\", \"location\": %u }%s", + input->name, + io_var_type_to_string(input->vector_type, input->vector_size), + input->location, + i + 1 < info->num_inputs ? ", " : "" + ); + } + SDL_IOprintf(outputIO, "], "); + + SDL_IOprintf(outputIO, "\"outputs\": ["); + for (Uint32 i = 0; i < info->num_outputs; i++) { + const SDL_ShaderCross_IOVarMetadata* output = &info->outputs[i]; + SDL_IOprintf(outputIO, "{ \"name\": \"%s\", \"type\": \"%s\", \"location\": %u }%s", + output->name, + io_var_type_to_string(output->vector_type, output->vector_size), + output->location, + i + 1 < info->num_outputs ? ", " : "" + ); + } + SDL_IOprintf(outputIO, "] }\n"); +} + +void write_compute_reflect_json(SDL_IOStream *outputIO, SDL_ShaderCross_ComputePipelineMetadata *info) +{ + SDL_IOprintf( + outputIO, + "{ \"samplers\": %u, \"readonly_storage_textures\": %u, \"readonly_storage_buffers\": %u, \"readwrite_storage_textures\": %u, \"readwrite_storage_buffers\": %u, \"uniform_buffers\": %u, \"threadcount_x\": %u, \"threadcount_y\": %u, \"threadcount_z\": %u }\n", + info->num_samplers, + info->num_readonly_storage_textures, + info->num_readonly_storage_buffers, + info->num_readwrite_storage_textures, + info->num_readwrite_storage_buffers, + info->num_uniform_buffers, + info->threadcount_x, + info->threadcount_y, + info->threadcount_z + ); +} + +int main(int argc, char *argv[]) +{ + bool sourceValid = false; + bool destinationValid = false; + bool stageValid = false; + + bool spirvSource = false; + ShaderCross_ShaderFormat destinationFormat = SHADERFORMAT_INVALID; + SDL_ShaderCross_ShaderStage shaderStage = SDL_SHADERCROSS_SHADERSTAGE_VERTEX; + char *outputFilename = NULL; + char *entrypointName = "main"; + char *includeDir = NULL; + + char *filename = NULL; + size_t fileSize = 0; + void *fileData = NULL; + bool accept_optionals = true; + + SDL_ShaderCross_HLSL_Define *defines = NULL; + size_t numDefines = 0; + + bool cullUnusedBindings = false; + bool enableDebug = false; + char *mslVersion = NULL; + + bool psslCompat = false; + +#ifdef LEAKCHECK + SDLTest_TrackAllocations(); +#endif + + for (int i = 1; i < argc; i += 1) { + char *arg = argv[i]; + + if (accept_optionals && arg[0] == '-') { + if (SDL_strcmp(arg, "-h") == 0 || SDL_strcmp(arg, "--help") == 0) { + print_help(); + return 0; + } else if (SDL_strcmp(arg, "-s") == 0 || SDL_strcmp(arg, "--source") == 0) { + if (i + 1 >= argc) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s requires an argument", arg); + print_help(); + return 1; + } + i += 1; + if (SDL_strcasecmp(argv[i], "spirv") == 0) { + spirvSource = true; + sourceValid = true; + } else if (SDL_strcasecmp(argv[i], "hlsl") == 0) { + spirvSource = false; + sourceValid = true; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized source input %s, source must be SPIRV or HLSL!", argv[i]); + print_help(); + return 1; + } + } else if (SDL_strcmp(arg, "-d") == 0 || SDL_strcmp(arg, "--dest") == 0) { + if (i + 1 >= argc) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s requires an argument", arg); + print_help(); + return 1; + } + i += 1; + if (SDL_strcasecmp(argv[i], "DXBC") == 0) { + destinationFormat = SHADERFORMAT_DXBC; + destinationValid = true; + } else if (SDL_strcasecmp(argv[i], "DXIL") == 0) { + destinationFormat = SHADERFORMAT_DXIL; + destinationValid = true; + } else if (SDL_strcasecmp(argv[i], "MSL") == 0) { + destinationFormat = SHADERFORMAT_MSL; + destinationValid = true; + } else if (SDL_strcasecmp(argv[i], "SPIRV") == 0) { + destinationFormat = SHADERFORMAT_SPIRV; + destinationValid = true; + } else if (SDL_strcasecmp(argv[i], "HLSL") == 0) { + destinationFormat = SHADERFORMAT_HLSL; + destinationValid = true; + } else if (SDL_strcasecmp(argv[i], "JSON") == 0) { + destinationFormat = SHADERFORMAT_JSON; + destinationValid = true; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized destination input %s, destination must be DXBC, DXIL, MSL or SPIRV!", argv[i]); + print_help(); + return 1; + } + } else if (SDL_strcmp(arg, "-t") == 0 || SDL_strcmp(arg, "--stage") == 0) { + if (i + 1 >= argc) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s requires an argument", arg); + print_help(); + return 1; + } + i += 1; + if (SDL_strcasecmp(argv[i], "vertex") == 0) { + shaderStage = SDL_SHADERCROSS_SHADERSTAGE_VERTEX; + stageValid = true; + } else if (SDL_strcasecmp(argv[i], "fragment") == 0) { + shaderStage = SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT; + stageValid = true; + } else if (SDL_strcasecmp(argv[i], "compute") == 0) { + shaderStage = SDL_SHADERCROSS_SHADERSTAGE_COMPUTE; + stageValid = true; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unrecognized shader stage input %s, must be vertex, fragment, or compute.", argv[i]); + print_help(); + return 1; + } + } else if (SDL_strcmp(arg, "-e") == 0 || SDL_strcmp(arg, "--entrypoint") == 0) { + if (i + 1 >= argc) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s requires an argument", arg); + print_help(); + return 1; + } + i += 1; + entrypointName = argv[i]; + } else if (SDL_strcmp(arg, "-I") == 0 || SDL_strcmp(arg, "--include") == 0) { + if (includeDir) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "'%s' can only be used once", arg); + print_help(); + return 1; + } + if (i + 1 >= argc) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s requires an argument", arg); + print_help(); + return 1; + } + i += 1; + includeDir = argv[i]; + } else if (SDL_strcmp(arg, "-o") == 0 || SDL_strcmp(arg, "--output") == 0) { + if (i + 1 >= argc) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s requires an argument", arg); + print_help(); + return 1; + } + i += 1; + outputFilename = argv[i]; + } else if (SDL_strncmp(argv[i], "-D", SDL_strlen("-D")) == 0) { + numDefines += 1; + defines = SDL_realloc(defines, sizeof(SDL_ShaderCross_HLSL_Define) * numDefines); + char *equalSign = SDL_strchr(argv[i], '='); + if (equalSign != NULL) { + defines[numDefines - 1].value = equalSign + 1; + size_t len = defines[numDefines - 1].value - argv[i] - 2; + defines[numDefines - 1].name = SDL_malloc(len); + SDL_utf8strlcpy(defines[numDefines - 1].name, (const char *)argv[i] + 2, len); + } else { // no '=' was found + defines[numDefines - 1].value = NULL; + size_t len = SDL_utf8strlen(argv[i]) + 1 - 2; + defines[numDefines - 1].name = SDL_malloc(len); + SDL_utf8strlcpy(defines[numDefines - 1].name, (const char *)argv[i] + 2, len); + } + } else if (SDL_strcmp(arg, "--msl-version") == 0) { + if (i + 1 >= argc) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s requires an argument", arg); + print_help(); + return 1; + } + i += 1; + mslVersion = argv[i]; + } else if (SDL_strcmp(arg, "-c") == 0 || SDL_strcmp(arg, "--cull") == 0) { + cullUnusedBindings = true; + } else if (SDL_strcmp(arg, "-g") == 0 || SDL_strcmp(arg, "--debug") == 0) { + enableDebug = true; + } else if (SDL_strcmp(arg, "-p") == 0 || SDL_strcmp(arg, "--pssl") == 0) { + psslCompat = true; + } else if (SDL_strcmp(arg, "--") == 0) { + accept_optionals = false; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s: Unknown argument: %s", argv[0], arg); + print_help(); + return 1; + } + } else if (!filename) { + filename = arg; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s: Unknown argument: %s", argv[0], arg); + print_help(); + return 1; + } + } + if (!filename) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s: missing input path", argv[0]); + print_help(); + return 1; + } + if (!outputFilename) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s: missing output path", argv[0]); + print_help(); + return 1; + } + fileData = SDL_LoadFile(filename, &fileSize); + if (fileData == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid file (%s)", SDL_GetError()); + return 1; + } + + if (!SDL_ShaderCross_Init()) + { + SDL_LogError(SDL_LOG_CATEGORY_GPU, "%s", "Failed to initialize shadercross!"); + return 1; + } + + if (!sourceValid) { + if (SDL_strstr(filename, ".spv")) { + spirvSource = true; + } else if (SDL_strstr(filename, ".hlsl")) { + spirvSource = false; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Could not infer source format!"); + print_help(); + return 1; + } + } + + if (!destinationValid) { + if (SDL_strstr(outputFilename, ".dxbc")) { + destinationFormat = SHADERFORMAT_DXBC; + } else if (SDL_strstr(outputFilename, ".dxil")) { + destinationFormat = SHADERFORMAT_DXIL; + } else if (SDL_strstr(outputFilename, ".msl")) { + destinationFormat = SHADERFORMAT_MSL; + } else if (SDL_strstr(outputFilename, ".spv")) { + destinationFormat = SHADERFORMAT_SPIRV; + } else if (SDL_strstr(outputFilename, ".hlsl")) { + destinationFormat = SHADERFORMAT_HLSL; + } else if (SDL_strstr(outputFilename, ".json")) { + destinationFormat = SHADERFORMAT_JSON; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", "Could not infer destination format!"); + print_help(); + return 1; + } + } + + if (!stageValid) { + if (SDL_strcasestr(filename, ".vert")) { + shaderStage = SDL_SHADERCROSS_SHADERSTAGE_VERTEX; + } else if (SDL_strcasestr(filename, ".frag")) { + shaderStage = SDL_SHADERCROSS_SHADERSTAGE_FRAGMENT; + } else if (SDL_strcasestr(filename, ".comp")) { + shaderStage = SDL_SHADERCROSS_SHADERSTAGE_COMPUTE; + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not infer shader stage from filename!"); + print_help(); + return 1; + } + } + + SDL_IOStream *outputIO = SDL_IOFromFile(outputFilename, "w"); + + if (outputIO == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "%s", SDL_GetError()); + return 1; + } + + size_t bytecodeSize; + int result = 0; + + // null-terminate the defines array + if (defines != NULL) { + defines = SDL_realloc(defines, sizeof(SDL_ShaderCross_HLSL_Define) * (numDefines + 1)); + defines[numDefines].name = NULL; + defines[numDefines].value = NULL; + } + + if (spirvSource) { + SDL_ShaderCross_SPIRV_Info spirvInfo; + spirvInfo.bytecode = fileData; + spirvInfo.bytecode_size = fileSize; + spirvInfo.entrypoint = entrypointName; + spirvInfo.shader_stage = shaderStage; + spirvInfo.props = SDL_CreateProperties(); + if (enableDebug) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN, true); + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, filename); + } + if (cullUnusedBindings) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN, true); + } + if (mslVersion) { + SDL_SetStringProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SPIRV_MSL_VERSION_STRING, mslVersion); + } + if (psslCompat) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SPIRV_PSSL_COMPATIBILITY_BOOLEAN, true); + } + + switch (destinationFormat) { + case SHADERFORMAT_DXBC: { + Uint8 *buffer = SDL_ShaderCross_CompileDXBCFromSPIRV( + &spirvInfo, + &bytecodeSize); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile DXBC from SPIR-V: %s", SDL_GetError()); + result = 1; + } else { + SDL_WriteIO(outputIO, buffer, bytecodeSize); + SDL_free(buffer); + } + break; + } + + case SHADERFORMAT_DXIL: { + Uint8 *buffer = SDL_ShaderCross_CompileDXILFromSPIRV( + &spirvInfo, + &bytecodeSize); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile DXIL from SPIR-V: %s", SDL_GetError()); + result = 1; + } else { + SDL_WriteIO(outputIO, buffer, bytecodeSize); + SDL_free(buffer); + } + break; + } + + case SHADERFORMAT_MSL: { + char *buffer = SDL_ShaderCross_TranspileMSLFromSPIRV( + &spirvInfo); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to transpile MSL from SPIR-V: %s", SDL_GetError()); + result = 1; + } else { + SDL_IOprintf(outputIO, "%s", buffer); + SDL_free(buffer); + } + break; + } + + case SHADERFORMAT_HLSL: { + char *buffer = SDL_ShaderCross_TranspileHLSLFromSPIRV( + &spirvInfo); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to transpile HLSL from SPIRV: %s", SDL_GetError()); + result = 1; + } else { + SDL_IOprintf(outputIO, "%s", buffer); + SDL_free(buffer); + } + break; + } + + case SHADERFORMAT_SPIRV: { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Input and output are both SPIRV. Did you mean to do that?"); + result = 1; + break; + } + + case SHADERFORMAT_JSON: { + if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) { + SDL_ShaderCross_ComputePipelineMetadata *info = SDL_ShaderCross_ReflectComputeSPIRV( + fileData, + fileSize, + 0); + if (info) { + write_compute_reflect_json(outputIO, info); + SDL_free(info); + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError()); + result = 1; + } + } else { + SDL_ShaderCross_GraphicsShaderMetadata *info = SDL_ShaderCross_ReflectGraphicsSPIRV( + fileData, + fileSize, + 0); + if (info) { + write_graphics_reflect_json(outputIO, info); + SDL_free(info); + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError()); + result = 1; + } + } + break; + } + + case SHADERFORMAT_INVALID: { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Destination format not provided!"); + result = 1; + break; + } + } + + SDL_DestroyProperties(spirvInfo.props); + } else { + SDL_ShaderCross_HLSL_Info hlslInfo; + hlslInfo.source = fileData; + hlslInfo.entrypoint = entrypointName; + hlslInfo.include_dir = includeDir; + hlslInfo.defines = defines; + hlslInfo.shader_stage = shaderStage; + hlslInfo.props = SDL_CreateProperties(); + + if (enableDebug) { + SDL_SetBooleanProperty(hlslInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN, true); + SDL_SetStringProperty(hlslInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, filename); + } + + if (cullUnusedBindings) { + SDL_SetBooleanProperty(hlslInfo.props, SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN, true); + } + + switch (destinationFormat) { + case SHADERFORMAT_DXBC: { + Uint8 *buffer = SDL_ShaderCross_CompileDXBCFromHLSL( + &hlslInfo, + &bytecodeSize); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile DXBC from HLSL: %s", SDL_GetError()); + result = 1; + } else { + SDL_WriteIO(outputIO, buffer, bytecodeSize); + SDL_free(buffer); + } + break; + } + + case SHADERFORMAT_DXIL: { + Uint8 *buffer = SDL_ShaderCross_CompileDXILFromHLSL( + &hlslInfo, + &bytecodeSize); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile DXIL from HLSL: %s", SDL_GetError()); + result = 1; + } else { + SDL_WriteIO(outputIO, buffer, bytecodeSize); + SDL_free(buffer); + } + break; + } + + // TODO: Should we have TranspileMSLFromHLSL? + case SHADERFORMAT_MSL: { + void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL( + &hlslInfo, + &bytecodeSize); + if (spirv == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to transpile MSL from HLSL: %s", SDL_GetError()); + result = 1; + } else { + SDL_ShaderCross_SPIRV_Info spirvInfo; + spirvInfo.bytecode = spirv; + spirvInfo.bytecode_size = bytecodeSize; + spirvInfo.entrypoint = entrypointName; + spirvInfo.shader_stage = shaderStage; + spirvInfo.props = SDL_CreateProperties(); + + if (enableDebug) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN, true); + SDL_SetStringProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, filename); + } + if (cullUnusedBindings) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN, true); + } + if (mslVersion) { + SDL_SetStringProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SPIRV_MSL_VERSION_STRING, mslVersion); + } + + char *buffer = SDL_ShaderCross_TranspileMSLFromSPIRV( + &spirvInfo); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to transpile MSL from HLSL: %s", SDL_GetError()); + result = 1; + } else { + SDL_IOprintf(outputIO, "%s", buffer); + SDL_free(spirv); + SDL_free(buffer); + } + SDL_DestroyProperties(spirvInfo.props); + } + break; + } + + case SHADERFORMAT_SPIRV: { + Uint8 *buffer = SDL_ShaderCross_CompileSPIRVFromHLSL( + &hlslInfo, + &bytecodeSize); + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile SPIR-V From HLSL: %s", SDL_GetError()); + result = 1; + } else { + SDL_WriteIO(outputIO, buffer, bytecodeSize); + SDL_free(buffer); + } + break; + } + + case SHADERFORMAT_HLSL: { + void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL( + &hlslInfo, + &bytecodeSize); + + if (spirv == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile HLSL to SPIRV: %s", SDL_GetError()); + result = 1; + break; + } + + SDL_ShaderCross_SPIRV_Info spirvInfo; + spirvInfo.bytecode = spirv; + spirvInfo.bytecode_size = bytecodeSize; + spirvInfo.entrypoint = entrypointName; + spirvInfo.shader_stage = shaderStage; + spirvInfo.props = SDL_CreateProperties(); + + if (enableDebug) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_ENABLE_BOOLEAN, true); + SDL_SetStringProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_DEBUG_NAME_STRING, filename); + } + if (cullUnusedBindings) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SHADER_CULL_UNUSED_BINDINGS_BOOLEAN, true); + } + if (psslCompat) { + SDL_SetBooleanProperty(spirvInfo.props, SDL_SHADERCROSS_PROP_SPIRV_PSSL_COMPATIBILITY_BOOLEAN, true); + } + + char *buffer = SDL_ShaderCross_TranspileHLSLFromSPIRV( + &spirvInfo); + + if (buffer == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to transpile HLSL from SPIRV: %s", SDL_GetError()); + result = 1; + break; + } + + SDL_IOprintf(outputIO, "%s", buffer); + SDL_free(spirv); + SDL_free(buffer); + SDL_DestroyProperties(spirvInfo.props); + break; + } + + case SHADERFORMAT_JSON: { + void *spirv = SDL_ShaderCross_CompileSPIRVFromHLSL( + &hlslInfo, + &bytecodeSize); + + if (spirv == NULL) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile HLSL to SPIRV: %s", SDL_GetError()); + result = 1; + break; + } + + if (shaderStage == SDL_SHADERCROSS_SHADERSTAGE_COMPUTE) { + SDL_ShaderCross_ComputePipelineMetadata *info = SDL_ShaderCross_ReflectComputeSPIRV( + spirv, + bytecodeSize, + 0); + SDL_free(spirv); + + if (info) { + write_compute_reflect_json(outputIO, info); + SDL_free(info); + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError()); + result = 1; + } + } else { + SDL_ShaderCross_GraphicsShaderMetadata *info = SDL_ShaderCross_ReflectGraphicsSPIRV( + spirv, + bytecodeSize, + 0); + SDL_free(spirv); + + if (info) { + write_graphics_reflect_json(outputIO, info); + SDL_free(info); + } else { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to reflect SPIRV: %s", SDL_GetError()); + result = 1; + } + } + + break; + } + + case SHADERFORMAT_INVALID: { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Destination format not provided!"); + result = 1; + break; + } + } + + SDL_DestroyProperties(hlslInfo.props); + } + + SDL_CloseIO(outputIO); + SDL_free(fileData); + for (Uint32 i = 0; i < numDefines; i += 1) { + SDL_free(defines[i].name); + } + SDL_free(defines); + SDL_ShaderCross_Quit(); + SDL_Quit(); + +#ifdef LEAKCHECK + SDLTest_LogAllocations(); +#endif + + return result; +} diff --git a/thirdparty/SDL_shadercross/src/version.rc b/thirdparty/SDL_shadercross/src/version.rc new file mode 100644 index 000000000..50c4dcebf --- /dev/null +++ b/thirdparty/SDL_shadercross/src/version.rc @@ -0,0 +1,38 @@ + +#include "winresrc.h" + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,0,0,0 + PRODUCTVERSION 3,0,0,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0x0L + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "SDL_shadercross\0" + VALUE "FileVersion", "3, 0, 0, 0\0" + VALUE "InternalName", "SDL_shadercross\0" + VALUE "LegalCopyright", "Copyright (C) 2024 Sam Lantinga\0" + VALUE "OriginalFilename", "SDL3_shadercross.dll\0" + VALUE "ProductName", "Simple DirectMedia Layer\0" + VALUE "ProductVersion", "3, 0, 0, 0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/thirdparty/SPIRV-Cross/CMakeLists.txt b/thirdparty/SPIRV-Cross/CMakeLists.txt new file mode 100644 index 000000000..8e5129c8b --- /dev/null +++ b/thirdparty/SPIRV-Cross/CMakeLists.txt @@ -0,0 +1,659 @@ +# Copyright 2016-2021 Google Inc. +# SPDX-License-Identifier: Apache-2.0 OR MIT +# +# 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. + +# +# At your option, you may choose to accept this material under either: +# 1. The Apache License, Version 2.0, found at , or +# 2. The MIT License, found at . +# + +cmake_minimum_required(VERSION 3.10) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Avoid a warning if parent project sets VERSION in project(). +if (${CMAKE_VERSION} VERSION_GREATER "3.0.1") + cmake_policy(SET CMP0048 NEW) +endif() + +# Avoid error on Android NDK 27+'s CMake files +cmake_policy(SET CMP0057 NEW) + +project(SPIRV-Cross LANGUAGES CXX C) +enable_testing() + +include(GNUInstallDirs) + +option(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS "Instead of throwing exceptions assert" OFF) +option(SPIRV_CROSS_SHARED "Build the C API as a single shared library." OFF) +option(SPIRV_CROSS_STATIC "Build the C and C++ API as static libraries." ON) +option(SPIRV_CROSS_CLI "Build the CLI binary. Requires SPIRV_CROSS_STATIC." ON) +option(SPIRV_CROSS_ENABLE_TESTS "Enable SPIRV-Cross tests." ON) + +option(SPIRV_CROSS_ENABLE_GLSL "Enable GLSL support." ON) +option(SPIRV_CROSS_ENABLE_HLSL "Enable HLSL target support." ON) +option(SPIRV_CROSS_ENABLE_MSL "Enable MSL target support." ON) +option(SPIRV_CROSS_ENABLE_CPP "Enable C++ target support." ON) +option(SPIRV_CROSS_ENABLE_REFLECT "Enable JSON reflection target support." ON) +option(SPIRV_CROSS_ENABLE_C_API "Enable C API wrapper support in static library." ON) +option(SPIRV_CROSS_ENABLE_UTIL "Enable util module support." ON) + +option(SPIRV_CROSS_SANITIZE_ADDRESS "Sanitize address" OFF) +option(SPIRV_CROSS_SANITIZE_MEMORY "Sanitize memory" OFF) +option(SPIRV_CROSS_SANITIZE_THREADS "Sanitize threads" OFF) +option(SPIRV_CROSS_SANITIZE_UNDEFINED "Sanitize undefined" OFF) + +option(SPIRV_CROSS_NAMESPACE_OVERRIDE "" "Override the namespace used in the C++ API.") +option(SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE "" "Override the namespace used by spirv.hpp (to workaround conflicts).") +option(SPIRV_CROSS_FORCE_STL_TYPES "Force use of STL types instead of STL replacements in certain places. Might reduce performance." OFF) + +option(SPIRV_CROSS_SKIP_INSTALL "Skips installation targets." OFF) + +option(SPIRV_CROSS_WERROR "Fail build on warnings." OFF) +option(SPIRV_CROSS_MISC_WARNINGS "Misc warnings useful for Travis runs." OFF) + +option(SPIRV_CROSS_FORCE_PIC "Force position-independent code for all targets." OFF) + +if(${CMAKE_GENERATOR} MATCHES "Makefile") + if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + message(FATAL_ERROR "Build out of tree to avoid overwriting Makefile") + endif() +endif() + +set(spirv-compiler-options "") +set(spirv-compiler-defines "") +set(spirv-cross-link-flags "") + +message(STATUS "SPIRV-Cross: Finding Git version for SPIRV-Cross.") +set(spirv-cross-build-version "unknown") +find_package(Git) +if (GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} describe --always --tags --dirty=+ + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE spirv-cross-build-version + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + message(STATUS "SPIRV-Cross: Git hash: ${spirv-cross-build-version}") +else() + message(STATUS "SPIRV-Cross: Git not found, using unknown build version.") +endif() + +string(TIMESTAMP spirv-cross-timestamp) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/gitversion.in.h ${CMAKE_CURRENT_BINARY_DIR}/gitversion.h @ONLY) + +if (SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + if (NOT MSVC) + set(spirv-compiler-options ${spirv-compiler-options} -fno-exceptions) + endif() +endif() + +if (SPIRV_CROSS_FORCE_STL_TYPES) + set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_FORCE_STL_TYPES) +endif() + +if (WIN32) + set(CMAKE_DEBUG_POSTFIX "d") +endif() + +if (CMAKE_COMPILER_IS_GNUCXX OR ((${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") AND NOT MSVC)) + set(spirv-compiler-options ${spirv-compiler-options} -Wall -Wextra -Wshadow -Wno-deprecated-declarations) + if (SPIRV_CROSS_MISC_WARNINGS) + if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") + set(spirv-compiler-options ${spirv-compiler-options} -Wshorten-64-to-32) + endif() + endif() + if (SPIRV_CROSS_WERROR) + set(spirv-compiler-options ${spirv-compiler-options} -Werror) + endif() + + if (SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) + set(spirv-compiler-options ${spirv-compiler-options} -fno-exceptions) + endif() + + if (SPIRV_CROSS_SANITIZE_ADDRESS) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=address) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=address") + endif() + + if (SPIRV_CROSS_SANITIZE_UNDEFINED) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=undefined) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=undefined") + endif() + + if (SPIRV_CROSS_SANITIZE_MEMORY) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=memory) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=memory") + endif() + + if (SPIRV_CROSS_SANITIZE_THREADS) + set(spirv-compiler-options ${spirv-compiler-options} -fsanitize=thread) + set(spirv-cross-link-flags "${spirv-cross-link-flags} -fsanitize=thread") + endif() +elseif (MSVC) + # AppVeyor spuriously fails in debug build on older MSVC without /bigobj. + set(spirv-compiler-options ${spirv-compiler-options} /wd4267 /wd4996 $<$:/bigobj>) +endif() + +macro(extract_headers out_abs file_list) + set(${out_abs}) # absolute paths + foreach(_a ${file_list}) + # get_filename_component only returns the longest extension, so use a regex + string(REGEX REPLACE ".*\\.(h|hpp)" "\\1" ext ${_a}) + + # For shared library, we are only interested in the C header. + if (SPIRV_CROSS_STATIC) + if(("${ext}" STREQUAL "h") OR ("${ext}" STREQUAL "hpp")) + list(APPEND ${out_abs} "${_a}") + endif() + else() + if("${ext}" STREQUAL "h") + list(APPEND ${out_abs} "${_a}") + endif() + endif() + endforeach() +endmacro() + +macro(spirv_cross_add_library name config_name library_type) + add_library(${name} ${library_type} ${ARGN}) + extract_headers(hdrs "${ARGN}") + target_include_directories(${name} PUBLIC + $ + $) + set_target_properties(${name} PROPERTIES + PUBLIC_HEADERS "${hdrs}") + if (SPIRV_CROSS_FORCE_PIC) + set_target_properties(${name} PROPERTIES POSITION_INDEPENDENT_CODE ON) + endif() + target_compile_options(${name} PRIVATE ${spirv-compiler-options}) + target_compile_definitions(${name} PRIVATE ${spirv-compiler-defines}) + if (SPIRV_CROSS_NAMESPACE_OVERRIDE) + if (${library_type} MATCHES "STATIC") + target_compile_definitions(${name} PUBLIC SPIRV_CROSS_NAMESPACE_OVERRIDE=${SPIRV_CROSS_NAMESPACE_OVERRIDE}) + else() + target_compile_definitions(${name} PRIVATE SPIRV_CROSS_NAMESPACE_OVERRIDE=${SPIRV_CROSS_NAMESPACE_OVERRIDE}) + endif() + endif() + + if (SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE) + if (${library_type} MATCHES "STATIC") + target_compile_definitions(${name} PUBLIC SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE=${SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE}) + else() + target_compile_definitions(${name} PRIVATE SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE=${SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE}) + endif() + endif() + + if (NOT SPIRV_CROSS_SKIP_INSTALL) + install(TARGETS ${name} + EXPORT ${config_name}Config + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/spirv_cross) + install(FILES ${hdrs} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/spirv_cross) + install(EXPORT ${config_name}Config DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${config_name}/cmake) + export(TARGETS ${name} FILE ${config_name}Config.cmake) + endif() +endmacro() + +set(spirv-cross-core-sources + ${CMAKE_CURRENT_SOURCE_DIR}/GLSL.std.450.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_common.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_containers.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_error_handling.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_parser.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_parser.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_parsed_ir.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_parsed_ir.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cfg.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cfg.cpp) + +set(spirv-cross-c-sources + spirv.h + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_c.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_c.h) + +set(spirv-cross-glsl-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_glsl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_glsl.hpp) + +set(spirv-cross-cpp-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cpp.hpp) + +set(spirv-cross-msl-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_msl.hpp) + +set(spirv-cross-hlsl-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_hlsl.hpp) + +set(spirv-cross-reflect-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_reflect.hpp) + +set(spirv-cross-util-sources + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross_util.hpp) + +set(spirv-cross-abi-major 0) +set(spirv-cross-abi-minor 68) +set(spirv-cross-abi-patch 0) +set(SPIRV_CROSS_VERSION ${spirv-cross-abi-major}.${spirv-cross-abi-minor}.${spirv-cross-abi-patch}) + +if (SPIRV_CROSS_STATIC) + if (NOT SPIRV_CROSS_SKIP_INSTALL) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/pkg-config/spirv-cross-c.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c.pc @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + endif() + + spirv_cross_add_library(spirv-cross-core spirv_cross_core STATIC + ${spirv-cross-core-sources}) + + if (SPIRV_CROSS_ENABLE_GLSL) + spirv_cross_add_library(spirv-cross-glsl spirv_cross_glsl STATIC + ${spirv-cross-glsl-sources}) + target_link_libraries(spirv-cross-glsl PRIVATE spirv-cross-core) + endif() + + if (SPIRV_CROSS_ENABLE_CPP) + spirv_cross_add_library(spirv-cross-cpp spirv_cross_cpp STATIC + ${spirv-cross-cpp-sources}) + + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-cpp PRIVATE spirv-cross-glsl) + else() + message(FATAL_ERROR "Must enable GLSL support to enable C++ support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_REFLECT) + if (SPIRV_CROSS_ENABLE_GLSL) + spirv_cross_add_library(spirv-cross-reflect spirv_cross_reflect STATIC + ${spirv-cross-reflect-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable JSON reflection support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_MSL) + spirv_cross_add_library(spirv-cross-msl spirv_cross_msl STATIC + ${spirv-cross-msl-sources}) + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-msl PRIVATE spirv-cross-glsl) + else() + message(FATAL_ERROR "Must enable GLSL support to enable MSL support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_HLSL) + spirv_cross_add_library(spirv-cross-hlsl spirv_cross_hlsl STATIC + ${spirv-cross-hlsl-sources}) + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-hlsl PRIVATE spirv-cross-glsl) + else() + message(FATAL_ERROR "Must enable GLSL support to enable HLSL support.") + endif() + endif() + + if (SPIRV_CROSS_ENABLE_UTIL) + spirv_cross_add_library(spirv-cross-util spirv_cross_util STATIC + ${spirv-cross-util-sources}) + target_link_libraries(spirv-cross-util PRIVATE spirv-cross-core) + endif() + + if (SPIRV_CROSS_ENABLE_C_API) + spirv_cross_add_library(spirv-cross-c spirv_cross_c STATIC + ${spirv-cross-c-sources}) + target_include_directories(spirv-cross-c PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_compile_definitions(spirv-cross-c PRIVATE HAVE_SPIRV_CROSS_GIT_VERSION) + + if (SPIRV_CROSS_ENABLE_GLSL) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-glsl) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_GLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_HLSL) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-hlsl) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_HLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_MSL) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-msl) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_MSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_CPP) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-cpp) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_CPP=1) + endif() + + if (SPIRV_CROSS_ENABLE_REFLECT) + target_link_libraries(spirv-cross-c PRIVATE spirv-cross-reflect) + target_compile_definitions(spirv-cross-c PRIVATE SPIRV_CROSS_C_API_REFLECT=1) + endif() + endif() +endif() + +if (SPIRV_CROSS_SHARED) + if (NOT SPIRV_CROSS_SKIP_INSTALL) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/pkg-config/spirv-cross-c-shared.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c-shared.pc @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/spirv-cross-c-shared.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + endif() + + spirv_cross_add_library(spirv-cross-c-shared spirv_cross_c_shared SHARED + ${spirv-cross-core-sources} + ${spirv-cross-c-sources}) + + target_include_directories(spirv-cross-c-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_compile_definitions(spirv-cross-c-shared PRIVATE HAVE_SPIRV_CROSS_GIT_VERSION) + + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-glsl-sources}) + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_GLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_HLSL) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-hlsl-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable HLSL support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_HLSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_MSL) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-msl-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable MSL support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_MSL=1) + endif() + + if (SPIRV_CROSS_ENABLE_CPP) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-cpp-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable C++ support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_CPP=1) + endif() + + if (SPIRV_CROSS_ENABLE_REFLECT) + if (SPIRV_CROSS_ENABLE_GLSL) + target_sources(spirv-cross-c-shared PRIVATE ${spirv-cross-reflect-sources}) + else() + message(FATAL_ERROR "Must enable GLSL support to enable JSON reflection support.") + endif() + target_compile_definitions(spirv-cross-c-shared PRIVATE SPIRV_CROSS_C_API_REFLECT=1) + endif() + + if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")) + # Only export the C API. + target_compile_options(spirv-cross-c-shared PRIVATE -fvisibility=hidden) + if (NOT APPLE) + set_target_properties(spirv-cross-c-shared PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + endif() + endif() + + target_compile_definitions(spirv-cross-c-shared PRIVATE SPVC_EXPORT_SYMBOLS) + + set_target_properties(spirv-cross-c-shared PROPERTIES + VERSION ${SPIRV_CROSS_VERSION} + SOVERSION ${spirv-cross-abi-major}) +endif() + +if (SPIRV_CROSS_CLI) + if (NOT SPIRV_CROSS_ENABLE_GLSL) + message(FATAL_ERROR "Must enable GLSL if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_HLSL) + message(FATAL_ERROR "Must enable HLSL if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_MSL) + message(FATAL_ERROR "Must enable MSL if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_CPP) + message(FATAL_ERROR "Must enable C++ if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_REFLECT) + message(FATAL_ERROR "Must enable reflection if building CLI.") + endif() + + if (NOT SPIRV_CROSS_ENABLE_UTIL) + message(FATAL_ERROR "Must enable utils if building CLI.") + endif() + + if (NOT SPIRV_CROSS_STATIC) + message(FATAL_ERROR "Must build static libraries if building CLI.") + endif() + add_executable(spirv-cross main.cpp) + target_compile_options(spirv-cross PRIVATE ${spirv-compiler-options}) + target_include_directories(spirv-cross PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_compile_definitions(spirv-cross PRIVATE ${spirv-compiler-defines} HAVE_SPIRV_CROSS_GIT_VERSION) + set_target_properties(spirv-cross PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + if (NOT SPIRV_CROSS_SKIP_INSTALL) + install(TARGETS spirv-cross DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + target_link_libraries(spirv-cross PRIVATE + spirv-cross-glsl + spirv-cross-hlsl + spirv-cross-cpp + spirv-cross-reflect + spirv-cross-msl + spirv-cross-util + spirv-cross-core) + + if (SPIRV_CROSS_ENABLE_TESTS) + # Set up tests, using only the simplest modes of the test_shaders + # script. You have to invoke the script manually to: + # - Update the reference files + # - Get cycle counts from malisc + # - Keep failing outputs + if (${CMAKE_VERSION} VERSION_GREATER "3.12") + find_package(Python3) + if (${PYTHON3_FOUND}) + set(PYTHONINTERP_FOUND ON) + set(PYTHON_VERSION_MAJOR 3) + set(PYTHON_EXECUTABLE ${Python3_EXECUTABLE}) + else() + set(PYTHONINTERP_FOUND OFF) + endif() + else() + find_package(PythonInterp) + endif() + + find_program(spirv-cross-glslang NAMES glslangValidator + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/glslang-build/output/bin + NO_DEFAULT_PATH) + find_program(spirv-cross-spirv-as NAMES spirv-as + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin + NO_DEFAULT_PATH) + find_program(spirv-cross-spirv-val NAMES spirv-val + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin + NO_DEFAULT_PATH) + find_program(spirv-cross-spirv-opt NAMES spirv-opt + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/external/spirv-tools-build/output/bin + NO_DEFAULT_PATH) + + if ((${spirv-cross-glslang} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-as} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-val} MATCHES "NOTFOUND") OR (${spirv-cross-spirv-opt} MATCHES "NOTFOUND")) + set(SPIRV_CROSS_ENABLE_TESTS OFF) + message("SPIRV-Cross: Testing will be disabled for SPIRV-Cross. Could not find glslang or SPIRV-Tools build under external/. To enable testing, run ./checkout_glslang_spirv_tools.sh and ./build_glslang_spirv_tools.sh first.") + else() + set(SPIRV_CROSS_ENABLE_TESTS ON) + message("SPIRV-Cross: Found glslang and SPIRV-Tools. Enabling test suite.") + message("SPIRV-Cross: Found glslangValidator in: ${spirv-cross-glslang}.") + message("SPIRV-Cross: Found spirv-as in: ${spirv-cross-spirv-as}.") + message("SPIRV-Cross: Found spirv-val in: ${spirv-cross-spirv-val}.") + message("SPIRV-Cross: Found spirv-opt in: ${spirv-cross-spirv-opt}.") + endif() + + set(spirv-cross-externals + --glslang "${spirv-cross-glslang}" + --spirv-as "${spirv-cross-spirv-as}" + --spirv-opt "${spirv-cross-spirv-opt}" + --spirv-val "${spirv-cross-spirv-val}") + + if (${PYTHONINTERP_FOUND} AND SPIRV_CROSS_ENABLE_TESTS) + if (${PYTHON_VERSION_MAJOR} GREATER 2) + add_executable(spirv-cross-c-api-test tests-other/c_api_test.c) + target_link_libraries(spirv-cross-c-api-test spirv-cross-c) + set_target_properties(spirv-cross-c-api-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-small-vector-test tests-other/small_vector.cpp) + target_link_libraries(spirv-cross-small-vector-test spirv-cross-core) + set_target_properties(spirv-cross-small-vector-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-msl-constexpr-test tests-other/msl_constexpr_test.cpp) + target_link_libraries(spirv-cross-msl-constexpr-test spirv-cross-c) + set_target_properties(spirv-cross-msl-constexpr-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-msl-resource-binding-test tests-other/msl_resource_bindings.cpp) + target_link_libraries(spirv-cross-msl-resource-binding-test spirv-cross-c) + set_target_properties(spirv-cross-msl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-hlsl-resource-binding-test tests-other/hlsl_resource_bindings.cpp) + target_link_libraries(spirv-cross-hlsl-resource-binding-test spirv-cross-c) + set_target_properties(spirv-cross-hlsl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-msl-ycbcr-conversion-test tests-other/msl_ycbcr_conversion_test.cpp) + target_link_libraries(spirv-cross-msl-ycbcr-conversion-test spirv-cross-c) + set_target_properties(spirv-cross-msl-ycbcr-conversion-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-typed-id-test tests-other/typed_id_test.cpp) + target_link_libraries(spirv-cross-typed-id-test spirv-cross-core) + set_target_properties(spirv-cross-typed-id-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-debug-lines-test tests-other/debug-lines.cpp) + target_link_libraries(spirv-cross-debug-lines-test spirv-cross-core) + set_target_properties(spirv-cross-debug-lines-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + add_executable(spirv-cross-debug-info-test tests-other/debug-info.cpp) + target_link_libraries(spirv-cross-debug-info-test spirv-cross-core) + set_target_properties(spirv-cross-debug-info-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + + if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")) + target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra) + endif() + add_test(NAME spirv-cross-c-api-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/c_api_test.spv + ${spirv-cross-abi-major} + ${spirv-cross-abi-minor} + ${spirv-cross-abi-patch}) + add_test(NAME spirv-cross-small-vector-test + COMMAND $) + add_test(NAME spirv-cross-msl-constexpr-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_constexpr_test.spv) + add_test(NAME spirv-cross-msl-resource-binding-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_resource_binding.spv) + add_test(NAME spirv-cross-hlsl-resource-binding-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/hlsl_resource_binding.spv) + add_test(NAME spirv-cross-msl-ycbcr-conversion-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test.spv) + add_test(NAME spirv-cross-msl-ycbcr-conversion-test-2 + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test_2.spv) + add_test(NAME spirv-cross-typed-id-test + COMMAND $) + add_test(NAME spirv-cross-debug-lines-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/debug-lines.spv) + add_test(NAME spirv-cross-debug-info-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/debug-info.spv) + add_test(NAME spirv-cross-test + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-no-opt + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-metal + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-metal-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl-no-opt + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-hlsl + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-hlsl-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl-no-opt + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-metal-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --metal --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-msl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-hlsl-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --hlsl --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-hlsl + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-reflection + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --reflect --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-reflection + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-ue4 + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --msl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-ue4 + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-ue4-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --msl --opt --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-ue4 + WORKING_DIRECTORY $) + add_test(NAME spirv-cross-test-ue4-no-opt + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --msl --parallel + ${spirv-cross-externals} + ${CMAKE_CURRENT_SOURCE_DIR}/shaders-ue4-no-opt + WORKING_DIRECTORY $) + endif() + elseif(NOT ${PYTHONINTERP_FOUND}) + message(WARNING "SPIRV-Cross: Testing disabled. Could not find python3. If you have python3 installed try running " + "cmake with -DPYTHON_EXECUTABLE:FILEPATH=/path/to/python3 to help it find the executable") + endif() + endif() +endif() diff --git a/thirdparty/SPIRV-Cross/GLSL.std.450.h b/thirdparty/SPIRV-Cross/GLSL.std.450.h new file mode 100644 index 000000000..2686fc4ea --- /dev/null +++ b/thirdparty/SPIRV-Cross/GLSL.std.450.h @@ -0,0 +1,114 @@ +/* + * Copyright 2014-2016,2021 The Khronos Group, Inc. + * SPDX-License-Identifier: MIT + * + * MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS + * STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND + * HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +*/ + +#ifndef GLSLstd450_H +#define GLSLstd450_H + +static const int GLSLstd450Version = 100; +static const int GLSLstd450Revision = 3; + +enum GLSLstd450 { + GLSLstd450Bad = 0, // Don't use + + GLSLstd450Round = 1, + GLSLstd450RoundEven = 2, + GLSLstd450Trunc = 3, + GLSLstd450FAbs = 4, + GLSLstd450SAbs = 5, + GLSLstd450FSign = 6, + GLSLstd450SSign = 7, + GLSLstd450Floor = 8, + GLSLstd450Ceil = 9, + GLSLstd450Fract = 10, + + GLSLstd450Radians = 11, + GLSLstd450Degrees = 12, + GLSLstd450Sin = 13, + GLSLstd450Cos = 14, + GLSLstd450Tan = 15, + GLSLstd450Asin = 16, + GLSLstd450Acos = 17, + GLSLstd450Atan = 18, + GLSLstd450Sinh = 19, + GLSLstd450Cosh = 20, + GLSLstd450Tanh = 21, + GLSLstd450Asinh = 22, + GLSLstd450Acosh = 23, + GLSLstd450Atanh = 24, + GLSLstd450Atan2 = 25, + + GLSLstd450Pow = 26, + GLSLstd450Exp = 27, + GLSLstd450Log = 28, + GLSLstd450Exp2 = 29, + GLSLstd450Log2 = 30, + GLSLstd450Sqrt = 31, + GLSLstd450InverseSqrt = 32, + + GLSLstd450Determinant = 33, + GLSLstd450MatrixInverse = 34, + + GLSLstd450Modf = 35, // second operand needs an OpVariable to write to + GLSLstd450ModfStruct = 36, // no OpVariable operand + GLSLstd450FMin = 37, + GLSLstd450UMin = 38, + GLSLstd450SMin = 39, + GLSLstd450FMax = 40, + GLSLstd450UMax = 41, + GLSLstd450SMax = 42, + GLSLstd450FClamp = 43, + GLSLstd450UClamp = 44, + GLSLstd450SClamp = 45, + GLSLstd450FMix = 46, + GLSLstd450IMix = 47, // Reserved + GLSLstd450Step = 48, + GLSLstd450SmoothStep = 49, + + GLSLstd450Fma = 50, + GLSLstd450Frexp = 51, // second operand needs an OpVariable to write to + GLSLstd450FrexpStruct = 52, // no OpVariable operand + GLSLstd450Ldexp = 53, + + GLSLstd450PackSnorm4x8 = 54, + GLSLstd450PackUnorm4x8 = 55, + GLSLstd450PackSnorm2x16 = 56, + GLSLstd450PackUnorm2x16 = 57, + GLSLstd450PackHalf2x16 = 58, + GLSLstd450PackDouble2x32 = 59, + GLSLstd450UnpackSnorm2x16 = 60, + GLSLstd450UnpackUnorm2x16 = 61, + GLSLstd450UnpackHalf2x16 = 62, + GLSLstd450UnpackSnorm4x8 = 63, + GLSLstd450UnpackUnorm4x8 = 64, + GLSLstd450UnpackDouble2x32 = 65, + + GLSLstd450Length = 66, + GLSLstd450Distance = 67, + GLSLstd450Cross = 68, + GLSLstd450Normalize = 69, + GLSLstd450FaceForward = 70, + GLSLstd450Reflect = 71, + GLSLstd450Refract = 72, + + GLSLstd450FindILsb = 73, + GLSLstd450FindSMsb = 74, + GLSLstd450FindUMsb = 75, + + GLSLstd450InterpolateAtCentroid = 76, + GLSLstd450InterpolateAtSample = 77, + GLSLstd450InterpolateAtOffset = 78, + + GLSLstd450NMin = 79, + GLSLstd450NMax = 80, + GLSLstd450NClamp = 81, + + GLSLstd450Count +}; + +#endif // #ifndef GLSLstd450_H diff --git a/thirdparty/SPIRV-Cross/LICENSE b/thirdparty/SPIRV-Cross/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/thirdparty/SPIRV-Cross/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/thirdparty/SPIRV-Cross/LICENSES/Apache-2.0.txt b/thirdparty/SPIRV-Cross/LICENSES/Apache-2.0.txt new file mode 100644 index 000000000..4ed90b952 --- /dev/null +++ b/thirdparty/SPIRV-Cross/LICENSES/Apache-2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +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. diff --git a/thirdparty/SPIRV-Cross/LICENSES/CC-BY-4.0.txt b/thirdparty/SPIRV-Cross/LICENSES/CC-BY-4.0.txt new file mode 100644 index 000000000..3f92dfc5f --- /dev/null +++ b/thirdparty/SPIRV-Cross/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,324 @@ +Creative Commons Attribution 4.0 International Creative Commons Corporation +("Creative Commons") is not a law firm and does not provide legal services +or legal advice. Distribution of Creative Commons public licenses does not +create a lawyer-client or other relationship. Creative Commons makes its licenses +and related information available on an "as-is" basis. Creative Commons gives +no warranties regarding its licenses, any material licensed under their terms +and conditions, or any related information. Creative Commons disclaims all +liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions +that creators and other rights holders may use to share original works of +authorship and other material subject to copyright and certain other rights +specified in the public license below. The following considerations are for +informational purposes only, are not exhaustive, and do not form part of our +licenses. + +Considerations for licensors: Our public licenses are intended for use by +those authorized to give the public permission to use material in ways otherwise +restricted by copyright and certain other rights. Our licenses are irrevocable. +Licensors should read and understand the terms and conditions of the license +they choose before applying it. Licensors should also secure all rights necessary +before applying our licenses so that the public can reuse the material as +expected. Licensors should clearly mark any material not subject to the license. +This includes other CC-licensed material, or material used under an exception +or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors + +Considerations for the public: By using one of our public licenses, a licensor +grants the public permission to use the licensed material under specified +terms and conditions. If the licensor's permission is not necessary for any +reason–for example, because of any applicable exception or limitation to copyright–then +that use is not regulated by the license. Our licenses grant only permissions +under copyright and certain other rights that a licensor has authority to +grant. Use of the licensed material may still be restricted for other reasons, +including because others have copyright or other rights in the material. A +licensor may make special requests, such as asking that all changes be marked +or described. Although not required by our licenses, you are encouraged to +respect those requests where reasonable. More considerations for the public +: wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution +4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to +be bound by the terms and conditions of this Creative Commons Attribution +4.0 International Public License ("Public License"). To the extent this Public +License may be interpreted as a contract, You are granted the Licensed Rights +in consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the Licensor +receives from making the Licensed Material available under these terms and +conditions. + +Section 1 – Definitions. + +a. Adapted Material means material subject to Copyright and Similar Rights +that is derived from or based upon the Licensed Material and in which the +Licensed Material is translated, altered, arranged, transformed, or otherwise +modified in a manner requiring permission under the Copyright and Similar +Rights held by the Licensor. For purposes of this Public License, where the +Licensed Material is a musical work, performance, or sound recording, Adapted +Material is always produced where the Licensed Material is synched in timed +relation with a moving image. + +b. Adapter's License means the license You apply to Your Copyright and Similar +Rights in Your contributions to Adapted Material in accordance with the terms +and conditions of this Public License. + +c. Copyright and Similar Rights means copyright and/or similar rights closely +related to copyright including, without limitation, performance, broadcast, +sound recording, and Sui Generis Database Rights, without regard to how the +rights are labeled or categorized. For purposes of this Public License, the +rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + +d. Effective Technological Measures means those measures that, in the absence +of proper authority, may not be circumvented under laws fulfilling obligations +under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, +and/or similar international agreements. + +e. Exceptions and Limitations means fair use, fair dealing, and/or any other +exception or limitation to Copyright and Similar Rights that applies to Your +use of the Licensed Material. + +f. Licensed Material means the artistic or literary work, database, or other +material to which the Licensor applied this Public License. + +g. Licensed Rights means the rights granted to You subject to the terms and +conditions of this Public License, which are limited to all Copyright and +Similar Rights that apply to Your use of the Licensed Material and that the +Licensor has authority to license. + +h. Licensor means the individual(s) or entity(ies) granting rights under this +Public License. + +i. Share means to provide material to the public by any means or process that +requires permission under the Licensed Rights, such as reproduction, public +display, public performance, distribution, dissemination, communication, or +importation, and to make material available to the public including in ways +that members of the public may access the material from a place and at a time +individually chosen by them. + +j. Sui Generis Database Rights means rights other than copyright resulting +from Directive 96/9/EC of the European Parliament and of the Council of 11 +March 1996 on the legal protection of databases, as amended and/or succeeded, +as well as other essentially equivalent rights anywhere in the world. + +k. You means the individual or entity exercising the Licensed Rights under +this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + +1. Subject to the terms and conditions of this Public License, the Licensor +hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, +irrevocable license to exercise the Licensed Rights in the Licensed Material +to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + +2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions +and Limitations apply to Your use, this Public License does not apply, and +You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + +4. Media and formats; technical modifications allowed. The Licensor authorizes +You to exercise the Licensed Rights in all media and formats whether now known +or hereafter created, and to make technical modifications necessary to do +so. The Licensor waives and/or agrees not to assert any right or authority +to forbid You from making technical modifications necessary to exercise the +Licensed Rights, including technical modifications necessary to circumvent +Effective Technological Measures. For purposes of this Public License, simply +making modifications authorized by this Section 2(a)(4) never produces Adapted +Material. + + 5. Downstream recipients. + +A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed +Material automatically receives an offer from the Licensor to exercise the +Licensed Rights under the terms and conditions of this Public License. + +B. No downstream restrictions. You may not offer or impose any additional +or different terms or conditions on, or apply any Effective Technological +Measures to, the Licensed Material if doing so restricts exercise of the Licensed +Rights by any recipient of the Licensed Material. + +6. No endorsement. Nothing in this Public License constitutes or may be construed +as permission to assert or imply that You are, or that Your use of the Licensed +Material is, connected with, or sponsored, endorsed, or granted official status +by, the Licensor or others designated to receive attribution as provided in +Section 3(a)(1)(A)(i). + + b. Other rights. + +1. Moral rights, such as the right of integrity, are not licensed under this +Public License, nor are publicity, privacy, and/or other similar personality +rights; however, to the extent possible, the Licensor waives and/or agrees +not to assert any such rights held by the Licensor to the limited extent necessary +to allow You to exercise the Licensed Rights, but not otherwise. + +2. Patent and trademark rights are not licensed under this Public License. + +3. To the extent possible, the Licensor waives any right to collect royalties +from You for the exercise of the Licensed Rights, whether directly or through +a collecting society under any voluntary or waivable statutory or compulsory +licensing scheme. In all other cases the Licensor expressly reserves any right +to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following +conditions. + + a. Attribution. + +1. If You Share the Licensed Material (including in modified form), You must: + +A. retain the following if it is supplied by the Licensor with the Licensed +Material: + +i. identification of the creator(s) of the Licensed Material and any others +designated to receive attribution, in any reasonable manner requested by the +Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + +v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + +B. indicate if You modified the Licensed Material and retain an indication +of any previous modifications; and + +C. indicate the Licensed Material is licensed under this Public License, and +include the text of, or the URI or hyperlink to, this Public License. + +2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner +based on the medium, means, and context in which You Share the Licensed Material. +For example, it may be reasonable to satisfy the conditions by providing a +URI or hyperlink to a resource that includes the required information. + +3. If requested by the Licensor, You must remove any of the information required +by Section 3(a)(1)(A) to the extent reasonably practicable. + +4. If You Share Adapted Material You produce, the Adapter's License You apply +must not prevent recipients of the Adapted Material from complying with this +Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to +Your use of the Licensed Material: + +a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, +reuse, reproduce, and Share all or a substantial portion of the contents of +the database; + +b. if You include all or a substantial portion of the database contents in +a database in which You have Sui Generis Database Rights, then the database +in which You have Sui Generis Database Rights (but not its individual contents) +is Adapted Material; and + +c. You must comply with the conditions in Section 3(a) if You Share all or +a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace +Your obligations under this Public License where the Licensed Rights include +other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +a. Unless otherwise separately undertaken by the Licensor, to the extent possible, +the Licensor offers the Licensed Material as-is and as-available, and makes +no representations or warranties of any kind concerning the Licensed Material, +whether express, implied, statutory, or other. This includes, without limitation, +warranties of title, merchantability, fitness for a particular purpose, non-infringement, +absence of latent or other defects, accuracy, or the presence or absence of +errors, whether or not known or discoverable. Where disclaimers of warranties +are not allowed in full or in part, this disclaimer may not apply to You. + +b. To the extent possible, in no event will the Licensor be liable to You +on any legal theory (including, without limitation, negligence) or otherwise +for any direct, special, indirect, incidental, consequential, punitive, exemplary, +or other losses, costs, expenses, or damages arising out of this Public License +or use of the Licensed Material, even if the Licensor has been advised of +the possibility of such losses, costs, expenses, or damages. Where a limitation +of liability is not allowed in full or in part, this limitation may not apply +to You. + +c. The disclaimer of warranties and limitation of liability provided above +shall be interpreted in a manner that, to the extent possible, most closely +approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + +a. This Public License applies for the term of the Copyright and Similar Rights +licensed here. However, if You fail to comply with this Public License, then +Your rights under this Public License terminate automatically. + +b. Where Your right to use the Licensed Material has terminated under Section +6(a), it reinstates: + +1. automatically as of the date the violation is cured, provided it is cured +within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + +c. For the avoidance of doubt, this Section 6(b) does not affect any right +the Licensor may have to seek remedies for Your violations of this Public +License. + +d. For the avoidance of doubt, the Licensor may also offer the Licensed Material +under separate terms or conditions or stop distributing the Licensed Material +at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + +a. The Licensor shall not be bound by any additional or different terms or +conditions communicated by You unless expressly agreed. + +b. Any arrangements, understandings, or agreements regarding the Licensed +Material not stated herein are separate from and independent of the terms +and conditions of this Public License. + +Section 8 – Interpretation. + +a. For the avoidance of doubt, this Public License does not, and shall not +be interpreted to, reduce, limit, restrict, or impose conditions on any use +of the Licensed Material that could lawfully be made without permission under +this Public License. + +b. To the extent possible, if any provision of this Public License is deemed +unenforceable, it shall be automatically reformed to the minimum extent necessary +to make it enforceable. If the provision cannot be reformed, it shall be severed +from this Public License without affecting the enforceability of the remaining +terms and conditions. + +c. No term or condition of this Public License will be waived and no failure +to comply consented to unless expressly agreed to by the Licensor. + +d. Nothing in this Public License constitutes or may be interpreted as a limitation +upon, or waiver of, any privileges and immunities that apply to the Licensor +or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative +Commons may elect to apply one of its public licenses to material it publishes +and in those instances will be considered the "Licensor." The text of the +Creative Commons public licenses is dedicated to the public domain under the +CC0 Public Domain Dedication. Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as otherwise +permitted by the Creative Commons policies published at creativecommons.org/policies, +Creative Commons does not authorize the use of the trademark "Creative Commons" +or any other trademark or logo of Creative Commons without its prior written +consent including, without limitation, in connection with any unauthorized +modifications to any of its public licenses or any other arrangements, understandings, +or agreements concerning use of licensed material. For the avoidance of doubt, +this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/thirdparty/SPIRV-Cross/LICENSES/LicenseRef-KhronosFreeUse.txt b/thirdparty/SPIRV-Cross/LICENSES/LicenseRef-KhronosFreeUse.txt new file mode 100644 index 000000000..430863bc9 --- /dev/null +++ b/thirdparty/SPIRV-Cross/LICENSES/LicenseRef-KhronosFreeUse.txt @@ -0,0 +1,23 @@ +Copyright (c) 2014-2020 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and/or associated documentation files (the "Materials"), +to deal in the Materials without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Materials, and to permit persons to whom the +Materials are furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Materials. + +MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +IN THE MATERIALS. diff --git a/thirdparty/SPIRV-Cross/LICENSES/MIT.txt b/thirdparty/SPIRV-Cross/LICENSES/MIT.txt new file mode 100644 index 000000000..204b93da4 --- /dev/null +++ b/thirdparty/SPIRV-Cross/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/thirdparty/SPIRV-Cross/NonSemanticShaderDebugInfo100.h b/thirdparty/SPIRV-Cross/NonSemanticShaderDebugInfo100.h new file mode 100644 index 000000000..b276b560c --- /dev/null +++ b/thirdparty/SPIRV-Cross/NonSemanticShaderDebugInfo100.h @@ -0,0 +1,171 @@ +// Copyright (c) 2018-2024 The Khronos Group Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and/or associated documentation files (the "Materials"), +// to deal in the Materials without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Materials, and to permit persons to whom the +// Materials are furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Materials. +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS +// STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND +// HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ +// +// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS +// IN THE MATERIALS. + +#ifndef SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ +#define SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + NonSemanticShaderDebugInfo100Version = 100, + NonSemanticShaderDebugInfo100Version_BitWidthPadding = 0x7fffffff +}; +enum { + NonSemanticShaderDebugInfo100Revision = 6, + NonSemanticShaderDebugInfo100Revision_BitWidthPadding = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100Instructions { + NonSemanticShaderDebugInfo100DebugInfoNone = 0, + NonSemanticShaderDebugInfo100DebugCompilationUnit = 1, + NonSemanticShaderDebugInfo100DebugTypeBasic = 2, + NonSemanticShaderDebugInfo100DebugTypePointer = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifier = 4, + NonSemanticShaderDebugInfo100DebugTypeArray = 5, + NonSemanticShaderDebugInfo100DebugTypeVector = 6, + NonSemanticShaderDebugInfo100DebugTypedef = 7, + NonSemanticShaderDebugInfo100DebugTypeFunction = 8, + NonSemanticShaderDebugInfo100DebugTypeEnum = 9, + NonSemanticShaderDebugInfo100DebugTypeComposite = 10, + NonSemanticShaderDebugInfo100DebugTypeMember = 11, + NonSemanticShaderDebugInfo100DebugTypeInheritance = 12, + NonSemanticShaderDebugInfo100DebugTypePtrToMember = 13, + NonSemanticShaderDebugInfo100DebugTypeTemplate = 14, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameter = 15, + NonSemanticShaderDebugInfo100DebugTypeTemplateTemplateParameter = 16, + NonSemanticShaderDebugInfo100DebugTypeTemplateParameterPack = 17, + NonSemanticShaderDebugInfo100DebugGlobalVariable = 18, + NonSemanticShaderDebugInfo100DebugFunctionDeclaration = 19, + NonSemanticShaderDebugInfo100DebugFunction = 20, + NonSemanticShaderDebugInfo100DebugLexicalBlock = 21, + NonSemanticShaderDebugInfo100DebugLexicalBlockDiscriminator = 22, + NonSemanticShaderDebugInfo100DebugScope = 23, + NonSemanticShaderDebugInfo100DebugNoScope = 24, + NonSemanticShaderDebugInfo100DebugInlinedAt = 25, + NonSemanticShaderDebugInfo100DebugLocalVariable = 26, + NonSemanticShaderDebugInfo100DebugInlinedVariable = 27, + NonSemanticShaderDebugInfo100DebugDeclare = 28, + NonSemanticShaderDebugInfo100DebugValue = 29, + NonSemanticShaderDebugInfo100DebugOperation = 30, + NonSemanticShaderDebugInfo100DebugExpression = 31, + NonSemanticShaderDebugInfo100DebugMacroDef = 32, + NonSemanticShaderDebugInfo100DebugMacroUndef = 33, + NonSemanticShaderDebugInfo100DebugImportedEntity = 34, + NonSemanticShaderDebugInfo100DebugSource = 35, + NonSemanticShaderDebugInfo100DebugFunctionDefinition = 101, + NonSemanticShaderDebugInfo100DebugSourceContinued = 102, + NonSemanticShaderDebugInfo100DebugLine = 103, + NonSemanticShaderDebugInfo100DebugNoLine = 104, + NonSemanticShaderDebugInfo100DebugBuildIdentifier = 105, + NonSemanticShaderDebugInfo100DebugStoragePath = 106, + NonSemanticShaderDebugInfo100DebugEntryPoint = 107, + NonSemanticShaderDebugInfo100DebugTypeMatrix = 108, + NonSemanticShaderDebugInfo100InstructionsMax = 0x7fffffff +}; + + +enum NonSemanticShaderDebugInfo100DebugInfoFlags { + NonSemanticShaderDebugInfo100None = 0x0000, + NonSemanticShaderDebugInfo100FlagIsProtected = 0x01, + NonSemanticShaderDebugInfo100FlagIsPrivate = 0x02, + NonSemanticShaderDebugInfo100FlagIsPublic = 0x03, + NonSemanticShaderDebugInfo100FlagIsLocal = 0x04, + NonSemanticShaderDebugInfo100FlagIsDefinition = 0x08, + NonSemanticShaderDebugInfo100FlagFwdDecl = 0x10, + NonSemanticShaderDebugInfo100FlagArtificial = 0x20, + NonSemanticShaderDebugInfo100FlagExplicit = 0x40, + NonSemanticShaderDebugInfo100FlagPrototyped = 0x80, + NonSemanticShaderDebugInfo100FlagObjectPointer = 0x100, + NonSemanticShaderDebugInfo100FlagStaticMember = 0x200, + NonSemanticShaderDebugInfo100FlagIndirectVariable = 0x400, + NonSemanticShaderDebugInfo100FlagLValueReference = 0x800, + NonSemanticShaderDebugInfo100FlagRValueReference = 0x1000, + NonSemanticShaderDebugInfo100FlagIsOptimized = 0x2000, + NonSemanticShaderDebugInfo100FlagIsEnumClass = 0x4000, + NonSemanticShaderDebugInfo100FlagTypePassByValue = 0x8000, + NonSemanticShaderDebugInfo100FlagTypePassByReference = 0x10000, + NonSemanticShaderDebugInfo100FlagUnknownPhysicalLayout = 0x20000, + NonSemanticShaderDebugInfo100DebugInfoFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100BuildIdentifierFlags { + NonSemanticShaderDebugInfo100IdentifierPossibleDuplicates = 0x01, + NonSemanticShaderDebugInfo100BuildIdentifierFlagsMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncoding { + NonSemanticShaderDebugInfo100Unspecified = 0, + NonSemanticShaderDebugInfo100Address = 1, + NonSemanticShaderDebugInfo100Boolean = 2, + NonSemanticShaderDebugInfo100Float = 3, + NonSemanticShaderDebugInfo100Signed = 4, + NonSemanticShaderDebugInfo100SignedChar = 5, + NonSemanticShaderDebugInfo100Unsigned = 6, + NonSemanticShaderDebugInfo100UnsignedChar = 7, + NonSemanticShaderDebugInfo100DebugBaseTypeAttributeEncodingMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugCompositeType { + NonSemanticShaderDebugInfo100Class = 0, + NonSemanticShaderDebugInfo100Structure = 1, + NonSemanticShaderDebugInfo100Union = 2, + NonSemanticShaderDebugInfo100DebugCompositeTypeMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugTypeQualifier { + NonSemanticShaderDebugInfo100ConstType = 0, + NonSemanticShaderDebugInfo100VolatileType = 1, + NonSemanticShaderDebugInfo100RestrictType = 2, + NonSemanticShaderDebugInfo100AtomicType = 3, + NonSemanticShaderDebugInfo100DebugTypeQualifierMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugOperation { + NonSemanticShaderDebugInfo100Deref = 0, + NonSemanticShaderDebugInfo100Plus = 1, + NonSemanticShaderDebugInfo100Minus = 2, + NonSemanticShaderDebugInfo100PlusUconst = 3, + NonSemanticShaderDebugInfo100BitPiece = 4, + NonSemanticShaderDebugInfo100Swap = 5, + NonSemanticShaderDebugInfo100Xderef = 6, + NonSemanticShaderDebugInfo100StackValue = 7, + NonSemanticShaderDebugInfo100Constu = 8, + NonSemanticShaderDebugInfo100Fragment = 9, + NonSemanticShaderDebugInfo100DebugOperationMax = 0x7fffffff +}; + +enum NonSemanticShaderDebugInfo100DebugImportedEntity { + NonSemanticShaderDebugInfo100ImportedModule = 0, + NonSemanticShaderDebugInfo100ImportedDeclaration = 1, + NonSemanticShaderDebugInfo100DebugImportedEntityMax = 0x7fffffff +}; + + +#ifdef __cplusplus +} +#endif + +#endif // SPIRV_UNIFIED1_NonSemanticShaderDebugInfo100_H_ diff --git a/thirdparty/SPIRV-Cross/README.md b/thirdparty/SPIRV-Cross/README.md new file mode 100644 index 000000000..a1aa5511d --- /dev/null +++ b/thirdparty/SPIRV-Cross/README.md @@ -0,0 +1,558 @@ + + +# SPIRV-Cross + +SPIRV-Cross is a tool designed for parsing and converting SPIR-V to other shader languages. + +[![CI](https://github.com/KhronosGroup/SPIRV-Cross/actions/workflows/main.yml/badge.svg)](https://github.com/KhronosGroup/SPIRV-Cross/actions/workflows/main.yml) +[![Build Status](https://ci.appveyor.com/api/projects/status/github/KhronosGroup/SPIRV-Cross?svg=true&branch=main)](https://ci.appveyor.com/project/HansKristian-Work/SPIRV-Cross) + +## Features + + - Convert SPIR-V to readable, usable and efficient GLSL + - 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 a JSON reflection format + - Convert SPIR-V to debuggable C++ [DEPRECATED] + - 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. + +SPIRV-Cross tries hard to emit readable and clean output from the SPIR-V. +The goal is to emit GLSL or MSL that looks like it was written by a human and not awkward IR/assembly-like code. + +NOTE: Individual features are expected to be mostly complete, but it is possible that certain obscure GLSL features are not yet supported. +However, most missing features are expected to be "trivial" improvements at this stage. + +## Building + +SPIRV-Cross has been tested on Linux, iOS/OSX, Windows and Android. CMake is the main build system. + +### NOTE: main branch rename + +On 2023-01-12, `master` was renamed to `main` as per Khronos policy. + +### Linux and macOS + +Building with CMake is recommended, as it is the only build system which is tested in continuous integration. +It is also the only build system which has install commands and other useful build system features. + +However, you can just run `make` on the command line as a fallback if you only care about the CLI tool. + +A non-ancient GCC (4.8+) or Clang (3.x+) compiler is required as SPIRV-Cross uses C++11 extensively. + +### Windows + +Building with CMake is recommended, which is the only way to target MSVC. +MinGW-w64 based compilation works with `make` as a fallback. + +### Android + +SPIRV-Cross is only useful as a library here. Use the CMake build to link SPIRV-Cross to your project. + +### C++ exceptions + +The make and CMake build flavors offer the option to treat exceptions as assertions. To disable exceptions for make just append `SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=1` to the command line. For CMake append `-DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=ON`. By default exceptions are enabled. + +### Static, shared and CLI + +You can use `-DSPIRV_CROSS_STATIC=ON/OFF` `-DSPIRV_CROSS_SHARED=ON/OFF` `-DSPIRV_CROSS_CLI=ON/OFF` to control which modules are built (and installed). + +### Installing SPIRV-Cross (vcpkg) + +Alternatively, you can build and install SPIRV-Cross using [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: + +``` +git clone https://github.com/Microsoft/vcpkg.git +cd vcpkg +./bootstrap-vcpkg.sh +./vcpkg integrate install +./vcpkg install spirv-cross +``` + +The SPIRV-Cross port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + +## Usage + +### Using the C++ API + +The C++ API is the main API for SPIRV-Cross. For more in-depth documentation than what's provided in this README, +please have a look at the [Wiki](https://github.com/KhronosGroup/SPIRV-Cross/wiki). +**NOTE**: This API is not guaranteed to be ABI-stable, and it is highly recommended to link against this API statically. +The API is generally quite stable, but it can change over time, see the C API for more stability. + +To perform reflection and convert to other shader languages you can use the SPIRV-Cross API. +For example: + +```c++ +#include "spirv_glsl.hpp" +#include +#include + +extern std::vector load_spirv_file(); + +int main() +{ + // Read SPIR-V from disk or similar. + std::vector spirv_binary = load_spirv_file(); + + spirv_cross::CompilerGLSL glsl(std::move(spirv_binary)); + + // The SPIR-V is now parsed, and we can perform reflection on it. + spirv_cross::ShaderResources resources = glsl.get_shader_resources(); + + // Get all sampled images in the shader. + for (auto &resource : resources.sampled_images) + { + unsigned set = glsl.get_decoration(resource.id, spv::DecorationDescriptorSet); + unsigned binding = glsl.get_decoration(resource.id, spv::DecorationBinding); + printf("Image %s at set = %u, binding = %u\n", resource.name.c_str(), set, binding); + + // Modify the decoration to prepare it for GLSL. + glsl.unset_decoration(resource.id, spv::DecorationDescriptorSet); + + // Some arbitrary remapping if we want. + glsl.set_decoration(resource.id, spv::DecorationBinding, set * 16 + binding); + } + + // Set some options. + spirv_cross::CompilerGLSL::Options options; + options.version = 310; + options.es = true; + glsl.set_common_options(options); + + // Compile to GLSL, ready to give to GL driver. + std::string source = glsl.compile(); +} +``` + +### Using the C API wrapper + +To facilitate C compatibility and compatibility with foreign programming languages, a C89-compatible API wrapper is provided. Unlike the C++ API, +the goal of this wrapper is to be fully stable, both API and ABI-wise. +This is the only interface which is supported when building SPIRV-Cross as a shared library. + +An important point of the wrapper is that all memory allocations are contained in the `spvc_context`. +This simplifies the use of the API greatly. However, you should destroy the context as soon as reasonable, +or use `spvc_context_release_allocations()` if you intend to reuse the `spvc_context` object again soon. + +Most functions return a `spvc_result`, where `SPVC_SUCCESS` is the only success code. +For brevity, the code below does not do any error checking. + +```c +#include + +const SpvId *spirv = get_spirv_data(); +size_t word_count = get_spirv_word_count(); + +spvc_context context = NULL; +spvc_parsed_ir ir = NULL; +spvc_compiler compiler_glsl = NULL; +spvc_compiler_options options = NULL; +spvc_resources resources = NULL; +const spvc_reflected_resource *list = NULL; +const char *result = NULL; +size_t count; +size_t i; + +// Create context. +spvc_context_create(&context); + +// Set debug callback. +spvc_context_set_error_callback(context, error_callback, userdata); + +// Parse the SPIR-V. +spvc_context_parse_spirv(context, spirv, word_count, &ir); + +// Hand it off to a compiler instance and give it ownership of the IR. +spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_glsl); + +// Do some basic reflection. +spvc_compiler_create_shader_resources(compiler_glsl, &resources); +spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count); + +for (i = 0; i < count; i++) +{ + printf("ID: %u, BaseTypeID: %u, TypeID: %u, Name: %s\n", list[i].id, list[i].base_type_id, list[i].type_id, + list[i].name); + printf(" Set: %u, Binding: %u\n", + spvc_compiler_get_decoration(compiler_glsl, list[i].id, SpvDecorationDescriptorSet), + spvc_compiler_get_decoration(compiler_glsl, list[i].id, SpvDecorationBinding)); +} + +// Modify options. +spvc_compiler_create_compiler_options(compiler_glsl, &options); +spvc_compiler_options_set_uint(options, SPVC_COMPILER_OPTION_GLSL_VERSION, 330); +spvc_compiler_options_set_bool(options, SPVC_COMPILER_OPTION_GLSL_ES, SPVC_FALSE); +spvc_compiler_install_compiler_options(compiler_glsl, options); + +spvc_compiler_compile(compiler_glsl, &result); +printf("Cross-compiled source: %s\n", result); + +// Frees all memory we allocated so far. +spvc_context_destroy(context); +``` + +### Linking + +#### CMake add_subdirectory() + +This is the recommended way if you are using CMake and want to link against SPIRV-Cross statically. + +#### Integrating SPIRV-Cross in a custom build system + +To add SPIRV-Cross to your own codebase, just copy the source and header files from root directory +and build the relevant .cpp files you need. Make sure to build with C++11 support, e.g. `-std=c++11` in GCC and Clang. +Alternatively, the Makefile generates a libspirv-cross.a static library during build that can be linked in. + +#### Linking against SPIRV-Cross as a system library + +It is possible to link against SPIRV-Cross when it is installed as a system library, +which would be mostly relevant for Unix-like platforms. + +##### pkg-config + +For Unix-based systems, a pkg-config is installed for the C API, e.g.: + +``` +$ pkg-config spirv-cross-c-shared --libs --cflags +-I/usr/local/include/spirv_cross -L/usr/local/lib -lspirv-cross-c-shared +``` + +##### CMake + +If the project is installed, it can be found with `find_package()`, e.g.: + +``` +cmake_minimum_required(VERSION 3.5) +set(CMAKE_C_STANDARD 99) +project(Test LANGUAGES C) + +find_package(spirv_cross_c_shared) +if (spirv_cross_c_shared_FOUND) + message(STATUS "Found SPIRV-Cross C API! :)") +else() + message(STATUS "Could not find SPIRV-Cross C API! :(") +endif() + +add_executable(test test.c) +target_link_libraries(test spirv-cross-c-shared) +``` + +test.c: +```c +#include + +int main(void) +{ + spvc_context context; + spvc_context_create(&context); + spvc_context_destroy(context); +} +``` + +### CLI + +The CLI is suitable for basic cross-compilation tasks, but it cannot support the full flexibility that the API can. +Some examples below. + +#### Creating a SPIR-V file from GLSL with glslang + +``` +glslangValidator -H -V -o test.spv test.frag +``` + +NOTE: This assumes Vulkan GLSL input. In general, only modern GLSL (#version 330+ for desktop) can be compiled to SPIR-V. +Typically when cross compiling GLSL, you start with modern GLSL and cross compile to older targets for compatibility, +not the other way around. + +#### Converting a SPIR-V file to GLSL ES + +``` +glslangValidator -H -V -o test.spv shaders/comp/basic.comp +./spirv-cross --version 310 --es test.spv +``` + +#### Converting to desktop GLSL + +``` +glslangValidator -H -V -o test.spv shaders/comp/basic.comp +./spirv-cross --version 330 --no-es test.spv --output test.comp +``` + +#### Disable prettifying optimizations + +``` +glslangValidator -H -V -o test.spv shaders/comp/basic.comp +./spirv-cross --version 310 --es test.spv --output test.comp --force-temporary +``` + +### Using shaders generated from C++ backend + +Please see `samples/cpp` where some GLSL shaders are compiled to SPIR-V, decompiled to C++ and run with test data. +Reading through the samples should explain how to use the C++ interface. +A simple Makefile is included to build all shaders in the directory. + +### Implementation notes + +When using SPIR-V and SPIRV-Cross as an intermediate step for cross-compiling between high level languages there are some considerations to take into account, +as not all features used by one high-level language are necessarily supported natively by the target shader language. +SPIRV-Cross aims to provide the tools needed to handle these scenarios in a clean and robust way, but some manual action is required to maintain compatibility. + +#### HLSL source to GLSL + +##### HLSL entry points + +When using SPIR-V shaders compiled from HLSL, there are some extra things you need to take care of. +First make sure that the entry point is used correctly. +If you forget to set the entry point correctly in glslangValidator (-e MyFancyEntryPoint), +you will likely encounter this error message: + +``` +Cannot end a function before ending the current block. +Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid. +``` + +##### Vertex/Fragment interface linking + +HLSL relies on semantics in order to effectively link together shader stages. In the SPIR-V generated by glslang, the transformation from HLSL to GLSL ends up looking like + +```c++ +struct VSOutput { + // SV_Position is rerouted to gl_Position + float4 position : SV_Position; + float4 coord : TEXCOORD0; +}; + +VSOutput main(...) {} +``` + +```c++ +struct VSOutput { + float4 coord; +} +layout(location = 0) out VSOutput _magicNameGeneratedByGlslang; +``` + +While this works, be aware of the type of the struct which is used in the vertex stage and the fragment stage. +There may be issues if the structure type name differs in vertex stage and fragment stage. + +You can make use of the reflection interface to force the name of the struct type. + +``` +// Something like this for both vertex outputs and fragment inputs. +compiler.set_name(varying_resource.base_type_id, "VertexFragmentLinkage"); +``` + +Some platform may require identical variable name for both vertex outputs and fragment inputs. (for example MacOSX) +to rename variable base on location, please add +``` +--rename-interface-variable +``` + +#### HLSL source to legacy GLSL/ESSL + +HLSL tends to emit varying struct types to pass data between vertex and fragment. +This is not supported in legacy GL/GLES targets, so to support this, varying structs are flattened. +This is done automatically, but the API user might need to be aware that this is happening in order to support all cases. + +Modern GLES code like this: +```c++ +struct Output { + vec4 a; + vec2 b; +}; +out Output vout; +``` + +Is transformed into: +```c++ +struct Output { + vec4 a; + vec2 b; +}; +varying vec4 Output_a; +varying vec2 Output_b; +``` + +Note that now, both the struct name and the member names will participate in the linking interface between vertex and fragment, so +API users might want to ensure that both the struct names and member names match so that vertex outputs and fragment inputs can link properly. + + +#### Separate image samplers (HLSL/Vulkan) for backends which do not support it (GLSL) + +Another thing you need to remember is when using samplers and textures in HLSL these are separable, and not directly compatible with GLSL. If you need to use this with desktop GL/GLES, you need to call `Compiler::build_combined_image_samplers` first before calling `Compiler::compile`, or you will get an exception. + +```c++ +// From main.cpp +// Builds a mapping for all combinations of images and samplers. +compiler->build_combined_image_samplers(); + +// Give the remapped combined samplers new names. +// Here you can also set up decorations if you want (binding = #N). +for (auto &remap : compiler->get_combined_image_samplers()) +{ + compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id), + compiler->get_name(remap.sampler_id))); +} +``` + +If your target is Vulkan GLSL, `--vulkan-semantics` will emit separate image samplers as you'd expect. +The command line client calls `Compiler::build_combined_image_samplers` automatically, but if you're calling the library, you'll need to do this yourself. + +#### Descriptor sets (Vulkan GLSL) for backends which do not support them (pre HLSL 5.1 / GLSL) + +Descriptor sets are unique to Vulkan, so make sure that descriptor set + binding is remapped to a flat binding scheme (set always 0), so that other APIs can make sense of the bindings. +This can be done with `Compiler::set_decoration(id, spv::DecorationDescriptorSet)`. For other backends like MSL and HLSL, descriptor sets +can be used, with some minor caveats, see below. + +##### MSL 2.0+ + +Metal supports indirect argument buffers (--msl-argument-buffers). In this case, descriptor sets become argument buffers, +and bindings are mapped to [[id(N)]] within the argument buffer. One quirk is that arrays of resources consume multiple ids, +where Vulkan does not. This can be worked around either from shader authoring stage +or remapping bindings as needed to avoid the overlap. +There is also a rich API to declare remapping schemes which is intended to work like +the pipeline layout in Vulkan. See `CompilerMSL::add_msl_resource_binding`. Remapping combined image samplers for example +must be split into two bindings in MSL, so it's possible to declare an id for the texture and sampler binding separately. + +##### HLSL - SM 5.1+ + +In SM 5.1+, descriptor set bindings are interpreted as register spaces directly. In HLSL however, arrays of resources consume +multiple binding slots where Vulkan does not, so there might be overlap if the SPIR-V was not authored with this in mind. +This can be worked around either from shader authoring stage (don't assign overlapping bindings) +or remap bindings in SPIRV-Cross as needed to avoid the overlap. + +#### Linking by name for targets which do not support explicit locations (legacy GLSL/ESSL) + +Modern GLSL and HLSL sources (and SPIR-V) relies on explicit layout(location) qualifiers to guide the linking process between shader stages, +but older GLSL relies on symbol names to perform the linking. When emitting shaders with older versions, these layout statements will be removed, +so it is important that the API user ensures that the names of I/O variables are sanitized so that linking will work properly. +The reflection API can rename variables, struct types and struct members to deal with these scenarios using `Compiler::set_name` and friends. + +#### Clip-space conventions + +SPIRV-Cross can perform some common clip space conversions on gl_Position/SV_Position by enabling `CompilerGLSL::Options.vertex.fixup_clipspace`. +While this can be convenient, it is recommended to modify the projection matrices instead as that can achieve the same result. + +For GLSL targets, enabling this will convert a shader which assumes `[0, w]` depth range (Vulkan / D3D / Metal) into `[-w, w]` range. +For MSL and HLSL targets, enabling this will convert a shader in `[-w, w]` depth range (OpenGL) to `[0, w]` depth range. + +By default, the CLI will not enable `fixup_clipspace`, but in the API you might want to set an explicit value using `CompilerGLSL::set_options()`. + +Y-flipping of gl_Position and similar is also supported. +The use of this is discouraged, because relying on vertex shader Y-flipping tends to get quite messy. +To enable this, set `CompilerGLSL::Options.vertex.flip_vert_y` or `--flip-vert-y` in CLI. + +#### Reserved identifiers + +When cross-compiling, certain identifiers are considered to be reserved by the implementation. +Code generated by SPIRV-Cross cannot emit these identifiers as they are reserved and used for various internal purposes, +and such variables will typically show up as `_RESERVED_IDENTIFIER_FIXUP_` +or some similar name to make it more obvious that an identifier has been renamed. + +Reflection output will follow the exact name specified in the SPIR-V module. It might not be a valid identifier in the C sense, +as it may contain non-alphanumeric/non-underscore characters. + +Reserved identifiers currently assumed by the implementation are (in pseudo-regex): + +- _$digit+, e.g. `_100`, `_2` +- _$digit+_.+, e.g. `_100_tmp`, `_2_foobar`. `_2Bar` is **not** reserved. +- gl_- prefix +- spv- prefix +- SPIRV_Cross prefix. This prefix is generally used for interface variables where app needs to provide data for workaround purposes. + This identifier will not be rewritten, but be aware of potential collisions. +- Double underscores (reserved by all target languages). + +Members of structs also have a reserved identifier: +- _m$digit+$END, e.g. `_m20` and `_m40` are reserved, but not `_m40Foobar`. + +## Contributing + +Contributions to SPIRV-Cross are welcome. See Testing and Licensing sections for details. + +### Testing + +SPIRV-Cross maintains a test suite of shaders with reference output of how the output looks after going through a roundtrip through +glslangValidator/spirv-as then back through SPIRV-Cross again. +The reference files are stored inside the repository in order to be able to track regressions. + +All pull requests should ensure that test output does not change unexpectedly. This can be tested with: + +``` +./checkout_glslang_spirv_tools.sh # Checks out glslang and SPIRV-Tools at a fixed revision which matches the reference output. +./build_glslang_spirv_tools.sh # Builds glslang and SPIRV-Tools. +./test_shaders.sh # Runs over all changes and makes sure that there are no deltas compared to reference files. +``` + +`./test_shaders.sh` currently requires a Makefile setup with GCC/Clang to be set up. +However, on Windows, this can be rather inconvenient if a MinGW environment is not set up. +To use a spirv-cross binary you built with CMake (or otherwise), you can pass in an environment variable as such: + +``` +SPIRV_CROSS_PATH=path/to/custom/spirv-cross ./test_shaders.sh +``` + +However, when improving SPIRV-Cross there are of course legitimate cases where reference output should change. +In these cases, run: + +``` +./update_test_shaders.sh # SPIRV_CROSS_PATH also works here. +``` + +to update the reference files and include these changes as part of the pull request. +Always make sure you are running the correct version of glslangValidator as well as SPIRV-Tools when updating reference files. +See `checkout_glslang_spirv_tools.sh` which revisions are currently expected. The revisions change regularly. + +In short, the main branch should always be able to run `./test_shaders.py shaders` and friends without failure. +SPIRV-Cross uses Travis CI to test all pull requests, so it is not strictly needed to perform testing yourself if you have problems running it locally. +A pull request which does not pass testing on Travis will not be accepted however. + +When adding support for new features to SPIRV-Cross, a new shader and reference file should be added which covers usage of the new shader features in question. +Travis CI runs the test suite with the CMake, by running `ctest`. This is a more straight-forward alternative to `./test_shaders.sh`. + +### Licensing + +Contributors of new files should add a copyright header at the top of every new source code file with their copyright +along with the Apache 2.0 licensing stub. + +### Formatting + +SPIRV-Cross uses `clang-format` to automatically format code. +Please use `clang-format` with the style sheet found in `.clang-format` to automatically format code before submitting a pull request. + +To make things easy, the `format_all.sh` script can be used to format all +source files in the library. In this directory, run the following from the +command line: + + ./format_all.sh + +## Regression testing + +In shaders/ a collection of shaders are maintained for purposes of regression testing. +The current reference output is contained in reference/. +`./test_shaders.py shaders` can be run to perform regression testing. + +See `./test_shaders.py --help` for more. + +### Metal backend + +To test the roundtrip path GLSL -> SPIR-V -> MSL, `--msl` can be added, e.g. `./test_shaders.py --msl shaders-msl`. + +### HLSL backend + +To test the roundtrip path GLSL -> SPIR-V -> HLSL, `--hlsl` can be added, e.g. `./test_shaders.py --hlsl shaders-hlsl`. + +### Updating regression tests + +When legitimate changes are found, use `--update` flag to update regression files. +Otherwise, `./test_shaders.py` will fail with error code. + +### Mali Offline Compiler cycle counts + +To obtain a CSV of static shader cycle counts before and after going through spirv-cross, add +`--malisc` flag to `./test_shaders`. This requires the Mali Offline Compiler to be installed in PATH. + diff --git a/thirdparty/SPIRV-Cross/cmake/gitversion.in.h b/thirdparty/SPIRV-Cross/cmake/gitversion.in.h new file mode 100644 index 000000000..bff73e964 --- /dev/null +++ b/thirdparty/SPIRV-Cross/cmake/gitversion.in.h @@ -0,0 +1,9 @@ +// Copyright 2016-2021 The Khronos Group Inc. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef SPIRV_CROSS_GIT_VERSION_H_ +#define SPIRV_CROSS_GIT_VERSION_H_ + +#define SPIRV_CROSS_GIT_REVISION "Git commit: @spirv-cross-build-version@ Timestamp: @spirv-cross-timestamp@" + +#endif diff --git a/thirdparty/SPIRV-Cross/include/spirv_cross/barrier.hpp b/thirdparty/SPIRV-Cross/include/spirv_cross/barrier.hpp new file mode 100644 index 000000000..4ca7f4d77 --- /dev/null +++ b/thirdparty/SPIRV-Cross/include/spirv_cross/barrier.hpp @@ -0,0 +1,80 @@ +/* + * Copyright 2015-2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_BARRIER_HPP +#define SPIRV_CROSS_BARRIER_HPP + +#include +#include + +namespace spirv_cross +{ +class Barrier +{ +public: + Barrier() + { + count.store(0); + iteration.store(0); + } + + void set_release_divisor(unsigned divisor) + { + this->divisor = divisor; + } + + static inline void memoryBarrier() + { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + + void reset_counter() + { + count.store(0); + iteration.store(0); + } + + void wait() + { + unsigned target_iteration = iteration.load(std::memory_order_relaxed) + 1; + // Overflows cleanly. + unsigned target_count = divisor * target_iteration; + + // Barriers don't enforce memory ordering. + // Be as relaxed about the barrier as we possibly can! + unsigned c = count.fetch_add(1u, std::memory_order_relaxed); + + if (c + 1 == target_count) + { + iteration.store(target_iteration, std::memory_order_relaxed); + } + else + { + // If we have more threads than the CPU, don't hog the CPU for very long periods of time. + while (iteration.load(std::memory_order_relaxed) != target_iteration) + std::this_thread::yield(); + } + } + +private: + unsigned divisor = 1; + std::atomic count; + std::atomic iteration; +}; +} + +#endif diff --git a/thirdparty/SPIRV-Cross/include/spirv_cross/external_interface.h b/thirdparty/SPIRV-Cross/include/spirv_cross/external_interface.h new file mode 100644 index 000000000..949654f5b --- /dev/null +++ b/thirdparty/SPIRV-Cross/include/spirv_cross/external_interface.h @@ -0,0 +1,127 @@ +/* + * Copyright 2015-2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_EXTERNAL_INTERFACE_H +#define SPIRV_CROSS_EXTERNAL_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct spirv_cross_shader spirv_cross_shader_t; + +struct spirv_cross_interface +{ + spirv_cross_shader_t *(*construct)(void); + void (*destruct)(spirv_cross_shader_t *thiz); + void (*invoke)(spirv_cross_shader_t *thiz); +}; + +void spirv_cross_set_stage_input(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size); + +void spirv_cross_set_stage_output(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size); + +void spirv_cross_set_push_constant(spirv_cross_shader_t *thiz, void *data, size_t size); + +void spirv_cross_set_uniform_constant(spirv_cross_shader_t *thiz, unsigned location, void *data, size_t size); + +void spirv_cross_set_resource(spirv_cross_shader_t *thiz, unsigned set, unsigned binding, void **data, size_t size); + +const struct spirv_cross_interface *spirv_cross_get_interface(void); + +typedef enum spirv_cross_builtin { + SPIRV_CROSS_BUILTIN_POSITION = 0, + SPIRV_CROSS_BUILTIN_FRAG_COORD = 1, + SPIRV_CROSS_BUILTIN_WORK_GROUP_ID = 2, + SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS = 3, + SPIRV_CROSS_NUM_BUILTINS +} spirv_cross_builtin; + +void spirv_cross_set_builtin(spirv_cross_shader_t *thiz, spirv_cross_builtin builtin, void *data, size_t size); + +#define SPIRV_CROSS_NUM_DESCRIPTOR_SETS 4 +#define SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS 16 +#define SPIRV_CROSS_NUM_STAGE_INPUTS 16 +#define SPIRV_CROSS_NUM_STAGE_OUTPUTS 16 +#define SPIRV_CROSS_NUM_UNIFORM_CONSTANTS 32 + +enum spirv_cross_format +{ + SPIRV_CROSS_FORMAT_R8_UNORM = 0, + SPIRV_CROSS_FORMAT_R8G8_UNORM = 1, + SPIRV_CROSS_FORMAT_R8G8B8_UNORM = 2, + SPIRV_CROSS_FORMAT_R8G8B8A8_UNORM = 3, + + SPIRV_CROSS_NUM_FORMATS +}; + +enum spirv_cross_wrap +{ + SPIRV_CROSS_WRAP_CLAMP_TO_EDGE = 0, + SPIRV_CROSS_WRAP_REPEAT = 1, + + SPIRV_CROSS_NUM_WRAP +}; + +enum spirv_cross_filter +{ + SPIRV_CROSS_FILTER_NEAREST = 0, + SPIRV_CROSS_FILTER_LINEAR = 1, + + SPIRV_CROSS_NUM_FILTER +}; + +enum spirv_cross_mipfilter +{ + SPIRV_CROSS_MIPFILTER_BASE = 0, + SPIRV_CROSS_MIPFILTER_NEAREST = 1, + SPIRV_CROSS_MIPFILTER_LINEAR = 2, + + SPIRV_CROSS_NUM_MIPFILTER +}; + +struct spirv_cross_miplevel +{ + const void *data; + unsigned width, height; + size_t stride; +}; + +struct spirv_cross_sampler_info +{ + const struct spirv_cross_miplevel *mipmaps; + unsigned num_mipmaps; + + enum spirv_cross_format format; + enum spirv_cross_wrap wrap_s; + enum spirv_cross_wrap wrap_t; + enum spirv_cross_filter min_filter; + enum spirv_cross_filter mag_filter; + enum spirv_cross_mipfilter mip_filter; +}; + +typedef struct spirv_cross_sampler_2d spirv_cross_sampler_2d_t; +spirv_cross_sampler_2d_t *spirv_cross_create_sampler_2d(const struct spirv_cross_sampler_info *info); +void spirv_cross_destroy_sampler_2d(spirv_cross_sampler_2d_t *samp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/SPIRV-Cross/include/spirv_cross/image.hpp b/thirdparty/SPIRV-Cross/include/spirv_cross/image.hpp new file mode 100644 index 000000000..a41ccdfbb --- /dev/null +++ b/thirdparty/SPIRV-Cross/include/spirv_cross/image.hpp @@ -0,0 +1,63 @@ +/* + * Copyright 2015-2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_IMAGE_HPP +#define SPIRV_CROSS_IMAGE_HPP + +#ifndef GLM_SWIZZLE +#define GLM_SWIZZLE +#endif + +#ifndef GLM_FORCE_RADIANS +#define GLM_FORCE_RADIANS +#endif + +#include + +namespace spirv_cross +{ +template +struct image2DBase +{ + virtual ~image2DBase() = default; + inline virtual T load(glm::ivec2 coord) const + { + return T(0, 0, 0, 1); + } + inline virtual void store(glm::ivec2 coord, const T &v) + { + } +}; + +typedef image2DBase image2D; +typedef image2DBase iimage2D; +typedef image2DBase uimage2D; + +template +inline T imageLoad(const image2DBase &image, glm::ivec2 coord) +{ + return image.load(coord); +} + +template +void imageStore(image2DBase &image, glm::ivec2 coord, const T &value) +{ + image.store(coord, value); +} +} + +#endif diff --git a/thirdparty/SPIRV-Cross/include/spirv_cross/internal_interface.hpp b/thirdparty/SPIRV-Cross/include/spirv_cross/internal_interface.hpp new file mode 100644 index 000000000..3ff7f8e25 --- /dev/null +++ b/thirdparty/SPIRV-Cross/include/spirv_cross/internal_interface.hpp @@ -0,0 +1,604 @@ +/* + * Copyright 2015-2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_INTERNAL_INTERFACE_HPP +#define SPIRV_CROSS_INTERNAL_INTERFACE_HPP + +// This file must only be included by the shader generated by spirv-cross! + +#ifndef GLM_FORCE_SWIZZLE +#define GLM_FORCE_SWIZZLE +#endif + +#ifndef GLM_FORCE_RADIANS +#define GLM_FORCE_RADIANS +#endif + +#include + +#include "barrier.hpp" +#include "external_interface.h" +#include "image.hpp" +#include "sampler.hpp" +#include "thread_group.hpp" +#include +#include + +namespace internal +{ +// Adaptor helpers to adapt GLSL access chain syntax to C++. +// Don't bother with arrays of arrays on uniforms ... +// Would likely need horribly complex variadic template munging. + +template +struct Interface +{ + enum + { + ArraySize = 1, + Size = sizeof(T) + }; + + Interface() + : ptr(0) + { + } + T &get() + { + assert(ptr); + return *ptr; + } + + T *ptr; +}; + +// For array types, return a pointer instead. +template +struct Interface +{ + enum + { + ArraySize = U, + Size = U * sizeof(T) + }; + + Interface() + : ptr(0) + { + } + T *get() + { + assert(ptr); + return ptr; + } + + T *ptr; +}; + +// For case when array size is 1, avoid double dereference. +template +struct PointerInterface +{ + enum + { + ArraySize = 1, + Size = sizeof(T *) + }; + enum + { + PreDereference = true + }; + + PointerInterface() + : ptr(0) + { + } + + T &get() + { + assert(ptr); + return *ptr; + } + + T *ptr; +}; + +// Automatically converts a pointer down to reference to match GLSL syntax. +template +struct DereferenceAdaptor +{ + DereferenceAdaptor(T **ptr) + : ptr(ptr) + { + } + T &operator[](unsigned index) const + { + return *(ptr[index]); + } + T **ptr; +}; + +// We can't have a linear array of T* since T* can be an abstract type in case of samplers. +// We also need a list of pointers since we can have run-time length SSBOs. +template +struct PointerInterface +{ + enum + { + ArraySize = U, + Size = sizeof(T *) * U + }; + enum + { + PreDereference = false + }; + PointerInterface() + : ptr(0) + { + } + + DereferenceAdaptor get() + { + assert(ptr); + return DereferenceAdaptor(ptr); + } + + T **ptr; +}; + +// Resources can be more abstract and be unsized, +// so we need to have an array of pointers for those cases. +template +struct Resource : PointerInterface +{ +}; + +// POD with no unknown sizes, so we can express these as flat arrays. +template +struct UniformConstant : Interface +{ +}; +template +struct StageInput : Interface +{ +}; +template +struct StageOutput : Interface +{ +}; +template +struct PushConstant : Interface +{ +}; +} + +struct spirv_cross_shader +{ + struct PPSize + { + PPSize() + : ptr(0) + , size(0) + { + } + void **ptr; + size_t size; + }; + + struct PPSizeResource + { + PPSizeResource() + : ptr(0) + , size(0) + , pre_dereference(false) + { + } + void **ptr; + size_t size; + bool pre_dereference; + }; + + PPSizeResource resources[SPIRV_CROSS_NUM_DESCRIPTOR_SETS][SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS]; + PPSize stage_inputs[SPIRV_CROSS_NUM_STAGE_INPUTS]; + PPSize stage_outputs[SPIRV_CROSS_NUM_STAGE_OUTPUTS]; + PPSize uniform_constants[SPIRV_CROSS_NUM_UNIFORM_CONSTANTS]; + PPSize push_constant; + PPSize builtins[SPIRV_CROSS_NUM_BUILTINS]; + + template + void register_builtin(spirv_cross_builtin builtin, const U &value) + { + assert(!builtins[builtin].ptr); + + builtins[builtin].ptr = (void **)&value.ptr; + builtins[builtin].size = sizeof(*value.ptr) * U::ArraySize; + } + + void set_builtin(spirv_cross_builtin builtin, void *data, size_t size) + { + assert(builtins[builtin].ptr); + assert(size >= builtins[builtin].size); + + *builtins[builtin].ptr = data; + } + + template + void register_resource(const internal::Resource &value, unsigned set, unsigned binding) + { + assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS); + assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS); + assert(!resources[set][binding].ptr); + + resources[set][binding].ptr = (void **)&value.ptr; + resources[set][binding].size = internal::Resource::Size; + resources[set][binding].pre_dereference = internal::Resource::PreDereference; + } + + template + void register_stage_input(const internal::StageInput &value, unsigned location) + { + assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS); + assert(!stage_inputs[location].ptr); + + stage_inputs[location].ptr = (void **)&value.ptr; + stage_inputs[location].size = internal::StageInput::Size; + } + + template + void register_stage_output(const internal::StageOutput &value, unsigned location) + { + assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS); + assert(!stage_outputs[location].ptr); + + stage_outputs[location].ptr = (void **)&value.ptr; + stage_outputs[location].size = internal::StageOutput::Size; + } + + template + void register_uniform_constant(const internal::UniformConstant &value, unsigned location) + { + assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS); + assert(!uniform_constants[location].ptr); + + uniform_constants[location].ptr = (void **)&value.ptr; + uniform_constants[location].size = internal::UniformConstant::Size; + } + + template + void register_push_constant(const internal::PushConstant &value) + { + assert(!push_constant.ptr); + + push_constant.ptr = (void **)&value.ptr; + push_constant.size = internal::PushConstant::Size; + } + + void set_stage_input(unsigned location, void *data, size_t size) + { + assert(location < SPIRV_CROSS_NUM_STAGE_INPUTS); + assert(stage_inputs[location].ptr); + assert(size >= stage_inputs[location].size); + + *stage_inputs[location].ptr = data; + } + + void set_stage_output(unsigned location, void *data, size_t size) + { + assert(location < SPIRV_CROSS_NUM_STAGE_OUTPUTS); + assert(stage_outputs[location].ptr); + assert(size >= stage_outputs[location].size); + + *stage_outputs[location].ptr = data; + } + + void set_uniform_constant(unsigned location, void *data, size_t size) + { + assert(location < SPIRV_CROSS_NUM_UNIFORM_CONSTANTS); + assert(uniform_constants[location].ptr); + assert(size >= uniform_constants[location].size); + + *uniform_constants[location].ptr = data; + } + + void set_push_constant(void *data, size_t size) + { + assert(push_constant.ptr); + assert(size >= push_constant.size); + + *push_constant.ptr = data; + } + + void set_resource(unsigned set, unsigned binding, void **data, size_t size) + { + assert(set < SPIRV_CROSS_NUM_DESCRIPTOR_SETS); + assert(binding < SPIRV_CROSS_NUM_DESCRIPTOR_BINDINGS); + assert(resources[set][binding].ptr); + assert(size >= resources[set][binding].size); + + // We're using the regular PointerInterface, dereference ahead of time. + if (resources[set][binding].pre_dereference) + *resources[set][binding].ptr = *data; + else + *resources[set][binding].ptr = data; + } +}; + +namespace spirv_cross +{ +template +struct BaseShader : spirv_cross_shader +{ + void invoke() + { + static_cast(this)->main(); + } +}; + +struct FragmentResources +{ + internal::StageOutput gl_FragCoord; + void init(spirv_cross_shader &s) + { + s.register_builtin(SPIRV_CROSS_BUILTIN_FRAG_COORD, gl_FragCoord); + } +#define gl_FragCoord __res->gl_FragCoord.get() +}; + +template +struct FragmentShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + FragmentShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct VertexResources +{ + internal::StageOutput gl_Position; + void init(spirv_cross_shader &s) + { + s.register_builtin(SPIRV_CROSS_BUILTIN_POSITION, gl_Position); + } +#define gl_Position __res->gl_Position.get() +}; + +template +struct VertexShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + VertexShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct TessEvaluationResources +{ + inline void init(spirv_cross_shader &) + { + } +}; + +template +struct TessEvaluationShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + TessEvaluationShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct TessControlResources +{ + inline void init(spirv_cross_shader &) + { + } +}; + +template +struct TessControlShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + TessControlShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct GeometryResources +{ + inline void init(spirv_cross_shader &) + { + } +}; + +template +struct GeometryShader : BaseShader> +{ + inline void main() + { + impl.main(); + } + + GeometryShader() + { + resources.init(*this); + impl.__res = &resources; + } + + T impl; + Res resources; +}; + +struct ComputeResources +{ + internal::StageInput gl_WorkGroupID__; + internal::StageInput gl_NumWorkGroups__; + void init(spirv_cross_shader &s) + { + s.register_builtin(SPIRV_CROSS_BUILTIN_WORK_GROUP_ID, gl_WorkGroupID__); + s.register_builtin(SPIRV_CROSS_BUILTIN_NUM_WORK_GROUPS, gl_NumWorkGroups__); + } +#define gl_WorkGroupID __res->gl_WorkGroupID__.get() +#define gl_NumWorkGroups __res->gl_NumWorkGroups__.get() + + Barrier barrier__; +#define barrier() __res->barrier__.wait() +}; + +struct ComputePrivateResources +{ + uint32_t gl_LocalInvocationIndex__; +#define gl_LocalInvocationIndex __priv_res.gl_LocalInvocationIndex__ + glm::uvec3 gl_LocalInvocationID__; +#define gl_LocalInvocationID __priv_res.gl_LocalInvocationID__ + glm::uvec3 gl_GlobalInvocationID__; +#define gl_GlobalInvocationID __priv_res.gl_GlobalInvocationID__ +}; + +template +struct ComputeShader : BaseShader> +{ + inline void main() + { + resources.barrier__.reset_counter(); + + for (unsigned z = 0; z < WorkGroupZ; z++) + for (unsigned y = 0; y < WorkGroupY; y++) + for (unsigned x = 0; x < WorkGroupX; x++) + impl[z][y][x].__priv_res.gl_GlobalInvocationID__ = + glm::uvec3(WorkGroupX, WorkGroupY, WorkGroupZ) * resources.gl_WorkGroupID__.get() + + glm::uvec3(x, y, z); + + group.run(); + group.wait(); + } + + ComputeShader() + : group(&impl[0][0][0]) + { + resources.init(*this); + resources.barrier__.set_release_divisor(WorkGroupX * WorkGroupY * WorkGroupZ); + + unsigned i = 0; + for (unsigned z = 0; z < WorkGroupZ; z++) + { + for (unsigned y = 0; y < WorkGroupY; y++) + { + for (unsigned x = 0; x < WorkGroupX; x++) + { + impl[z][y][x].__priv_res.gl_LocalInvocationID__ = glm::uvec3(x, y, z); + impl[z][y][x].__priv_res.gl_LocalInvocationIndex__ = i++; + impl[z][y][x].__res = &resources; + } + } + } + } + + T impl[WorkGroupZ][WorkGroupY][WorkGroupX]; + ThreadGroup group; + Res resources; +}; + +inline void memoryBarrierShared() +{ + Barrier::memoryBarrier(); +} +inline void memoryBarrier() +{ + Barrier::memoryBarrier(); +} +// TODO: Rest of the barriers. + +// Atomics +template +inline T atomicAdd(T &v, T a) +{ + static_assert(sizeof(std::atomic) == sizeof(T), "Cannot cast properly to std::atomic."); + + // We need explicit memory barriers in GLSL to enfore any ordering. + // FIXME: Can we really cast this? There is no other way I think ... + return std::atomic_fetch_add_explicit(reinterpret_cast *>(&v), a, std::memory_order_relaxed); +} +} + +void spirv_cross_set_stage_input(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size) +{ + shader->set_stage_input(location, data, size); +} + +void spirv_cross_set_stage_output(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size) +{ + shader->set_stage_output(location, data, size); +} + +void spirv_cross_set_uniform_constant(spirv_cross_shader_t *shader, unsigned location, void *data, size_t size) +{ + shader->set_uniform_constant(location, data, size); +} + +void spirv_cross_set_resource(spirv_cross_shader_t *shader, unsigned set, unsigned binding, void **data, size_t size) +{ + shader->set_resource(set, binding, data, size); +} + +void spirv_cross_set_push_constant(spirv_cross_shader_t *shader, void *data, size_t size) +{ + shader->set_push_constant(data, size); +} + +void spirv_cross_set_builtin(spirv_cross_shader_t *shader, spirv_cross_builtin builtin, void *data, size_t size) +{ + shader->set_builtin(builtin, data, size); +} + +#endif diff --git a/thirdparty/SPIRV-Cross/include/spirv_cross/sampler.hpp b/thirdparty/SPIRV-Cross/include/spirv_cross/sampler.hpp new file mode 100644 index 000000000..020848095 --- /dev/null +++ b/thirdparty/SPIRV-Cross/include/spirv_cross/sampler.hpp @@ -0,0 +1,106 @@ +/* + * Copyright 2015-2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_SAMPLER_HPP +#define SPIRV_CROSS_SAMPLER_HPP + +#include + +namespace spirv_cross +{ +struct spirv_cross_sampler_2d +{ + inline virtual ~spirv_cross_sampler_2d() + { + } +}; + +template +struct sampler2DBase : spirv_cross_sampler_2d +{ + sampler2DBase(const spirv_cross_sampler_info *info) + { + mips.insert(mips.end(), info->mipmaps, info->mipmaps + info->num_mipmaps); + format = info->format; + wrap_s = info->wrap_s; + wrap_t = info->wrap_t; + min_filter = info->min_filter; + mag_filter = info->mag_filter; + mip_filter = info->mip_filter; + } + + inline virtual T sample(glm::vec2 uv, float bias) + { + return sampleLod(uv, bias); + } + + inline virtual T sampleLod(glm::vec2 uv, float lod) + { + if (mag_filter == SPIRV_CROSS_FILTER_NEAREST) + { + uv.x = wrap(uv.x, wrap_s, mips[0].width); + uv.y = wrap(uv.y, wrap_t, mips[0].height); + glm::vec2 uv_full = uv * glm::vec2(mips[0].width, mips[0].height); + + int x = int(uv_full.x); + int y = int(uv_full.y); + return sample(x, y, 0); + } + else + { + return T(0, 0, 0, 1); + } + } + + inline float wrap(float v, spirv_cross_wrap wrap, unsigned size) + { + switch (wrap) + { + case SPIRV_CROSS_WRAP_REPEAT: + return v - glm::floor(v); + case SPIRV_CROSS_WRAP_CLAMP_TO_EDGE: + { + float half = 0.5f / size; + return glm::clamp(v, half, 1.0f - half); + } + + default: + return 0.0f; + } + } + + std::vector mips; + spirv_cross_format format; + spirv_cross_wrap wrap_s; + spirv_cross_wrap wrap_t; + spirv_cross_filter min_filter; + spirv_cross_filter mag_filter; + spirv_cross_mipfilter mip_filter; +}; + +typedef sampler2DBase sampler2D; +typedef sampler2DBase isampler2D; +typedef sampler2DBase usampler2D; + +template +inline T texture(const sampler2DBase &samp, const glm::vec2 &uv, float bias = 0.0f) +{ + return samp.sample(uv, bias); +} +} + +#endif diff --git a/thirdparty/SPIRV-Cross/include/spirv_cross/thread_group.hpp b/thirdparty/SPIRV-Cross/include/spirv_cross/thread_group.hpp new file mode 100644 index 000000000..b21558156 --- /dev/null +++ b/thirdparty/SPIRV-Cross/include/spirv_cross/thread_group.hpp @@ -0,0 +1,114 @@ +/* + * Copyright 2015-2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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_THREAD_GROUP_HPP +#define SPIRV_CROSS_THREAD_GROUP_HPP + +#include +#include +#include + +namespace spirv_cross +{ +template +class ThreadGroup +{ +public: + ThreadGroup(T *impl) + { + for (unsigned i = 0; i < Size; i++) + workers[i].start(&impl[i]); + } + + void run() + { + for (auto &worker : workers) + worker.run(); + } + + void wait() + { + for (auto &worker : workers) + worker.wait(); + } + +private: + struct Thread + { + enum State + { + Idle, + Running, + Dying + }; + State state = Idle; + + void start(T *impl) + { + worker = std::thread([impl, this] { + for (;;) + { + { + std::unique_lock l{ lock }; + cond.wait(l, [this] { return state != Idle; }); + if (state == Dying) + break; + } + + impl->main(); + + std::lock_guard l{ lock }; + state = Idle; + cond.notify_one(); + } + }); + } + + void wait() + { + std::unique_lock l{ lock }; + cond.wait(l, [this] { return state == Idle; }); + } + + void run() + { + std::lock_guard l{ lock }; + state = Running; + cond.notify_one(); + } + + ~Thread() + { + if (worker.joinable()) + { + { + std::lock_guard l{ lock }; + state = Dying; + cond.notify_one(); + } + worker.join(); + } + } + std::thread worker; + std::condition_variable cond; + std::mutex lock; + }; + Thread workers[Size]; +}; +} + +#endif diff --git a/thirdparty/SPIRV-Cross/main.cpp b/thirdparty/SPIRV-Cross/main.cpp new file mode 100644 index 000000000..759d38215 --- /dev/null +++ b/thirdparty/SPIRV-Cross/main.cpp @@ -0,0 +1,2099 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_cpp.hpp" +#include "spirv_cross_util.hpp" +#include "spirv_glsl.hpp" +#include "spirv_hlsl.hpp" +#include "spirv_msl.hpp" +#include "spirv_parser.hpp" +#include "spirv_reflect.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION +#include "gitversion.h" +#endif + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +static inline void THROW(const char *str) +{ + fprintf(stderr, "SPIRV-Cross will abort: %s\n", str); + fflush(stderr); + abort(); +} +#else +#define THROW(x) throw runtime_error(x) +#endif + +struct CLIParser; +struct CLICallbacks +{ + void add(const char *cli, const function &func) + { + callbacks[cli] = func; + } + unordered_map> callbacks; + function error_handler; + function default_handler; +}; + +struct CLIParser +{ + CLIParser(CLICallbacks cbs_, int argc_, char *argv_[]) + : cbs(std::move(cbs_)) + , argc(argc_) + , argv(argv_) + { + } + + bool parse() + { +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS + try +#endif + { + while (argc && !ended_state) + { + const char *next = *argv++; + argc--; + + if (*next != '-' && cbs.default_handler) + { + cbs.default_handler(next); + } + else + { + auto itr = cbs.callbacks.find(next); + if (itr == ::end(cbs.callbacks)) + { + THROW("Invalid argument"); + } + + itr->second(*this); + } + } + + return true; + } +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS + catch (...) + { + if (cbs.error_handler) + { + cbs.error_handler(); + } + return false; + } +#endif + } + + void end() + { + ended_state = true; + } + + uint32_t next_uint() + { + if (!argc) + { + THROW("Tried to parse uint, but nothing left in arguments"); + } + + uint64_t val = stoul(*argv); + if (val > numeric_limits::max()) + { + THROW("next_uint() out of range"); + } + + argc--; + argv++; + + return uint32_t(val); + } + + uint32_t next_hex_uint() + { + if (!argc) + { + THROW("Tried to parse uint, but nothing left in arguments"); + } + + uint64_t val = stoul(*argv, nullptr, 16); + if (val > numeric_limits::max()) + { + THROW("next_uint() out of range"); + } + + argc--; + argv++; + + return uint32_t(val); + } + + double next_double() + { + if (!argc) + { + THROW("Tried to parse double, but nothing left in arguments"); + } + + double val = stod(*argv); + + argc--; + argv++; + + 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) + { + THROW("Tried to parse string, but nothing left in arguments"); + } + + const char *ret = *argv; + argc--; + argv++; + return ret; + } + + CLICallbacks cbs; + int argc; + char **argv; + bool ended_state = false; +}; + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +static vector read_spirv_file_stdin() +{ +#ifdef _WIN32 + setmode(fileno(stdin), O_BINARY); +#endif + + vector buffer; + uint32_t tmp[256]; + size_t ret; + + while ((ret = fread(tmp, sizeof(uint32_t), 256, stdin))) + buffer.insert(buffer.end(), tmp, tmp + ret); + + return buffer; +} + +static vector read_spirv_file(const char *path) +{ + if (path[0] == '-' && path[1] == '\0') + return read_spirv_file_stdin(); + + FILE *file = fopen(path, "rb"); + if (!file) + { + fprintf(stderr, "Failed to open SPIR-V file: %s\n", path); + return {}; + } + + fseek(file, 0, SEEK_END); + long len = ftell(file) / sizeof(uint32_t); + rewind(file); + + vector spirv(len); + if (fread(spirv.data(), sizeof(uint32_t), len, file) != size_t(len)) + spirv.clear(); + + fclose(file); + return spirv; +} + +static bool write_string_to_file(const char *path, const char *string) +{ + FILE *file = fopen(path, "w"); + if (!file) + { + fprintf(stderr, "Failed to write file: %s\n", path); + return false; + } + + fprintf(file, "%s", string); + fclose(file); + return true; +} + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +static void print_resources(const Compiler &compiler, StorageClass storage, + const SmallVector &resources) +{ + fprintf(stderr, "%s\n", storage == StorageClassInput ? "builtin inputs" : "builtin outputs"); + fprintf(stderr, "=============\n\n"); + for (auto &res : resources) + { + bool active = compiler.has_active_builtin(res.builtin, storage); + const char *basetype = "?"; + auto &type = compiler.get_type(res.value_type_id); + switch (type.basetype) + { + case SPIRType::Float: basetype = "float"; break; + case SPIRType::Int: basetype = "int"; break; + case SPIRType::UInt: basetype = "uint"; break; + default: break; + } + + uint32_t array_size = 0; + bool array_size_literal = false; + if (!type.array.empty()) + { + array_size = type.array.front(); + array_size_literal = type.array_size_literal.front(); + } + + string type_str = basetype; + if (type.vecsize > 1) + type_str += std::to_string(type.vecsize); + + if (array_size) + { + if (array_size_literal) + type_str += join("[", array_size, "]"); + else + type_str += join("[", array_size, " (spec constant ID)]"); + } + + string builtin_str; + switch (res.builtin) + { + case BuiltInPosition: builtin_str = "Position"; break; + case BuiltInPointSize: builtin_str = "PointSize"; break; + case BuiltInCullDistance: builtin_str = "CullDistance"; break; + case BuiltInClipDistance: builtin_str = "ClipDistance"; break; + case BuiltInTessLevelInner: builtin_str = "TessLevelInner"; break; + case BuiltInTessLevelOuter: builtin_str = "TessLevelOuter"; break; + default: builtin_str = string("builtin #") + to_string(res.builtin); + } + + fprintf(stderr, "Builtin %s (%s) (active: %s).\n", builtin_str.c_str(), type_str.c_str(), active ? "yes" : "no"); + } + fprintf(stderr, "=============\n\n"); +} + +static void print_resources(const Compiler &compiler, const char *tag, const SmallVector &resources) +{ + fprintf(stderr, "%s\n", tag); + fprintf(stderr, "=============\n\n"); + bool print_ssbo = !strcmp(tag, "ssbos"); + + for (auto &res : resources) + { + auto &type = compiler.get_type(res.type_id); + + if (print_ssbo && compiler.buffer_is_hlsl_counter_buffer(res.id)) + continue; + + // 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 = compiler.get_storage_class(res.id) == StorageClassPushConstant; + bool is_block = compiler.get_decoration_bitset(type.self).get(DecorationBlock) || + compiler.get_decoration_bitset(type.self).get(DecorationBufferBlock); + bool is_sized_block = is_block && (compiler.get_storage_class(res.id) == StorageClassUniform || + compiler.get_storage_class(res.id) == StorageClassUniformConstant); + ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : ID(res.id); + + uint32_t block_size = 0; + uint32_t runtime_array_stride = 0; + if (is_sized_block) + { + auto &base_type = compiler.get_type(res.base_type_id); + block_size = uint32_t(compiler.get_declared_struct_size(base_type)); + runtime_array_stride = uint32_t(compiler.get_declared_struct_size_runtime_array(base_type, 1) - + compiler.get_declared_struct_size_runtime_array(base_type, 0)); + } + + Bitset mask; + if (print_ssbo) + mask = compiler.get_buffer_block_flags(res.id); + else + mask = compiler.get_decoration_bitset(res.id); + + string array; + for (auto arr : type.array) + array = join("[", arr ? convert_to_string(arr) : "", "]") + array; + + fprintf(stderr, " ID %03u : %s%s", uint32_t(res.id), + !res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str()); + + if (mask.get(DecorationLocation)) + fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation)); + if (mask.get(DecorationDescriptorSet)) + fprintf(stderr, " (Set : %u)", compiler.get_decoration(res.id, DecorationDescriptorSet)); + if (mask.get(DecorationBinding)) + fprintf(stderr, " (Binding : %u)", compiler.get_decoration(res.id, DecorationBinding)); + if (static_cast(compiler).variable_is_depth_or_compare(res.id)) + fprintf(stderr, " (comparison)"); + if (mask.get(DecorationInputAttachmentIndex)) + fprintf(stderr, " (Attachment : %u)", compiler.get_decoration(res.id, DecorationInputAttachmentIndex)); + if (mask.get(DecorationNonReadable)) + fprintf(stderr, " writeonly"); + if (mask.get(DecorationNonWritable)) + fprintf(stderr, " readonly"); + if (mask.get(DecorationRestrict)) + fprintf(stderr, " restrict"); + if (mask.get(DecorationCoherent)) + fprintf(stderr, " coherent"); + if (mask.get(DecorationVolatile)) + fprintf(stderr, " volatile"); + if (is_sized_block) + { + fprintf(stderr, " (BlockSize : %u bytes)", block_size); + if (runtime_array_stride) + fprintf(stderr, " (Unsized array stride: %u bytes)", runtime_array_stride); + } + + uint32_t counter_id = 0; + if (print_ssbo && compiler.buffer_get_hlsl_counter_buffer(res.id, counter_id)) + fprintf(stderr, " (HLSL counter buffer ID: %u)", counter_id); + fprintf(stderr, "\n"); + } + fprintf(stderr, "=============\n\n"); +} + +static const char *execution_model_to_str(ExecutionModel model) +{ + switch (model) + { + case ExecutionModelVertex: + return "vertex"; + case ExecutionModelTessellationControl: + return "tessellation control"; + case ExecutionModelTessellationEvaluation: + return "tessellation evaluation"; + case ExecutionModelGeometry: + return "geometry"; + case ExecutionModelFragment: + return "fragment"; + case ExecutionModelGLCompute: + return "compute"; + case ExecutionModelRayGenerationNV: + return "raygenNV"; + case ExecutionModelIntersectionNV: + return "intersectionNV"; + case ExecutionModelCallableNV: + return "callableNV"; + case ExecutionModelAnyHitNV: + return "anyhitNV"; + case ExecutionModelClosestHitNV: + return "closesthitNV"; + case ExecutionModelMissNV: + return "missNV"; + default: + return "???"; + } +} + +static void print_resources(const Compiler &compiler, const ShaderResources &res) +{ + auto &modes = compiler.get_execution_mode_bitset(); + + fprintf(stderr, "Entry points:\n"); + auto entry_points = compiler.get_entry_points_and_stages(); + for (auto &e : entry_points) + fprintf(stderr, " %s (%s)\n", e.name.c_str(), execution_model_to_str(e.execution_model)); + fprintf(stderr, "\n"); + + fprintf(stderr, "Execution modes:\n"); + modes.for_each_bit([&](uint32_t i) { + auto mode = static_cast(i); + uint32_t arg0 = compiler.get_execution_mode_argument(mode, 0); + uint32_t arg1 = compiler.get_execution_mode_argument(mode, 1); + uint32_t arg2 = compiler.get_execution_mode_argument(mode, 2); + + switch (static_cast(i)) + { + case ExecutionModeInvocations: + fprintf(stderr, " Invocations: %u\n", arg0); + break; + + case ExecutionModeLocalSize: + fprintf(stderr, " LocalSize: (%u, %u, %u)\n", arg0, arg1, arg2); + break; + + case ExecutionModeOutputVertices: + fprintf(stderr, " OutputVertices: %u\n", arg0); + break; + +#define CHECK_MODE(m) \ + case ExecutionMode##m: \ + fprintf(stderr, " %s\n", #m); \ + break + CHECK_MODE(SpacingEqual); + CHECK_MODE(SpacingFractionalEven); + CHECK_MODE(SpacingFractionalOdd); + CHECK_MODE(VertexOrderCw); + CHECK_MODE(VertexOrderCcw); + CHECK_MODE(PixelCenterInteger); + CHECK_MODE(OriginUpperLeft); + CHECK_MODE(OriginLowerLeft); + CHECK_MODE(EarlyFragmentTests); + CHECK_MODE(PointMode); + CHECK_MODE(Xfb); + CHECK_MODE(DepthReplacing); + CHECK_MODE(DepthGreater); + CHECK_MODE(DepthLess); + CHECK_MODE(DepthUnchanged); + CHECK_MODE(LocalSizeHint); + CHECK_MODE(InputPoints); + CHECK_MODE(InputLines); + CHECK_MODE(InputLinesAdjacency); + CHECK_MODE(Triangles); + CHECK_MODE(InputTrianglesAdjacency); + CHECK_MODE(Quads); + CHECK_MODE(Isolines); + CHECK_MODE(OutputPoints); + CHECK_MODE(OutputLineStrip); + CHECK_MODE(OutputTriangleStrip); + CHECK_MODE(VecTypeHint); + CHECK_MODE(ContractionOff); + + default: + break; + } + }); + fprintf(stderr, "\n"); + + print_resources(compiler, "subpass inputs", res.subpass_inputs); + print_resources(compiler, "inputs", res.stage_inputs); + print_resources(compiler, "outputs", res.stage_outputs); + print_resources(compiler, "textures", res.sampled_images); + print_resources(compiler, "separate images", res.separate_images); + print_resources(compiler, "separate samplers", res.separate_samplers); + print_resources(compiler, "images", res.storage_images); + print_resources(compiler, "ssbos", res.storage_buffers); + print_resources(compiler, "ubos", res.uniform_buffers); + print_resources(compiler, "push", res.push_constant_buffers); + print_resources(compiler, "counters", res.atomic_counters); + print_resources(compiler, "acceleration structures", res.acceleration_structures); + print_resources(compiler, "tensors", res.tensors); + print_resources(compiler, "record buffers", res.shader_record_buffers); + print_resources(compiler, StorageClassInput, res.builtin_inputs); + print_resources(compiler, StorageClassOutput, res.builtin_outputs); +} + +static void print_push_constant_resources(const Compiler &compiler, const SmallVector &res) +{ + for (auto &block : res) + { + auto ranges = compiler.get_active_buffer_ranges(block.id); + fprintf(stderr, "Active members in buffer: %s\n", + !block.name.empty() ? block.name.c_str() : compiler.get_fallback_name(block.id).c_str()); + + fprintf(stderr, "==================\n\n"); + for (auto &range : ranges) + { + const auto &name = compiler.get_member_name(block.base_type_id, range.index); + + fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index, + !name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(), + unsigned(range.offset), unsigned(range.range)); + } + fprintf(stderr, "==================\n\n"); + } +} + +static void print_spec_constants(const Compiler &compiler) +{ + auto spec_constants = compiler.get_specialization_constants(); + fprintf(stderr, "Specialization constants\n"); + fprintf(stderr, "==================\n\n"); + for (auto &c : spec_constants) + fprintf(stderr, "ID: %u, Spec ID: %u\n", uint32_t(c.id), c.constant_id); + fprintf(stderr, "==================\n\n"); +} + +static void print_capabilities_and_extensions(const Compiler &compiler) +{ + fprintf(stderr, "Capabilities\n"); + fprintf(stderr, "============\n"); + for (auto &capability : compiler.get_declared_capabilities()) + fprintf(stderr, "Capability: %u\n", static_cast(capability)); + fprintf(stderr, "============\n\n"); + + fprintf(stderr, "Extensions\n"); + fprintf(stderr, "============\n"); + for (auto &ext : compiler.get_declared_extensions()) + fprintf(stderr, "Extension: %s\n", ext.c_str()); + fprintf(stderr, "============\n\n"); +} + +struct PLSArg +{ + PlsFormat format; + string name; +}; + +struct Remap +{ + string src_name; + string dst_name; + unsigned components; +}; + +struct VariableTypeRemap +{ + string variable_name; + string new_variable_type; +}; + +struct InterfaceVariableRename +{ + StorageClass storageClass; + uint32_t location; + string variable_name; +}; + +struct HLSLVertexAttributeRemapNamed +{ + std::string name; + std::string semantic; +}; + +struct CLIArguments +{ + const char *input = nullptr; + const char *output = nullptr; + const char *cpp_interface_name = nullptr; + uint32_t version = 0; + uint32_t shader_model = 0; + uint32_t msl_version = 0; + bool es = false; + bool set_version = false; + bool set_shader_model = false; + bool set_msl_version = false; + bool set_es = false; + bool dump_resources = false; + bool force_temporary = false; + bool flatten_ubo = false; + bool fixup = false; + bool yflip = false; + bool sso = false; + bool support_nonzero_baseinstance = true; + bool msl_capture_output_to_buffer = false; + bool msl_swizzle_texture_samples = false; + bool msl_ios = false; + bool msl_pad_fragment_output = false; + bool msl_domain_lower_left = false; + bool msl_argument_buffers = false; + uint32_t msl_argument_buffers_tier = 0; // Tier 1 + bool msl_texture_buffer_native = false; + bool msl_framebuffer_fetch = false; + bool msl_invariant_float_math = false; + bool msl_emulate_cube_array = false; + bool msl_multiview = false; + bool msl_multiview_layered_rendering = true; + bool msl_view_index_from_device_index = false; + bool msl_dispatch_base = false; + bool msl_decoration_binding = false; + bool msl_force_active_argument_buffer_resources = false; + bool msl_force_native_arrays = false; + bool msl_enable_frag_depth_builtin = true; + bool msl_enable_frag_stencil_ref_builtin = true; + uint32_t msl_enable_frag_output_mask = 0xffffffff; + bool msl_enable_clip_distance_user_varying = true; + bool msl_raw_buffer_tese_input = false; + bool msl_multi_patch_workgroup = false; + bool msl_vertex_for_tessellation = false; + uint32_t msl_additional_fixed_sample_mask = 0xffffffff; + bool msl_arrayed_subpass_input = false; + uint32_t msl_r32ui_linear_texture_alignment = 4; + uint32_t msl_r32ui_alignment_constant_id = 65535; + bool msl_texture_1d_as_2d = false; + bool msl_ios_use_simdgroup_functions = false; + bool msl_emulate_subgroups = false; + uint32_t msl_fixed_subgroup_size = 0; + bool msl_force_sample_rate_shading = false; + bool msl_manual_helper_invocation_updates = true; + bool msl_check_discarded_frag_stores = false; + bool msl_force_fragment_with_side_effects_execution = false; + bool msl_sample_dref_lod_array_as_grad = false; + bool msl_runtime_array_rich_descriptor = false; + bool msl_replace_recursive_inputs = false; + bool msl_readwrite_texture_fences = true; + bool msl_agx_manual_cube_grad_fixup = false; + bool msl_input_attachment_is_ds_attachment = false; + bool msl_disable_rasterization = false; + bool msl_auto_disable_rasterization = false; + bool msl_enable_point_size_default = false; + float msl_default_point_size = 1.0f; + const char *msl_combined_sampler_suffix = nullptr; + bool glsl_emit_push_constant_as_ubo = false; + bool glsl_emit_ubo_as_plain_uniforms = false; + bool glsl_force_flattened_io_blocks = false; + uint32_t glsl_ovr_multiview_view_count = 0; + SmallVector> glsl_ext_framebuffer_fetch; + bool glsl_ext_framebuffer_fetch_noncoherent = false; + uint32_t glsl_descriptor_heap_set = UINT32_MAX; + uint32_t glsl_descriptor_heap_binding = UINT32_MAX; + bool vulkan_glsl_disable_ext_samplerless_texture_functions = false; + bool emit_line_directives = false; + bool enable_storage_image_qualifier_deduction = true; + bool force_zero_initialized_variables = false; + bool relax_nan_checks = false; + uint32_t force_recompile_max_debug_iterations = 3; + SmallVector msl_discrete_descriptor_sets; + SmallVector msl_device_argument_buffers; + SmallVector> msl_dynamic_buffers; + SmallVector> msl_inline_uniform_blocks; + SmallVector msl_shader_inputs; + SmallVector msl_shader_outputs; + SmallVector pls_in; + SmallVector pls_out; + SmallVector remaps; + SmallVector extensions; + SmallVector variable_type_remaps; + SmallVector interface_variable_renames; + SmallVector hlsl_attr_remap; + SmallVector hlsl_attr_remap_named; + SmallVector> masked_stage_outputs; + SmallVector masked_stage_builtins; + string entry; + string entry_stage; + + struct Rename + { + string old_name; + string new_name; + ExecutionModel execution_model; + }; + SmallVector entry_point_rename; + + uint32_t iterations = 1; + bool cpp = false; + string reflect; + bool msl = false; + bool hlsl = false; + bool hlsl_compat = false; + + bool hlsl_support_nonzero_base = false; + bool hlsl_base_vertex_index_explicit_binding = false; + uint32_t hlsl_base_vertex_index_register_index = 0; + uint32_t hlsl_base_vertex_index_register_space = 0; + + bool hlsl_force_storage_buffer_as_uav = false; + bool hlsl_nonwritable_uav_texture_as_srv = false; + bool hlsl_enable_16bit_types = false; + bool hlsl_flatten_matrix_vertex_input_semantics = false; + bool hlsl_preserve_structured_buffers = false; + bool hlsl_user_semantic = false; + HLSLBindingFlags hlsl_binding_flags = 0; + bool vulkan_semantics = false; + bool flatten_multidimensional_arrays = false; + bool use_420pack_extension = true; + bool remove_unused = false; + bool combined_samplers_inherit_bindings = false; +}; + +static void print_version() +{ +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION + fprintf(stderr, "%s\n", SPIRV_CROSS_GIT_REVISION); +#else + fprintf(stderr, "Git revision unknown. Build with CMake to create timestamp and revision info.\n"); +#endif +} + +static void print_help_backend() +{ + // clang-format off + fprintf(stderr, "\nSelect backend:\n" + "\tBy default, OpenGL-style GLSL is the target, with #version and GLSL/ESSL information inherited from the SPIR-V module if present.\n" + "\t[--vulkan-semantics] or [-V]:\n\t\tEmit Vulkan GLSL instead of plain GLSL. Makes use of Vulkan-only features to match SPIR-V.\n" + "\t[--msl]:\n\t\tEmit Metal Shading Language (MSL).\n" + "\t[--hlsl]:\n\t\tEmit HLSL.\n" + "\t[--reflect]:\n\t\tEmit JSON reflection.\n" + "\t[--cpp]:\n\t\tDEPRECATED. Emits C++ code.\n" + ); + // clang-format on +} + +static void print_help_glsl() +{ + // clang-format off + fprintf(stderr, "\nGLSL options:\n" + "\t[--es]:\n\t\tForce ESSL.\n" + "\t[--no-es]:\n\t\tForce desktop GLSL.\n" + "\t[--version ]:\n\t\tE.g. --version 450 will emit '#version 450' in shader.\n" + "\t\tCode generation will depend on the version used.\n" + "\t[--flatten-ubo]:\n\t\tEmit UBOs as plain uniform arrays which are suitable for use with glUniform4*v().\n" + "\t\tThis can be an optimization on GL implementations where this is faster or works around buggy driver implementations.\n" + "\t\tE.g.: uniform MyUBO { vec4 a; float b, c, d, e; }; will be emitted as uniform vec4 MyUBO[2];\n" + "\t\tCaveat: You cannot mix and match floating-point and integer in the same UBO with this option.\n" + "\t\tLegacy GLSL/ESSL (where this flattening makes sense) does not support bit-casting, which would have been the obvious workaround.\n" + "\t[--extension ext]:\n\t\tAdd #extension string of your choosing to GLSL output.\n" + "\t\tUseful if you use variable name remapping to something that requires an extension unknown to SPIRV-Cross.\n" + "\t[--remove-unused-variables]:\n\t\tDo not emit interface variables which are not statically accessed by the shader.\n" + "\t[--separate-shader-objects]:\n\t\tRedeclare gl_PerVertex blocks to be suitable for desktop GL separate shader objects.\n" + "\t[--glsl-emit-push-constant-as-ubo]:\n\t\tInstead of a plain uniform of struct for push constants, emit a UBO block instead.\n" + "\t[--glsl-emit-ubo-as-plain-uniforms]:\n\t\tInstead of emitting UBOs, emit them as plain uniform structs.\n" + "\t[--glsl-remap-ext-framebuffer-fetch input-attachment color-location]:\n\t\tRemaps an input attachment to use GL_EXT_shader_framebuffer_fetch.\n" + "\t\tgl_LastFragData[location] is read from. The attachment to read from must be declared as an output in the shader.\n" + "\t[--glsl-ext-framebuffer-fetch-noncoherent]:\n\t\tUses noncoherent qualifier for framebuffer fetch.\n" + "\t[--vulkan-glsl-disable-ext-samplerless-texture-functions]:\n\t\tDo not allow use of GL_EXT_samperless_texture_functions, even in Vulkan GLSL.\n" + "\t\tUse of texelFetch and similar might have to create dummy samplers to work around it.\n" + "\t[--combined-samplers-inherit-bindings]:\n\t\tInherit binding information from the textures when building combined image samplers from separate textures and samplers.\n" + "\t[--no-support-nonzero-baseinstance]:\n\t\tWhen using gl_InstanceIndex with desktop GL,\n" + "\t\tassume that base instance is always 0, and do not attempt to fix up gl_InstanceID to match Vulkan semantics.\n" + "\t[--pls-in format input-name]:\n\t\tRemaps a subpass input with name into a GL_EXT_pixel_local_storage input.\n" + "\t\tEntry in PLS block is ordered where first --pls-in marks the first entry. Can be called multiple times.\n" + "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n" + "\t\tRequires ESSL.\n" + "\t[--pls-out format output-name]:\n\t\tRemaps a color output with name into a GL_EXT_pixel_local_storage output.\n" + "\t\tEntry in PLS block is ordered where first --pls-output marks the first entry. Can be called multiple times.\n" + "\t\tFormats allowed: r11f_g11f_b10f, r32f, rg16f, rg16, rgb10_a2, rgba8, rgba8i, rgba8ui, rg16i, rgb10_a2ui, rg16ui, r32ui.\n" + "\t\tRequires ESSL.\n" + "\t[--remap source_name target_name components]:\n\t\tRemaps a variable to a different name with N components.\n" + "\t\tMain use case is to remap a subpass input to gl_LastFragDepthARM.\n" + "\t\tE.g.:\n" + "\t\tuniform subpassInput uDepth;\n" + "\t\t--remap uDepth gl_LastFragDepthARM 1 --extension GL_ARM_shader_framebuffer_fetch_depth_stencil\n" + "\t[--no-420pack-extension]:\n\t\tDo not make use of GL_ARB_shading_language_420pack in older GL targets to support layout(binding).\n" + "\t[--remap-variable-type ]:\n\t\tRemaps a variable type based on name.\n" + "\t\tPrimary use case is supporting external samplers in ESSL for video rendering on Android where you could remap a texture to a YUV one.\n" + "\t[--glsl-force-flattened-io-blocks]:\n\t\tAlways flatten I/O blocks and structs.\n" + "\t[--glsl-ovr-multiview-view-count count]:\n\t\tIn GL_OVR_multiview2, specify layout(num_views).\n" + "\t[--glsl-descriptor-heap-set-binding desc_set binding]:\n\t\tInstead of layout(descriptor_heap), emit layout(set = desc_set, binding = binding) instead for compatibility with mapping API.\n" + ); + // clang-format on +} + +static void print_help_hlsl() +{ + // clang-format off + fprintf(stderr, "\nHLSL options:\n" + "\t[--shader-model]:\n\t\tEnables a specific shader model, e.g. --shader-model 50 for SM 5.0.\n" + "\t[--flatten-ubo]:\n\t\tEmit UBOs as plain uniform arrays.\n" + "\t\tE.g.: uniform MyUBO { vec4 a; float b, c, d, e; }; will be emitted as uniform float4 MyUBO[2];\n" + "\t\tCaveat: You cannot mix and match floating-point and integer in the same UBO with this option.\n" + "\t[--hlsl-enable-compat]:\n\t\tAllow point size and point coord to be used, even if they won't work as expected.\n" + "\t\tPointSize is ignored, and PointCoord returns (0.5, 0.5).\n" + "\t[--hlsl-support-nonzero-basevertex-baseinstance]:\n\t\tSupport base vertex and base instance by emitting a special cbuffer declared as:\n" + "\t\tcbuffer SPIRV_Cross_VertexInfo { int SPIRV_Cross_BaseVertex; int SPIRV_Cross_BaseInstance; };\n" + "\t[--hlsl-basevertex-baseinstance-binding ]:\n\t\tAssign a fixed binding to SPIRV_Cross_VertexInfo.\n" + "\t[--hlsl-auto-binding (push, cbv, srv, uav, sampler, all)]\n" + "\t\tDo not emit any : register(#) bindings for specific resource types, and rely on HLSL compiler to assign something.\n" + "\t[--hlsl-force-storage-buffer-as-uav]:\n\t\tAlways emit SSBOs as UAVs, even when marked as read-only.\n" + "\t\tNormally, SSBOs marked with NonWritable will be emitted as SRVs.\n" + "\t[--hlsl-nonwritable-uav-texture-as-srv]:\n\t\tEmit NonWritable storage images as SRV textures instead of UAV.\n" + "\t\tUsing this option messes with the type system. SPIRV-Cross cannot guarantee that this will work.\n" + "\t\tOne major problem area with this feature is function arguments, where we won't know if we're seeing a UAV or SRV.\n" + "\t\tShader must ensure that read/write state is consistent at all call sites.\n" + "\t[--set-hlsl-vertex-input-semantic ]:\n\t\tEmits a specific vertex input semantic for a given location.\n" + "\t\tOtherwise, TEXCOORD# is used as semantics, where # is location.\n" + "\t[--set-hlsl-named-vertex-input-semantic ]:\n\t\tEmits a specific vertex input semantic for a given name.\n" + "\t\tOpName reflection information must be intact.\n" + "\t[--hlsl-enable-16bit-types]:\n\t\tEnables native use of half/int16_t/uint16_t and ByteAddressBuffer interaction with these types. Requires SM 6.2.\n" + "\t[--hlsl-flatten-matrix-vertex-input-semantics]:\n\t\tEmits matrix vertex inputs with input semantics as if they were independent vectors, e.g. TEXCOORD{2,3,4} rather than matrix form TEXCOORD2_{0,1,2}.\n" + "\t[--hlsl-preserve-structured-buffers]:\n\t\tEmit SturucturedBuffer rather than ByteAddressBuffer. Requires UserTypeGOOGLE to be emitted. Intended for DXC roundtrips.\n" + "\t[--hlsl-user-semantic]:\n\t\tUses UserSemantic decoration to generate vertex input and output semantics.\n" + ); + // clang-format on +} + +static void print_help_msl() +{ + // clang-format off + fprintf(stderr, "\nMSL options:\n" + "\t[--msl-version ]:\n\t\tUses a specific MSL version, e.g. --msl-version 20100 for MSL 2.1.\n" + "\t[--msl-capture-output]:\n\t\tWrites geometry varyings to a buffer instead of as stage-outputs.\n" + "\t[--msl-swizzle-texture-samples]:\n\t\tWorks around lack of support for VkImageView component swizzles.\n" + "\t\tThis has a massive impact on performance and bloat. Do not use this unless you are absolutely forced to.\n" + "\t\tTo use this feature, the API side must pass down swizzle buffers.\n" + "\t\tShould only be used by translation layers as a last resort.\n" + "\t\tRecent Metal versions do not require this workaround.\n" + "\t[--msl-ios]:\n\t\tTarget iOS Metal instead of macOS Metal.\n" + "\t[--msl-pad-fragment-output]:\n\t\tAlways emit color outputs as 4-component variables.\n" + "\t\tIn Metal, the fragment shader must emit at least as many components as the render target format.\n" + "\t[--msl-domain-lower-left]:\n\t\tUse a lower-left tessellation domain.\n" + "\t[--msl-argument-buffers]:\n\t\tEmit Metal argument buffers instead of discrete resource bindings.\n" + "\t\tRequires MSL 2.0 to be enabled.\n" + "\t[--msl-argument-buffer-tier]:\n\t\tWhen using Metal argument buffers, indicate the Metal argument buffer tier level supported by the Metal platform.\n" + "\t\tUses same values as Metal MTLArgumentBuffersTier enumeration (0 = Tier1, 1 = Tier2).\n" + "\t\tNOTE: Setting this value no longer enables msl-argument-buffers implicitly.\n" + "\t[--msl-runtime-array-rich-descriptor]:\n\t\tWhen declaring a runtime array of SSBOs, declare an array of {ptr, len} pairs to support OpArrayLength.\n" + "\t[--msl-replace-recursive-inputs]:\n\t\tWorks around a Metal 3.1 regression bug, which causes an infinite recursion crash during Metal's analysis of an entry point input structure that itself contains internal recursion.\n" + "\t[--msl-texture-buffer-native]:\n\t\tEnable native support for texel buffers. Otherwise, it is emulated as a normal texture.\n" + "\t[--msl-input-attachment-is-ds-attachment]:\n\t\tAdds a simple depth passthrough in fragment shaders when they do not modify the depth value.\n" + "\t\tRequired to force Metal to write to the depth/stencil attachment post fragment execution.\n" + "\t\tOtherwise, Metal may optimize the write to pre fragment execution which goes against the Vulkan spec.\n" + "\t\tOnly required if an input attachment and depth/stencil attachment reference the same resource.\n" + "\t[--msl-framebuffer-fetch]:\n\t\tImplement subpass inputs with frame buffer fetch.\n" + "\t\tEmits [[color(N)]] inputs in fragment stage.\n" + "\t\tRequires an Apple GPU.\n" + "\t[--msl-emulate-cube-array]:\n\t\tEmulate cube arrays with 2D array and manual math.\n" + "\t[--msl-discrete-descriptor-set ]:\n\t\tWhen using argument buffers, forces a specific descriptor set to be implemented without argument buffers.\n" + "\t\tUseful for implementing push descriptors in emulation layers.\n" + "\t\tCan be used multiple times for each descriptor set in question.\n" + "\t[--msl-device-argument-buffer ]:\n\t\tUse device address space to hold indirect argument buffers instead of constant.\n" + "\t\tComes up when trying to support argument buffers which are larger than 64 KiB.\n" + "\t[--msl-multiview]:\n\t\tEnable SPV_KHR_multiview emulation.\n" + "\t[--msl-multiview-no-layered-rendering]:\n\t\tDon't set [[render_target_array_index]] in multiview shaders.\n" + "\t\tUseful for devices which don't support layered rendering. Only effective when --msl-multiview is enabled.\n" + "\t[--msl-view-index-from-device-index]:\n\t\tTreat the view index as the device index instead.\n" + "\t\tFor multi-GPU rendering.\n" + "\t[--msl-dispatch-base]:\n\t\tAdd support for vkCmdDispatchBase() or similar APIs.\n" + "\t\tOffsets the workgroup ID based on a buffer.\n" + "\t[--msl-dynamic-buffer ]:\n\t\tMarks a buffer as having dynamic offset.\n" + "\t\tThe offset is applied in the shader with pointer arithmetic.\n" + "\t\tUseful for argument buffers where it is non-trivial to apply dynamic offset otherwise.\n" + "\t[--msl-inline-uniform-block ]:\n\t\tIn argument buffers, mark an UBO as being an inline uniform block which is embedded into the argument buffer itself.\n" + "\t[--msl-decoration-binding]:\n\t\tUse SPIR-V bindings directly as MSL bindings.\n" + "\t\tThis does not work in the general case as there is no descriptor set support, and combined image samplers are split up.\n" + "\t\tHowever, if the shader author knows of binding limitations, this option will avoid the need for reflection on Metal side.\n" + "\t[--msl-force-active-argument-buffer-resources]:\n\t\tAlways emit resources which are part of argument buffers.\n" + "\t\tThis makes sure that similar shaders with same resource declarations can share the argument buffer as declaring an argument buffer implies an ABI.\n" + "\t[--msl-force-native-arrays]:\n\t\tRather than implementing array types as a templated value type ala std::array, use plain, native arrays.\n" + "\t\tThis will lead to worse code-gen, but can work around driver bugs on certain driver revisions of certain Intel-based Macbooks where template arrays break.\n" + "\t[--msl-disable-frag-depth-builtin]:\n\t\tDisables FragDepth output. Useful if pipeline does not enable depth, as pipeline creation might otherwise fail.\n" + "\t[--msl-disable-frag-stencil-ref-builtin]:\n\t\tDisable FragStencilRef output. Useful if pipeline does not enable stencil output, as pipeline creation might otherwise fail.\n" + "\t[--msl-enable-frag-output-mask ]:\n\t\tOnly selectively enable fragment outputs. Useful if pipeline does not enable fragment output for certain locations, as pipeline creation might otherwise fail.\n" + "\t[--msl-no-clip-distance-user-varying]:\n\t\tDo not emit user varyings to emulate gl_ClipDistance in fragment shaders.\n" + "\t[--msl-add-shader-input ]:\n\t\tSpecify the format of the shader input at .\n" + "\t\t can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, " + "or other-typed variable. is the vector length of the variable, which must be greater than or equal to that declared in the shader. can be 'vertex', " + "'primitive', or 'patch' to indicate a per-vertex, per-primitive, or per-patch variable.\n" + "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n" + "\t[--msl-add-shader-output ]:\n\t\tSpecify the format of the shader output at .\n" + "\t\t can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, " + "or other-typed variable. is the vector length of the variable, which must be greater than or equal to that declared in the shader. can be 'vertex', " + "'primitive', or 'patch' to indicate a per-vertex, per-primitive, or per-patch variable.\n" + "\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n" + "\t[--msl-shader-input ]:\n\t\tSpecify the format of the shader input at .\n" + "\t\t can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, " + "or other-typed variable. is the vector length of the variable, which must be greater than or equal to that declared in the shader." + "\t\tEquivalent to --msl-add-shader-input with a rate of 'vertex'.\n" + "\t[--msl-shader-output ]:\n\t\tSpecify the format of the shader output at .\n" + "\t\t can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, " + "or other-typed variable. is the vector length of the variable, which must be greater than or equal to that declared in the shader." + "\t\tEquivalent to --msl-add-shader-output with a rate of 'vertex'.\n" + "\t[--msl-raw-buffer-tese-input]:\n\t\tUse raw buffers for tessellation evaluation input.\n" + "\t\tThis allows the use of nested structures and arrays.\n" + "\t\tIn a future version of SPIRV-Cross, this will become the default.\n" + "\t[--msl-multi-patch-workgroup]:\n\t\tUse the new style of tessellation control processing, where multiple patches are processed per workgroup.\n" + "\t\tThis should increase throughput by ensuring all the GPU's SIMD lanes are occupied, but it is not compatible with the old style.\n" + "\t\tIn addition, this style also passes input variables in buffers directly instead of using vertex attribute processing.\n" + "\t\tIn a future version of SPIRV-Cross, this will become the default.\n" + "\t[--msl-vertex-for-tessellation]:\n\t\tWhen handling a vertex shader, marks it as one that will be used with a new-style tessellation control shader.\n" + "\t\tThe vertex shader is output to MSL as a compute kernel which outputs vertices to the buffer in the order they are received, rather than in index order as with --msl-capture-output normally.\n" + "\t[--msl-additional-fixed-sample-mask ]:\n" + "\t\tSet an additional fixed sample mask. If the shader outputs a sample mask, then the final sample mask will be a bitwise AND of the two.\n" + "\t[--msl-arrayed-subpass-input]:\n\t\tAssume that images of dimension SubpassData have multiple layers. Layered input attachments are accessed relative to BuiltInLayer.\n" + "\t\tThis option has no effect if multiview is also enabled.\n" + "\t[--msl-r32ui-linear-texture-align ]:\n\t\tThe required alignment of linear textures of format MTLPixelFormatR32Uint.\n" + "\t\tThis is used to align the row stride for atomic accesses to such images.\n" + "\t[--msl-r32ui-linear-texture-align-constant-id ]:\n\t\tThe function constant ID to use for the linear texture alignment.\n" + "\t\tOn MSL 1.2 or later, you can override the alignment by setting this function constant.\n" + "\t[--msl-texture-1d-as-2d]:\n\t\tEmit Image variables of dimension Dim1D as texture2d.\n" + "\t\tIn Metal, 1D textures do not support all features that 2D textures do. Use this option if your code relies on these features.\n" + "\t[--msl-ios-use-simdgroup-functions]:\n\t\tUse simd_*() functions for subgroup ops instead of quad_*().\n" + "\t\tRecent Apple GPUs support SIMD-groups larger than a quad. Use this option to take advantage of this support.\n" + "\t[--msl-emulate-subgroups]:\n\t\tAssume subgroups of size 1.\n" + "\t\tIntended for Vulkan Portability implementations where Metal support for SIMD-groups is insufficient for true subgroups.\n" + "\t[--msl-fixed-subgroup-size ]:\n\t\tAssign a constant to the SubgroupSize builtin.\n" + "\t\tIntended for Vulkan Portability implementations where VK_EXT_subgroup_size_control is not supported or disabled.\n" + "\t\tIf 0, assume variable subgroup size as actually exposed by Metal.\n" + "\t[--msl-force-sample-rate-shading]:\n\t\tForce fragment shaders to run per sample.\n" + "\t\tThis adds a [[sample_id]] parameter if none is already present.\n" + "\t[--msl-no-manual-helper-invocation-updates]:\n\t\tDo not manually update the HelperInvocation builtin when a fragment is discarded.\n" + "\t\tSome Metal devices have a bug where simd_is_helper_thread() does not return true\n" + "\t\tafter the fragment is discarded. This behavior is required by Vulkan and SPIR-V, however.\n" + "\t[--msl-check-discarded-frag-stores]:\n\t\tAdd additional checks to resource stores in a fragment shader.\n" + "\t\tSome Metal devices have a bug where stores to resources from a fragment shader\n" + "\t\tcontinue to execute, even when the fragment is discarded. These checks\n" + "\t\tprevent these stores from executing.\n" + "\t[--msl-force-frag-execution]:\n\t\tEnforces fragment execution to avoid early discard by Metal\n" + "\t\tMetal will prematurely discard fragments before execution when side effects are present.\n" + "\t\tThis condition is triggered under the following conditions (side effect operations happen before discard):\n" + "\t\t\t1. Pre fragment depth test fails.\n" + "\t\t\t2. Modify depth value in fragment shader to constant value known at compile time.\n" + "\t\t\t3. Constant value will not pass post fragment depth test.\n" + "\t\t\t4. Fragment is always discarded in fragment execution.\n" + "\t\tHowever, Vulkan expects fragment shader to be executed since it cannot be discarded until the discard\n" + "\t\tpresent in the fragment execution, which would also execute the operations with side effects.\n" + "\t[--msl-sample-dref-lod-array-as-grad]:\n\t\tUse a gradient instead of a level argument.\n" + "\t\tSome Metal devices have a bug where the level() argument to\n" + "\t\tdepth2d_array::sample_compare() in a fragment shader is biased by some\n" + "\t\tunknown amount. This prevents the bias from being added.\n" + "\t[--msl-no-readwrite-texture-fences]:\n\t\tDo not insert fences before each read of a\n" + "\t\tread_write texture. MSL does not guarantee coherence between writes and later reads\n" + "\t\tof read_write textures. If you don't rely on this, you can disable this for a\n" + "\t\tpossible performance improvement.\n" + "\t[--msl-agx-manual-cube-grad-fixup]:\n\t\tManually transform cube texture gradients.\n" + "\t\tAll released Apple Silicon GPUs to date ignore one of the three partial derivatives\n" + "\t\tbased on the selected major axis, and expect the remaining derivatives to be\n" + "\t\tpartially transformed. This fixup gives correct results on Apple Silicon.\n" + "\t[--msl-combined-sampler-suffix ]:\n\t\tUses a custom suffix for combined samplers.\n" + "\t[--msl-disable-rasterization]:\n\t\tDisables rasterization and returns void from vertex-like entry points.\n" + "\t[--msl-auto-disable-rasterization]:\n\t\tDisables rasterization if BuiltInPosition is not written.\n" + "\t[--msl-default-point-size ]:\n\t\tApplies a default value if BuiltInPointSize is not written.\n"); + // clang-format on +} + +static void print_help_common() +{ + // clang-format off + fprintf(stderr, "\nCommon options:\n" + "\t[--entry name]:\n\t\tUse a specific entry point. By default, the first entry point in the module is used.\n" + "\t[--stage ]:\n\t\tForces use of a certain shader stage.\n" + "\t\tCan disambiguate the entry point if more than one entry point exists with same name, but different stage.\n" + "\t[--emit-line-directives]:\n\t\tIf SPIR-V has OpLine directives, aim to emit those accurately in output code as well.\n" + "\t[--rename-entry-point ]:\n\t\tRenames an entry point from what is declared in SPIR-V to code output.\n" + "\t\tMostly relevant for HLSL or MSL.\n" + "\t[--rename-interface-variable ]:\n\t\tRename an interface variable based on location decoration.\n" + "\t[--force-zero-initialized-variables]:\n\t\tForces temporary variables to be initialized to zero.\n" + "\t\tCan be useful in environments where compilers do not allow potentially uninitialized variables.\n" + "\t\tThis usually comes up with Phi temporaries.\n" + "\t[--fixup-clipspace]:\n\t\tFixup Z clip-space at the end of a vertex shader. The behavior is backend-dependent.\n" + "\t\tGLSL: Rewrites [0, w] Z range (D3D/Metal/Vulkan) to GL-style [-w, w].\n" + "\t\tHLSL/MSL: Rewrites [-w, w] Z range (GL) to D3D/Metal/Vulkan-style [0, w].\n" + "\t[--flip-vert-y]:\n\t\tInverts gl_Position.y (or equivalent) at the end of a vertex shader. This is equivalent to using negative viewport height.\n" + "\t[--mask-stage-output-location ]:\n" + "\t\tIf a stage output variable with matching location and component is active, optimize away the variable if applicable.\n" + "\t[--mask-stage-output-builtin ]:\n" + "\t\tIf a stage output variable with matching builtin is active, " + "optimize away the variable if it can affect cross-stage linking correctness.\n" + "\t[--relax-nan-checks]:\n\t\tRelax NaN checks for N{Clamp,Min,Max} and ordered vs. unordered compare instructions.\n" + ); + // clang-format on +} + +static void print_help_obscure() +{ + // clang-format off + fprintf(stderr, "\nObscure options:\n" + "\tThese options are not meant to be used on a regular basis. They have some occasional uses in the test suite.\n" + + "\t[--force-temporary]:\n\t\tAggressively emit temporary expressions instead of forwarding expressions. Very rarely used and under-tested.\n" + "\t[--revision]:\n\t\tPrints build timestamp and Git commit information (updated when cmake is configured).\n" + "\t[--iterations iter]:\n\t\tRecompiles the same shader over and over, benchmarking related.\n" + "\t[--disable-storage-image-qualifier-deduction]:\n\t\tIf storage images are received without any nonwritable or nonreadable information,\n""" + "\t\tdo not attempt to analyze usage, and always emit read/write state.\n" + "\t[--flatten-multidimensional-arrays]:\n\t\tDo not support multi-dimensional arrays and flatten them to one dimension.\n" + "\t[--cpp-interface-name ]:\n\t\tEmit a specific class name in C++ codegen.\n" + "\t[--force-recompile-max-debug-iterations ]:\n\t\tAllow compilation loop to run for N loops.\n" + "\t\tCan be used to triage workarounds, but should not be used as a crutch, since it masks an implementation bug.\n" + ); + // clang-format on +} + +static void print_help_all() +{ + print_version(); + + // clang-format off + fprintf(stderr, "Usage: spirv-cross [SPIR-V file] [options]\n" + "\nBasic:\n" + "\t[SPIR-V file] (- is stdin)\n" + "\t[--output ]: If not provided, prints output to stdout.\n" + "\t[--dump-resources]:\n\t\tPrints a basic reflection of the SPIR-V module along with other output.\n" + "\t[--help]:\n\t\tPrints a summary help message.\n" + ); + // clang-format on + + print_help_backend(); + print_help_common(); + print_help_glsl(); + print_help_msl(); + print_help_hlsl(); + print_help_obscure(); +} + +static void print_help() +{ + print_version(); + + // clang-format off + fprintf(stderr, "Usage: spirv-cross [SPIR-V file] [options]\n" + "\nBasic:\n" + "\t[SPIR-V file] (- is stdin)\n" + "\t[--output ]: If not provided, prints output to stdout.\n" + "\t[--help]:\n\t\tPrints this summary help message.\n" + "\t[--help-all]:\n\t\tPrints all available help options.\n" + ); + // clang-format on + + print_help_backend(); + print_help_common(); + + // clang-format off + fprintf(stderr, "\nHelp Categories:\n" + "\t[--help-glsl]\n" + "\t[--help-msl]\n" + "\t[--help-hlsl]\n" + "\t[--help-obscure]\n" + ); + // clang-format on +} + +static bool remap_generic(Compiler &compiler, const SmallVector &resources, const Remap &remap) +{ + auto itr = + find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; }); + + if (itr != end(resources)) + { + compiler.set_remapped_variable_state(itr->id, true); + compiler.set_name(itr->id, remap.dst_name); + compiler.set_subpass_input_remapped_components(itr->id, remap.components); + return true; + } + else + return false; +} + +static vector remap_pls(const SmallVector &pls_variables, const SmallVector &resources, + const SmallVector *secondary_resources) +{ + vector ret; + + for (auto &pls : pls_variables) + { + bool found = false; + for (auto &res : resources) + { + if (res.name == pls.name) + { + ret.push_back({ res.id, pls.format }); + found = true; + break; + } + } + + if (!found && secondary_resources) + { + for (auto &res : *secondary_resources) + { + if (res.name == pls.name) + { + ret.push_back({ res.id, pls.format }); + found = true; + break; + } + } + } + + if (!found) + fprintf(stderr, "Did not find stage input/output/target with name \"%s\".\n", pls.name.c_str()); + } + + return ret; +} + +static PlsFormat pls_format(const char *str) +{ + if (!strcmp(str, "r11f_g11f_b10f")) + return PlsR11FG11FB10F; + else if (!strcmp(str, "r32f")) + return PlsR32F; + else if (!strcmp(str, "rg16f")) + return PlsRG16F; + else if (!strcmp(str, "rg16")) + return PlsRG16; + else if (!strcmp(str, "rgb10_a2")) + return PlsRGB10A2; + else if (!strcmp(str, "rgba8")) + return PlsRGBA8; + else if (!strcmp(str, "rgba8i")) + return PlsRGBA8I; + else if (!strcmp(str, "rgba8ui")) + return PlsRGBA8UI; + else if (!strcmp(str, "rg16i")) + return PlsRG16I; + else if (!strcmp(str, "rgb10_a2ui")) + return PlsRGB10A2UI; + else if (!strcmp(str, "rg16ui")) + return PlsRG16UI; + else if (!strcmp(str, "r32ui")) + return PlsR32UI; + else + return PlsNone; +} + +static ExecutionModel stage_to_execution_model(const std::string &stage) +{ + if (stage == "vert") + return ExecutionModelVertex; + else if (stage == "frag") + return ExecutionModelFragment; + else if (stage == "comp") + return ExecutionModelGLCompute; + else if (stage == "tesc") + return ExecutionModelTessellationControl; + else if (stage == "tese") + return ExecutionModelTessellationEvaluation; + else if (stage == "geom") + return ExecutionModelGeometry; + else if (stage == "rgen") + return ExecutionModelRayGenerationKHR; + else if (stage == "rint") + return ExecutionModelIntersectionKHR; + else if (stage == "rahit") + return ExecutionModelAnyHitKHR; + else if (stage == "rchit") + return ExecutionModelClosestHitKHR; + else if (stage == "rmiss") + return ExecutionModelMissKHR; + else if (stage == "rcall") + return ExecutionModelCallableKHR; + else if (stage == "mesh") + return ExecutionModelMeshEXT; + else if (stage == "task") + return ExecutionModelTaskEXT; + else + SPIRV_CROSS_THROW("Invalid stage."); +} + +static HLSLBindingFlags hlsl_resource_type_to_flag(const std::string &arg) +{ + if (arg == "push") + return HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT; + else if (arg == "cbv") + return HLSL_BINDING_AUTO_CBV_BIT; + else if (arg == "srv") + return HLSL_BINDING_AUTO_SRV_BIT; + else if (arg == "uav") + return HLSL_BINDING_AUTO_UAV_BIT; + else if (arg == "sampler") + return HLSL_BINDING_AUTO_SAMPLER_BIT; + else if (arg == "all") + return HLSL_BINDING_AUTO_ALL; + else + { + fprintf(stderr, "Invalid resource type for --hlsl-auto-binding: %s\n", arg.c_str()); + return 0; + } +} + +static string compile_iteration(const CLIArguments &args, std::vector spirv_file) +{ + Parser spirv_parser(std::move(spirv_file)); + spirv_parser.parse(); + + unique_ptr compiler; + bool combined_image_samplers = false; + bool build_dummy_sampler = false; + + if (args.cpp) + { + compiler.reset(new CompilerCPP(std::move(spirv_parser.get_parsed_ir()))); + if (args.cpp_interface_name) + static_cast(compiler.get())->set_interface_name(args.cpp_interface_name); + } + else if (args.msl) + { + compiler.reset(new CompilerMSL(std::move(spirv_parser.get_parsed_ir()))); + + auto *msl_comp = static_cast(compiler.get()); + auto msl_opts = msl_comp->get_msl_options(); + if (args.set_msl_version) + msl_opts.msl_version = args.msl_version; + msl_opts.capture_output_to_buffer = args.msl_capture_output_to_buffer; + msl_opts.swizzle_texture_samples = args.msl_swizzle_texture_samples; + msl_opts.invariant_float_math = args.msl_invariant_float_math; + if (args.msl_ios) + { + msl_opts.platform = CompilerMSL::Options::iOS; + msl_opts.emulate_cube_array = args.msl_emulate_cube_array; + } + msl_opts.use_framebuffer_fetch_subpasses = args.msl_framebuffer_fetch; + msl_opts.pad_fragment_output_components = args.msl_pad_fragment_output; + msl_opts.tess_domain_origin_lower_left = args.msl_domain_lower_left; + msl_opts.argument_buffers = args.msl_argument_buffers; + msl_opts.argument_buffers_tier = static_cast(args.msl_argument_buffers_tier); + msl_opts.texture_buffer_native = args.msl_texture_buffer_native; + msl_opts.multiview = args.msl_multiview; + msl_opts.multiview_layered_rendering = args.msl_multiview_layered_rendering; + msl_opts.view_index_from_device_index = args.msl_view_index_from_device_index; + msl_opts.dispatch_base = args.msl_dispatch_base; + msl_opts.enable_decoration_binding = args.msl_decoration_binding; + msl_opts.force_active_argument_buffer_resources = args.msl_force_active_argument_buffer_resources; + msl_opts.force_native_arrays = args.msl_force_native_arrays; + msl_opts.enable_frag_depth_builtin = args.msl_enable_frag_depth_builtin; + msl_opts.enable_frag_stencil_ref_builtin = args.msl_enable_frag_stencil_ref_builtin; + msl_opts.enable_frag_output_mask = args.msl_enable_frag_output_mask; + msl_opts.enable_clip_distance_user_varying = args.msl_enable_clip_distance_user_varying; + msl_opts.raw_buffer_tese_input = args.msl_raw_buffer_tese_input; + msl_opts.multi_patch_workgroup = args.msl_multi_patch_workgroup; + msl_opts.vertex_for_tessellation = args.msl_vertex_for_tessellation; + msl_opts.additional_fixed_sample_mask = args.msl_additional_fixed_sample_mask; + msl_opts.arrayed_subpass_input = args.msl_arrayed_subpass_input; + msl_opts.r32ui_linear_texture_alignment = args.msl_r32ui_linear_texture_alignment; + msl_opts.r32ui_alignment_constant_id = args.msl_r32ui_alignment_constant_id; + msl_opts.texture_1D_as_2D = args.msl_texture_1d_as_2d; + msl_opts.ios_use_simdgroup_functions = args.msl_ios_use_simdgroup_functions; + msl_opts.emulate_subgroups = args.msl_emulate_subgroups; + msl_opts.fixed_subgroup_size = args.msl_fixed_subgroup_size; + msl_opts.force_sample_rate_shading = args.msl_force_sample_rate_shading; + msl_opts.manual_helper_invocation_updates = args.msl_manual_helper_invocation_updates; + msl_opts.check_discarded_frag_stores = args.msl_check_discarded_frag_stores; + msl_opts.force_fragment_with_side_effects_execution = args.msl_force_fragment_with_side_effects_execution; + msl_opts.sample_dref_lod_array_as_grad = args.msl_sample_dref_lod_array_as_grad; + msl_opts.ios_support_base_vertex_instance = true; + msl_opts.runtime_array_rich_descriptor = args.msl_runtime_array_rich_descriptor; + msl_opts.replace_recursive_inputs = args.msl_replace_recursive_inputs; + msl_opts.input_attachment_is_ds_attachment = args.msl_input_attachment_is_ds_attachment; + msl_opts.readwrite_texture_fences = args.msl_readwrite_texture_fences; + msl_opts.agx_manual_cube_grad_fixup = args.msl_agx_manual_cube_grad_fixup; + msl_opts.disable_rasterization = args.msl_disable_rasterization; + msl_opts.auto_disable_rasterization = args.msl_auto_disable_rasterization; + msl_opts.enable_point_size_default = args.msl_enable_point_size_default; + msl_opts.default_point_size = args.msl_default_point_size; + msl_comp->set_msl_options(msl_opts); + for (auto &v : args.msl_discrete_descriptor_sets) + msl_comp->add_discrete_descriptor_set(v); + for (auto &v : args.msl_device_argument_buffers) + msl_comp->set_argument_buffer_device_address_space(v, true); + uint32_t i = 0; + for (auto &v : args.msl_dynamic_buffers) + msl_comp->add_dynamic_buffer(v.first, v.second, i++); + for (auto &v : args.msl_inline_uniform_blocks) + msl_comp->add_inline_uniform_block(v.first, v.second); + for (auto &v : args.msl_shader_inputs) + msl_comp->add_msl_shader_input(v); + for (auto &v : args.msl_shader_outputs) + msl_comp->add_msl_shader_output(v); + if (args.msl_combined_sampler_suffix) + msl_comp->set_combined_sampler_suffix(args.msl_combined_sampler_suffix); + } + else if (args.hlsl) + compiler.reset(new CompilerHLSL(std::move(spirv_parser.get_parsed_ir()))); + else + { + combined_image_samplers = !args.vulkan_semantics; + if (!args.vulkan_semantics || args.vulkan_glsl_disable_ext_samplerless_texture_functions) + build_dummy_sampler = true; + compiler.reset(new CompilerGLSL(std::move(spirv_parser.get_parsed_ir()))); + } + + if (!args.variable_type_remaps.empty()) + { + auto remap_cb = [&](const SPIRType &, const string &name, string &out) -> void { + for (const VariableTypeRemap &remap : args.variable_type_remaps) + if (name == remap.variable_name) + out = remap.new_variable_type; + }; + + compiler->set_variable_type_remap_callback(std::move(remap_cb)); + } + + for (auto &masked : args.masked_stage_outputs) + compiler->mask_stage_output_by_location(masked.first, masked.second); + for (auto &masked : args.masked_stage_builtins) + compiler->mask_stage_output_by_builtin(masked); + + for (auto &rename : args.entry_point_rename) + compiler->rename_entry_point(rename.old_name, rename.new_name, rename.execution_model); + + auto entry_points = compiler->get_entry_points_and_stages(); + auto entry_point = args.entry; + ExecutionModel model = ExecutionModelMax; + + if (!args.entry_stage.empty()) + { + model = stage_to_execution_model(args.entry_stage); + if (entry_point.empty()) + { + // Just use the first entry point with this stage. + for (auto &e : entry_points) + { + if (e.execution_model == model) + { + entry_point = e.name; + break; + } + } + + if (entry_point.empty()) + { + fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str()); + exit(EXIT_FAILURE); + } + } + else + { + // Make sure both stage and name exists. + bool exists = false; + for (auto &e : entry_points) + { + if (e.execution_model == model && e.name == entry_point) + { + exists = true; + break; + } + } + + if (!exists) + { + fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(), + args.entry_stage.c_str()); + exit(EXIT_FAILURE); + } + } + } + else if (!entry_point.empty()) + { + // Make sure there is just one entry point with this name, or the stage + // is ambiguous. + uint32_t stage_count = 0; + for (auto &e : entry_points) + { + if (e.name == entry_point) + { + stage_count++; + model = e.execution_model; + } + } + + if (stage_count == 0) + { + fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str()); + exit(EXIT_FAILURE); + } + else if (stage_count > 1) + { + fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str()); + exit(EXIT_FAILURE); + } + } + + if (!entry_point.empty()) + compiler->set_entry_point(entry_point, model); + + if (!args.set_version && !compiler->get_common_options().version) + { + fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n"); + print_help(); + exit(EXIT_FAILURE); + } + + CompilerGLSL::Options opts = compiler->get_common_options(); + if (args.set_version) + opts.version = args.version; + if (args.set_es) + opts.es = args.es; + opts.force_temporary = args.force_temporary; + opts.separate_shader_objects = args.sso; + opts.flatten_multidimensional_arrays = args.flatten_multidimensional_arrays; + opts.enable_420pack_extension = args.use_420pack_extension; + 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; + opts.emit_push_constant_as_uniform_buffer = args.glsl_emit_push_constant_as_ubo; + opts.emit_uniform_buffer_as_plain_uniforms = args.glsl_emit_ubo_as_plain_uniforms; + opts.force_flattened_io_blocks = args.glsl_force_flattened_io_blocks; + opts.ovr_multiview_view_count = args.glsl_ovr_multiview_view_count; + opts.emit_line_directives = args.emit_line_directives; + opts.enable_storage_image_qualifier_deduction = args.enable_storage_image_qualifier_deduction; + opts.force_zero_initialized_variables = args.force_zero_initialized_variables; + opts.relax_nan_checks = args.relax_nan_checks; + opts.force_recompile_max_debug_iterations = args.force_recompile_max_debug_iterations; + compiler->set_common_options(opts); + + // This is enough for Vulkan mapping API. + if (args.glsl_descriptor_heap_set != UINT32_MAX) + compiler->remap_descriptor_heap(ResourceTypeUnknown, args.glsl_descriptor_heap_set, args.glsl_descriptor_heap_binding); + + for (auto &fetch : args.glsl_ext_framebuffer_fetch) + compiler->remap_ext_framebuffer_fetch(fetch.first, fetch.second, !args.glsl_ext_framebuffer_fetch_noncoherent); + + // Set HLSL specific options. + if (args.hlsl) + { + auto *hlsl = static_cast(compiler.get()); + auto hlsl_opts = hlsl->get_hlsl_options(); + if (args.set_shader_model) + { + if (args.shader_model < 30) + { + fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n"); + exit(EXIT_FAILURE); + } + + hlsl_opts.shader_model = args.shader_model; + } + + if (args.hlsl_compat) + { + // Enable all compat options. + hlsl_opts.point_size_compat = true; + hlsl_opts.point_coord_compat = true; + } + + if (hlsl_opts.shader_model <= 30) + { + combined_image_samplers = true; + build_dummy_sampler = true; + } + + // If we're explicitly renaming, we probably want that name to be output. + if (!args.entry_point_rename.empty()) + hlsl_opts.use_entry_point_name = true; + + hlsl_opts.support_nonzero_base_vertex_base_instance = args.hlsl_support_nonzero_base; + hlsl_opts.force_storage_buffer_as_uav = args.hlsl_force_storage_buffer_as_uav; + hlsl_opts.nonwritable_uav_texture_as_srv = args.hlsl_nonwritable_uav_texture_as_srv; + hlsl_opts.enable_16bit_types = args.hlsl_enable_16bit_types; + hlsl_opts.flatten_matrix_vertex_input_semantics = args.hlsl_flatten_matrix_vertex_input_semantics; + hlsl_opts.preserve_structured_buffers = args.hlsl_preserve_structured_buffers; + hlsl_opts.user_semantic = args.hlsl_user_semantic; + hlsl->set_hlsl_options(hlsl_opts); + hlsl->set_resource_binding_flags(args.hlsl_binding_flags); + if (args.hlsl_base_vertex_index_explicit_binding) + { + hlsl->set_hlsl_aux_buffer_binding(HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE, + args.hlsl_base_vertex_index_register_index, + args.hlsl_base_vertex_index_register_space); + } + } + + if (build_dummy_sampler) + { + uint32_t sampler = compiler->build_dummy_sampler_for_combined_images(); + if (sampler != 0) + { + // Set some defaults to make validation happy. + compiler->set_decoration(sampler, DecorationDescriptorSet, 0); + compiler->set_decoration(sampler, DecorationBinding, 0); + } + } + + ShaderResources res; + if (args.remove_unused) + { + auto active = compiler->get_active_interface_variables(); + res = compiler->get_shader_resources(active); + compiler->set_enabled_interface_variables(std::move(active)); + } + else + res = compiler->get_shader_resources(); + + if (args.flatten_ubo) + { + for (auto &ubo : res.uniform_buffers) + compiler->flatten_buffer_block(ubo.id); + for (auto &ubo : res.push_constant_buffers) + compiler->flatten_buffer_block(ubo.id); + } + + auto pls_inputs = remap_pls(args.pls_in, res.stage_inputs, &res.subpass_inputs); + auto pls_outputs = remap_pls(args.pls_out, res.stage_outputs, nullptr); + compiler->remap_pixel_local_storage(std::move(pls_inputs), std::move(pls_outputs)); + + for (auto &ext : args.extensions) + compiler->require_extension(ext); + + for (auto &remap : args.remaps) + { + if (remap_generic(*compiler, res.stage_inputs, remap)) + continue; + if (remap_generic(*compiler, res.stage_outputs, remap)) + continue; + if (remap_generic(*compiler, res.subpass_inputs, remap)) + continue; + } + + for (auto &rename : args.interface_variable_renames) + { + if (rename.storageClass == StorageClassInput) + spirv_cross_util::rename_interface_variable(*compiler, res.stage_inputs, rename.location, + rename.variable_name); + else if (rename.storageClass == StorageClassOutput) + spirv_cross_util::rename_interface_variable(*compiler, res.stage_outputs, rename.location, + rename.variable_name); + else + { + fprintf(stderr, "error at --rename-interface-variable ...\n"); + exit(EXIT_FAILURE); + } + } + + if (combined_image_samplers) + { + compiler->build_combined_image_samplers(); + if (args.combined_samplers_inherit_bindings) + spirv_cross_util::inherit_combined_sampler_bindings(*compiler); + + // Give the remapped combined samplers new names. + for (auto &remap : compiler->get_combined_image_samplers()) + { + compiler->set_name(remap.combined_id, join("SPIRV_Cross_Combined", compiler->get_name(remap.image_id), + compiler->get_name(remap.sampler_id))); + } + } + + if (args.hlsl) + { + auto *hlsl_compiler = static_cast(compiler.get()); + hlsl_compiler->remap_num_workgroups_builtin(); + } + + if (args.hlsl) + { + for (auto &remap : args.hlsl_attr_remap) + static_cast(compiler.get())->add_vertex_attribute_remap(remap); + + for (auto &named_remap : args.hlsl_attr_remap_named) + { + auto itr = std::find_if(res.stage_inputs.begin(), res.stage_inputs.end(), [&](const Resource &input_res) { + return input_res.name == named_remap.name; + }); + + if (itr != res.stage_inputs.end()) + { + HLSLVertexAttributeRemap remap = { + compiler->get_decoration(itr->id, DecorationLocation), + named_remap.semantic, + }; + static_cast(compiler.get())->add_vertex_attribute_remap(remap); + } + } + } + + auto ret = compiler->compile(); + + if (args.dump_resources) + { + compiler->update_active_builtins(); + print_resources(*compiler, res); + print_push_constant_resources(*compiler, res.push_constant_buffers); + print_spec_constants(*compiler); + print_capabilities_and_extensions(*compiler); + } + + return ret; +} + +static int main_inner(int argc, char *argv[]) +{ + CLIArguments args; + CLICallbacks cbs; + + cbs.add("--help", [](CLIParser &parser) { + print_help(); + parser.end(); + }); + cbs.add("--help-all", [](CLIParser &parser) { + print_help_all(); + parser.end(); + }); + cbs.add("--help-backend", [](CLIParser &parser) { + print_help_backend(); + parser.end(); + }); + cbs.add("--help-common", [](CLIParser &parser) { + print_help_common(); + parser.end(); + }); + cbs.add("--help-glsl", [](CLIParser &parser) { + print_help_glsl(); + parser.end(); + }); + cbs.add("--help-msl", [](CLIParser &parser) { + print_help_msl(); + parser.end(); + }); + cbs.add("--help-hlsl", [](CLIParser &parser) { + print_help_hlsl(); + parser.end(); + }); + cbs.add("--help-obscure", [](CLIParser &parser) { + print_help_obscure(); + parser.end(); + }); + cbs.add("--revision", [](CLIParser &parser) { + print_version(); + parser.end(); + }); + cbs.add("--output", [&args](CLIParser &parser) { args.output = parser.next_string(); }); + cbs.add("--es", [&args](CLIParser &) { + args.es = true; + args.set_es = true; + }); + cbs.add("--no-es", [&args](CLIParser &) { + args.es = false; + args.set_es = true; + }); + cbs.add("--version", [&args](CLIParser &parser) { + args.version = parser.next_uint(); + args.set_version = true; + }); + cbs.add("--dump-resources", [&args](CLIParser &) { args.dump_resources = true; }); + cbs.add("--force-temporary", [&args](CLIParser &) { args.force_temporary = true; }); + cbs.add("--flatten-ubo", [&args](CLIParser &) { args.flatten_ubo = true; }); + cbs.add("--fixup-clipspace", [&args](CLIParser &) { args.fixup = true; }); + 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("--glsl-emit-push-constant-as-ubo", [&args](CLIParser &) { args.glsl_emit_push_constant_as_ubo = true; }); + cbs.add("--glsl-emit-ubo-as-plain-uniforms", [&args](CLIParser &) { args.glsl_emit_ubo_as_plain_uniforms = true; }); + cbs.add("--glsl-force-flattened-io-blocks", [&args](CLIParser &) { args.glsl_force_flattened_io_blocks = true; }); + cbs.add("--glsl-ovr-multiview-view-count", [&args](CLIParser &parser) { args.glsl_ovr_multiview_view_count = parser.next_uint(); }); + cbs.add("--glsl-remap-ext-framebuffer-fetch", [&args](CLIParser &parser) { + uint32_t input_index = parser.next_uint(); + uint32_t color_attachment = parser.next_uint(); + args.glsl_ext_framebuffer_fetch.push_back({ input_index, color_attachment }); + }); + cbs.add("--glsl-ext-framebuffer-fetch-noncoherent", [&args](CLIParser &) { + args.glsl_ext_framebuffer_fetch_noncoherent = true; + }); + cbs.add("--glsl-descriptor-heap-set-binding", [&args](CLIParser &parser) + { + args.glsl_descriptor_heap_set = parser.next_uint(); + args.glsl_descriptor_heap_binding = parser.next_uint(); + }); + cbs.add("--vulkan-glsl-disable-ext-samplerless-texture-functions", + [&args](CLIParser &) { args.vulkan_glsl_disable_ext_samplerless_texture_functions = true; }); + cbs.add("--disable-storage-image-qualifier-deduction", + [&args](CLIParser &) { args.enable_storage_image_qualifier_deduction = false; }); + cbs.add("--force-zero-initialized-variables", + [&args](CLIParser &) { args.force_zero_initialized_variables = true; }); + cbs.add("--msl", [&args](CLIParser &) { args.msl = true; }); + cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; }); + cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; }); + cbs.add("--hlsl-support-nonzero-basevertex-baseinstance", + [&args](CLIParser &) { args.hlsl_support_nonzero_base = true; }); + cbs.add("--hlsl-basevertex-baseinstance-binding", [&args](CLIParser &parser) { + args.hlsl_base_vertex_index_explicit_binding = true; + args.hlsl_base_vertex_index_register_index = parser.next_uint(); + args.hlsl_base_vertex_index_register_space = parser.next_uint(); + }); + cbs.add("--hlsl-auto-binding", [&args](CLIParser &parser) { + args.hlsl_binding_flags |= hlsl_resource_type_to_flag(parser.next_string()); + }); + cbs.add("--hlsl-force-storage-buffer-as-uav", + [&args](CLIParser &) { args.hlsl_force_storage_buffer_as_uav = true; }); + cbs.add("--hlsl-nonwritable-uav-texture-as-srv", + [&args](CLIParser &) { args.hlsl_nonwritable_uav_texture_as_srv = true; }); + cbs.add("--hlsl-enable-16bit-types", [&args](CLIParser &) { args.hlsl_enable_16bit_types = true; }); + cbs.add("--hlsl-flatten-matrix-vertex-input-semantics", + [&args](CLIParser &) { args.hlsl_flatten_matrix_vertex_input_semantics = true; }); + cbs.add("--hlsl-preserve-structured-buffers", [&args](CLIParser &) { args.hlsl_preserve_structured_buffers = true; }); + cbs.add("--hlsl-user-semantic", [&args](CLIParser &) { args.hlsl_user_semantic = true; }); + cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; }); + cbs.add("-V", [&args](CLIParser &) { args.vulkan_semantics = true; }); + cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; }); + cbs.add("--no-420pack-extension", [&args](CLIParser &) { args.use_420pack_extension = false; }); + cbs.add("--msl-capture-output", [&args](CLIParser &) { args.msl_capture_output_to_buffer = true; }); + cbs.add("--msl-swizzle-texture-samples", [&args](CLIParser &) { args.msl_swizzle_texture_samples = true; }); + cbs.add("--msl-ios", [&args](CLIParser &) { args.msl_ios = true; }); + cbs.add("--msl-pad-fragment-output", [&args](CLIParser &) { args.msl_pad_fragment_output = true; }); + cbs.add("--msl-domain-lower-left", [&args](CLIParser &) { args.msl_domain_lower_left = true; }); + cbs.add("--msl-argument-buffers", [&args](CLIParser &) { args.msl_argument_buffers = true; }); + cbs.add("--msl-argument-buffer-tier", + [&args](CLIParser &parser) { args.msl_argument_buffers_tier = parser.next_uint(); }); + cbs.add("--msl-discrete-descriptor-set", + [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); }); + cbs.add("--msl-device-argument-buffer", + [&args](CLIParser &parser) { args.msl_device_argument_buffers.push_back(parser.next_uint()); }); + cbs.add("--msl-texture-buffer-native", [&args](CLIParser &) { args.msl_texture_buffer_native = true; }); + cbs.add("--msl-framebuffer-fetch", [&args](CLIParser &) { args.msl_framebuffer_fetch = true; }); + cbs.add("--msl-invariant-float-math", [&args](CLIParser &) { args.msl_invariant_float_math = true; }); + cbs.add("--msl-emulate-cube-array", [&args](CLIParser &) { args.msl_emulate_cube_array = true; }); + cbs.add("--msl-multiview", [&args](CLIParser &) { args.msl_multiview = true; }); + cbs.add("--msl-multiview-no-layered-rendering", + [&args](CLIParser &) { args.msl_multiview_layered_rendering = false; }); + cbs.add("--msl-view-index-from-device-index", + [&args](CLIParser &) { args.msl_view_index_from_device_index = true; }); + cbs.add("--msl-dispatch-base", [&args](CLIParser &) { args.msl_dispatch_base = true; }); + cbs.add("--msl-dynamic-buffer", [&args](CLIParser &parser) { + args.msl_argument_buffers = true; + // Make sure next_uint() is called in-order. + uint32_t desc_set = parser.next_uint(); + uint32_t binding = parser.next_uint(); + args.msl_dynamic_buffers.push_back(make_pair(desc_set, binding)); + }); + cbs.add("--msl-decoration-binding", [&args](CLIParser &) { args.msl_decoration_binding = true; }); + cbs.add("--msl-force-active-argument-buffer-resources", + [&args](CLIParser &) { args.msl_force_active_argument_buffer_resources = true; }); + cbs.add("--msl-inline-uniform-block", [&args](CLIParser &parser) { + args.msl_argument_buffers = true; + // Make sure next_uint() is called in-order. + uint32_t desc_set = parser.next_uint(); + uint32_t binding = parser.next_uint(); + args.msl_inline_uniform_blocks.push_back(make_pair(desc_set, binding)); + }); + cbs.add("--msl-force-native-arrays", [&args](CLIParser &) { args.msl_force_native_arrays = true; }); + cbs.add("--msl-disable-frag-depth-builtin", [&args](CLIParser &) { args.msl_enable_frag_depth_builtin = false; }); + cbs.add("--msl-disable-frag-stencil-ref-builtin", + [&args](CLIParser &) { args.msl_enable_frag_stencil_ref_builtin = false; }); + cbs.add("--msl-enable-frag-output-mask", + [&args](CLIParser &parser) { args.msl_enable_frag_output_mask = parser.next_hex_uint(); }); + cbs.add("--msl-no-clip-distance-user-varying", + [&args](CLIParser &) { args.msl_enable_clip_distance_user_varying = false; }); + cbs.add("--msl-add-shader-input", [&args](CLIParser &parser) { + MSLShaderInterfaceVariable input; + // Make sure next_uint() is called in-order. + input.location = parser.next_uint(); + const char *format = parser.next_value_string("other"); + if (strcmp(format, "any32") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_ANY32; + else if (strcmp(format, "any16") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_ANY16; + else if (strcmp(format, "u16") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_UINT16; + else if (strcmp(format, "u8") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_UINT8; + else + input.format = MSL_SHADER_VARIABLE_FORMAT_OTHER; + input.vecsize = parser.next_uint(); + const char *rate = parser.next_value_string("vertex"); + if (strcmp(rate, "primitive") == 0) + input.rate = MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE; + else if (strcmp(rate, "patch") == 0) + input.rate = MSL_SHADER_VARIABLE_RATE_PER_PATCH; + else + input.rate = MSL_SHADER_VARIABLE_RATE_PER_VERTEX; + args.msl_shader_inputs.push_back(input); + }); + cbs.add("--msl-add-shader-output", [&args](CLIParser &parser) { + MSLShaderInterfaceVariable output; + // Make sure next_uint() is called in-order. + output.location = parser.next_uint(); + const char *format = parser.next_value_string("other"); + if (strcmp(format, "any32") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_ANY32; + else if (strcmp(format, "any16") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_ANY16; + else if (strcmp(format, "u16") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_UINT16; + else if (strcmp(format, "u8") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_UINT8; + else + output.format = MSL_SHADER_VARIABLE_FORMAT_OTHER; + output.vecsize = parser.next_uint(); + const char *rate = parser.next_value_string("vertex"); + if (strcmp(rate, "primitive") == 0) + output.rate = MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE; + else if (strcmp(rate, "patch") == 0) + output.rate = MSL_SHADER_VARIABLE_RATE_PER_PATCH; + else + output.rate = MSL_SHADER_VARIABLE_RATE_PER_VERTEX; + args.msl_shader_outputs.push_back(output); + }); + cbs.add("--msl-shader-input", [&args](CLIParser &parser) { + MSLShaderInterfaceVariable input; + // Make sure next_uint() is called in-order. + input.location = parser.next_uint(); + const char *format = parser.next_value_string("other"); + if (strcmp(format, "any32") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_ANY32; + else if (strcmp(format, "any16") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_ANY16; + else if (strcmp(format, "u16") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_UINT16; + else if (strcmp(format, "u8") == 0) + input.format = MSL_SHADER_VARIABLE_FORMAT_UINT8; + else + input.format = MSL_SHADER_VARIABLE_FORMAT_OTHER; + input.vecsize = parser.next_uint(); + args.msl_shader_inputs.push_back(input); + }); + cbs.add("--msl-shader-output", [&args](CLIParser &parser) { + MSLShaderInterfaceVariable output; + // Make sure next_uint() is called in-order. + output.location = parser.next_uint(); + const char *format = parser.next_value_string("other"); + if (strcmp(format, "any32") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_ANY32; + else if (strcmp(format, "any16") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_ANY16; + else if (strcmp(format, "u16") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_UINT16; + else if (strcmp(format, "u8") == 0) + output.format = MSL_SHADER_VARIABLE_FORMAT_UINT8; + else + output.format = MSL_SHADER_VARIABLE_FORMAT_OTHER; + output.vecsize = parser.next_uint(); + args.msl_shader_outputs.push_back(output); + }); + cbs.add("--msl-raw-buffer-tese-input", [&args](CLIParser &) { args.msl_raw_buffer_tese_input = true; }); + cbs.add("--msl-multi-patch-workgroup", [&args](CLIParser &) { args.msl_multi_patch_workgroup = true; }); + cbs.add("--msl-vertex-for-tessellation", [&args](CLIParser &) { args.msl_vertex_for_tessellation = true; }); + cbs.add("--msl-additional-fixed-sample-mask", + [&args](CLIParser &parser) { args.msl_additional_fixed_sample_mask = parser.next_hex_uint(); }); + cbs.add("--msl-arrayed-subpass-input", [&args](CLIParser &) { args.msl_arrayed_subpass_input = true; }); + cbs.add("--msl-r32ui-linear-texture-align", + [&args](CLIParser &parser) { args.msl_r32ui_linear_texture_alignment = parser.next_uint(); }); + cbs.add("--msl-r32ui-linear-texture-align-constant-id", + [&args](CLIParser &parser) { args.msl_r32ui_alignment_constant_id = parser.next_uint(); }); + cbs.add("--msl-texture-1d-as-2d", [&args](CLIParser &) { args.msl_texture_1d_as_2d = true; }); + cbs.add("--msl-ios-use-simdgroup-functions", [&args](CLIParser &) { args.msl_ios_use_simdgroup_functions = true; }); + cbs.add("--msl-emulate-subgroups", [&args](CLIParser &) { args.msl_emulate_subgroups = true; }); + cbs.add("--msl-fixed-subgroup-size", + [&args](CLIParser &parser) { args.msl_fixed_subgroup_size = parser.next_uint(); }); + cbs.add("--msl-force-sample-rate-shading", [&args](CLIParser &) { args.msl_force_sample_rate_shading = true; }); + cbs.add("--msl-no-manual-helper-invocation-updates", + [&args](CLIParser &) { args.msl_manual_helper_invocation_updates = false; }); + cbs.add("--msl-check-discarded-frag-stores", [&args](CLIParser &) { args.msl_check_discarded_frag_stores = true; }); + cbs.add("--msl-force-frag-with-side-effects-execution", [&args](CLIParser &) { args.msl_force_fragment_with_side_effects_execution = true; }); + cbs.add("--msl-sample-dref-lod-array-as-grad", + [&args](CLIParser &) { args.msl_sample_dref_lod_array_as_grad = true; }); + cbs.add("--msl-no-readwrite-texture-fences", [&args](CLIParser &) { args.msl_readwrite_texture_fences = false; }); + cbs.add("--msl-agx-manual-cube-grad-fixup", [&args](CLIParser &) { args.msl_agx_manual_cube_grad_fixup = true; }); + cbs.add("--msl-combined-sampler-suffix", [&args](CLIParser &parser) { + args.msl_combined_sampler_suffix = parser.next_string(); + }); + cbs.add("--msl-runtime-array-rich-descriptor", + [&args](CLIParser &) { args.msl_runtime_array_rich_descriptor = true; }); + cbs.add("--msl-replace-recursive-inputs", + [&args](CLIParser &) { args.msl_replace_recursive_inputs = true; }); + cbs.add("--msl-input-attachment-is-ds-attachment", [&args](CLIParser &) { args.msl_input_attachment_is_ds_attachment = true; }); + cbs.add("--msl-disable-rasterization", [&args](CLIParser &) { args.msl_disable_rasterization = true; }); + cbs.add("--msl-auto-disable-rasterization", [&args](CLIParser &) { args.msl_auto_disable_rasterization = true; }); + cbs.add("--msl-default-point-size", [&args](CLIParser &parser) { + args.msl_enable_point_size_default = true; + args.msl_default_point_size = static_cast(parser.next_double()); + }); + cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); }); + cbs.add("--rename-entry-point", [&args](CLIParser &parser) { + auto old_name = parser.next_string(); + auto new_name = parser.next_string(); + auto model = stage_to_execution_model(parser.next_string()); + args.entry_point_rename.push_back({ old_name, new_name, std::move(model) }); + }); + cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); }); + cbs.add("--stage", [&args](CLIParser &parser) { args.entry_stage = parser.next_string(); }); + cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; }); + cbs.add("--set-hlsl-vertex-input-semantic", [&args](CLIParser &parser) { + HLSLVertexAttributeRemap remap; + remap.location = parser.next_uint(); + remap.semantic = parser.next_string(); + args.hlsl_attr_remap.push_back(std::move(remap)); + }); + cbs.add("--set-hlsl-named-vertex-input-semantic", [&args](CLIParser &parser) { + HLSLVertexAttributeRemapNamed remap; + remap.name = parser.next_string(); + remap.semantic = parser.next_string(); + args.hlsl_attr_remap_named.push_back(std::move(remap)); + }); + + cbs.add("--remap", [&args](CLIParser &parser) { + string src = parser.next_string(); + string dst = parser.next_string(); + uint32_t components = parser.next_uint(); + args.remaps.push_back({ std::move(src), std::move(dst), components }); + }); + + cbs.add("--remap-variable-type", [&args](CLIParser &parser) { + string var_name = parser.next_string(); + string new_type = parser.next_string(); + args.variable_type_remaps.push_back({ std::move(var_name), std::move(new_type) }); + }); + + cbs.add("--rename-interface-variable", [&args](CLIParser &parser) { + StorageClass cls = StorageClassMax; + string clsStr = parser.next_string(); + if (clsStr == "in") + cls = StorageClassInput; + else if (clsStr == "out") + cls = StorageClassOutput; + + uint32_t loc = parser.next_uint(); + string var_name = parser.next_string(); + args.interface_variable_renames.push_back({ cls, loc, std::move(var_name) }); + }); + + cbs.add("--pls-in", [&args](CLIParser &parser) { + auto fmt = pls_format(parser.next_string()); + auto name = parser.next_string(); + args.pls_in.push_back({ std::move(fmt), std::move(name) }); + }); + cbs.add("--pls-out", [&args](CLIParser &parser) { + auto fmt = pls_format(parser.next_string()); + auto name = parser.next_string(); + args.pls_out.push_back({ std::move(fmt), std::move(name) }); + }); + cbs.add("--shader-model", [&args](CLIParser &parser) { + args.shader_model = parser.next_uint(); + args.set_shader_model = true; + }); + cbs.add("--msl-version", [&args](CLIParser &parser) { + args.msl_version = parser.next_uint(); + args.set_msl_version = true; + }); + + cbs.add("--remove-unused-variables", [&args](CLIParser &) { args.remove_unused = true; }); + 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.add("--emit-line-directives", [&args](CLIParser &) { args.emit_line_directives = true; }); + + cbs.add("--mask-stage-output-location", [&](CLIParser &parser) { + uint32_t location = parser.next_uint(); + uint32_t component = parser.next_uint(); + args.masked_stage_outputs.push_back({ location, component }); + }); + + cbs.add("--mask-stage-output-builtin", [&](CLIParser &parser) { + BuiltIn masked_builtin = BuiltInMax; + std::string builtin = parser.next_string(); + if (builtin == "Position") + masked_builtin = BuiltInPosition; + else if (builtin == "PointSize") + masked_builtin = BuiltInPointSize; + else if (builtin == "CullDistance") + masked_builtin = BuiltInCullDistance; + else if (builtin == "ClipDistance") + masked_builtin = BuiltInClipDistance; + else + { + print_help(); + exit(EXIT_FAILURE); + } + args.masked_stage_builtins.push_back(masked_builtin); + }); + + cbs.add("--force-recompile-max-debug-iterations", [&](CLIParser &parser) { + args.force_recompile_max_debug_iterations = parser.next_uint(); + }); + + cbs.add("--relax-nan-checks", [&](CLIParser &) { args.relax_nan_checks = true; }); + + cbs.default_handler = [&args](const char *value) { args.input = value; }; + cbs.add("-", [&args](CLIParser &) { args.input = "-"; }); + cbs.error_handler = [] { print_help(); }; + + CLIParser parser{ std::move(cbs), argc - 1, argv + 1 }; + if (!parser.parse()) + return EXIT_FAILURE; + else if (parser.ended_state) + return EXIT_SUCCESS; + + if (!args.input) + { + fprintf(stderr, "Didn't specify input file.\n"); + print_help(); + return EXIT_FAILURE; + } + + auto spirv_file = read_spirv_file(args.input); + if (spirv_file.empty()) + return EXIT_FAILURE; + + // Special case reflection because it has little to do with the path followed by code-outputting compilers + if (!args.reflect.empty()) + { + Parser spirv_parser(std::move(spirv_file)); + spirv_parser.parse(); + + CompilerReflection compiler(std::move(spirv_parser.get_parsed_ir())); + 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; + } + + string compiled_output; + + if (args.iterations == 1) + compiled_output = compile_iteration(args, std::move(spirv_file)); + else + { + for (unsigned i = 0; i < args.iterations; i++) + compiled_output = compile_iteration(args, spirv_file); + } + + if (args.output) + write_string_to_file(args.output, compiled_output.c_str()); + else + printf("%s", compiled_output.c_str()); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) +{ +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS + return main_inner(argc, argv); +#else + // Make sure we catch the exception or it just disappears into the aether on Windows. + try + { + return main_inner(argc, argv); + } + catch (const std::exception &e) + { + fprintf(stderr, "SPIRV-Cross threw an exception: %s\n", e.what()); + return EXIT_FAILURE; + } +#endif +} diff --git a/thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c-shared.pc.in b/thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c-shared.pc.in new file mode 100644 index 000000000..94e63eaeb --- /dev/null +++ b/thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c-shared.pc.in @@ -0,0 +1,16 @@ +# Copyright 2020-2021 Hans-Kristian Arntzen +# SPDX-License-Identifier: Apache-2.0 + +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +sharedlibdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/spirv_cross + +Name: spirv-cross-c-shared +Description: C API for SPIRV-Cross (shared) +Version: @SPIRV_CROSS_VERSION@ + +Requires: +Libs: -L${libdir} -L${sharedlibdir} -lspirv-cross-c-shared +Cflags: -I${includedir} diff --git a/thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c.pc.in b/thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c.pc.in new file mode 100644 index 000000000..2b586b6ff --- /dev/null +++ b/thirdparty/SPIRV-Cross/pkg-config/spirv-cross-c.pc.in @@ -0,0 +1,15 @@ +# Copyright 2020-2021 Hans-Kristian Arntzen +# SPDX-License-Identifier: Apache-2.0 + +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/spirv_cross + +Name: spirv-cross-c +Description: C API for SPIRV-Cross (static) +Version: @SPIRV_CROSS_VERSION@ + +Requires: +Libs: -L${libdir} -lspirv-cross-c +Cflags: -I${includedir} diff --git a/thirdparty/SPIRV-Cross/spirv.h b/thirdparty/SPIRV-Cross/spirv.h new file mode 100644 index 000000000..d00cf0f4e --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv.h @@ -0,0 +1,5602 @@ +/* +** Copyright: 2014-2024 The Khronos Group Inc. +** License: MIT +** +** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +** KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +** SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +** https://www.khronos.org/registry/ +*/ + +/* +** This header is automatically generated by the same tool that creates +** the Binary Section of the SPIR-V specification. +*/ + +/* +** Enumeration tokens for SPIR-V, in various styles: +** C, C++, C++11, JSON, Lua, Python, C#, D, Beef +** +** - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +** - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +** - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +** - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +** - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +** - C# will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +** - Beef will use enum classes in the Specification class located in the "Spv" namespace, +** e.g.: Spv.Specification.SourceLanguage.GLSL +** +** Some tokens act like mask values, which can be OR'd together, +** while others are mutually exclusive. The mask-like ones have +** "Mask" in their name, and a parallel enum that has the shift +** amount (1 << x) for each corresponding enumerant. +*/ + +#ifndef spirv_H +#define spirv_H + +typedef unsigned int SpvId; + +#define SPV_VERSION 0x10600 +#define SPV_REVISION 1 + +static const unsigned int SpvMagicNumber = 0x07230203; +static const unsigned int SpvVersion = 0x00010600; +static const unsigned int SpvRevision = 1; +static const unsigned int SpvOpCodeMask = 0xffff; +static const unsigned int SpvWordCountShift = 16; + +typedef enum SpvSourceLanguage_ { + SpvSourceLanguageUnknown = 0, + SpvSourceLanguageESSL = 1, + SpvSourceLanguageGLSL = 2, + SpvSourceLanguageOpenCL_C = 3, + SpvSourceLanguageOpenCL_CPP = 4, + SpvSourceLanguageHLSL = 5, + SpvSourceLanguageCPP_for_OpenCL = 6, + SpvSourceLanguageSYCL = 7, + SpvSourceLanguageHERO_C = 8, + SpvSourceLanguageNZSL = 9, + SpvSourceLanguageWGSL = 10, + SpvSourceLanguageSlang = 11, + SpvSourceLanguageZig = 12, + SpvSourceLanguageRust = 13, + SpvSourceLanguageMax = 0x7fffffff, +} SpvSourceLanguage; + +typedef enum SpvExecutionModel_ { + SpvExecutionModelVertex = 0, + SpvExecutionModelTessellationControl = 1, + SpvExecutionModelTessellationEvaluation = 2, + SpvExecutionModelGeometry = 3, + SpvExecutionModelFragment = 4, + SpvExecutionModelGLCompute = 5, + SpvExecutionModelKernel = 6, + SpvExecutionModelTaskNV = 5267, + SpvExecutionModelMeshNV = 5268, + SpvExecutionModelRayGenerationKHR = 5313, + SpvExecutionModelRayGenerationNV = 5313, + SpvExecutionModelIntersectionKHR = 5314, + SpvExecutionModelIntersectionNV = 5314, + SpvExecutionModelAnyHitKHR = 5315, + SpvExecutionModelAnyHitNV = 5315, + SpvExecutionModelClosestHitKHR = 5316, + SpvExecutionModelClosestHitNV = 5316, + SpvExecutionModelMissKHR = 5317, + SpvExecutionModelMissNV = 5317, + SpvExecutionModelCallableKHR = 5318, + SpvExecutionModelCallableNV = 5318, + SpvExecutionModelTaskEXT = 5364, + SpvExecutionModelMeshEXT = 5365, + SpvExecutionModelMax = 0x7fffffff, +} SpvExecutionModel; + +typedef enum SpvAddressingModel_ { + SpvAddressingModelLogical = 0, + SpvAddressingModelPhysical32 = 1, + SpvAddressingModelPhysical64 = 2, + SpvAddressingModelPhysicalStorageBuffer64 = 5348, + SpvAddressingModelPhysicalStorageBuffer64EXT = 5348, + SpvAddressingModelMax = 0x7fffffff, +} SpvAddressingModel; + +typedef enum SpvMemoryModel_ { + SpvMemoryModelSimple = 0, + SpvMemoryModelGLSL450 = 1, + SpvMemoryModelOpenCL = 2, + SpvMemoryModelVulkan = 3, + SpvMemoryModelVulkanKHR = 3, + SpvMemoryModelMax = 0x7fffffff, +} SpvMemoryModel; + +typedef enum SpvExecutionMode_ { + SpvExecutionModeInvocations = 0, + SpvExecutionModeSpacingEqual = 1, + SpvExecutionModeSpacingFractionalEven = 2, + SpvExecutionModeSpacingFractionalOdd = 3, + SpvExecutionModeVertexOrderCw = 4, + SpvExecutionModeVertexOrderCcw = 5, + SpvExecutionModePixelCenterInteger = 6, + SpvExecutionModeOriginUpperLeft = 7, + SpvExecutionModeOriginLowerLeft = 8, + SpvExecutionModeEarlyFragmentTests = 9, + SpvExecutionModePointMode = 10, + SpvExecutionModeXfb = 11, + SpvExecutionModeDepthReplacing = 12, + SpvExecutionModeDepthGreater = 14, + SpvExecutionModeDepthLess = 15, + SpvExecutionModeDepthUnchanged = 16, + SpvExecutionModeLocalSize = 17, + SpvExecutionModeLocalSizeHint = 18, + SpvExecutionModeInputPoints = 19, + SpvExecutionModeInputLines = 20, + SpvExecutionModeInputLinesAdjacency = 21, + SpvExecutionModeTriangles = 22, + SpvExecutionModeInputTrianglesAdjacency = 23, + SpvExecutionModeQuads = 24, + SpvExecutionModeIsolines = 25, + SpvExecutionModeOutputVertices = 26, + SpvExecutionModeOutputPoints = 27, + SpvExecutionModeOutputLineStrip = 28, + SpvExecutionModeOutputTriangleStrip = 29, + SpvExecutionModeVecTypeHint = 30, + SpvExecutionModeContractionOff = 31, + SpvExecutionModeInitializer = 33, + SpvExecutionModeFinalizer = 34, + SpvExecutionModeSubgroupSize = 35, + SpvExecutionModeSubgroupsPerWorkgroup = 36, + SpvExecutionModeSubgroupsPerWorkgroupId = 37, + SpvExecutionModeLocalSizeId = 38, + SpvExecutionModeLocalSizeHintId = 39, + SpvExecutionModeNonCoherentColorAttachmentReadEXT = 4169, + SpvExecutionModeNonCoherentDepthAttachmentReadEXT = 4170, + SpvExecutionModeNonCoherentStencilAttachmentReadEXT = 4171, + SpvExecutionModeSubgroupUniformControlFlowKHR = 4421, + SpvExecutionModePostDepthCoverage = 4446, + SpvExecutionModeDenormPreserve = 4459, + SpvExecutionModeDenormFlushToZero = 4460, + SpvExecutionModeSignedZeroInfNanPreserve = 4461, + SpvExecutionModeRoundingModeRTE = 4462, + SpvExecutionModeRoundingModeRTZ = 4463, + SpvExecutionModeNonCoherentTileAttachmentReadQCOM = 4489, + SpvExecutionModeTileShadingRateQCOM = 4490, + SpvExecutionModeEarlyAndLateFragmentTestsAMD = 5017, + SpvExecutionModeStencilRefReplacingEXT = 5027, + SpvExecutionModeCoalescingAMDX = 5069, + SpvExecutionModeIsApiEntryAMDX = 5070, + SpvExecutionModeMaxNodeRecursionAMDX = 5071, + SpvExecutionModeStaticNumWorkgroupsAMDX = 5072, + SpvExecutionModeShaderIndexAMDX = 5073, + SpvExecutionModeMaxNumWorkgroupsAMDX = 5077, + SpvExecutionModeStencilRefUnchangedFrontAMD = 5079, + SpvExecutionModeStencilRefGreaterFrontAMD = 5080, + SpvExecutionModeStencilRefLessFrontAMD = 5081, + SpvExecutionModeStencilRefUnchangedBackAMD = 5082, + SpvExecutionModeStencilRefGreaterBackAMD = 5083, + SpvExecutionModeStencilRefLessBackAMD = 5084, + SpvExecutionModeQuadDerivativesKHR = 5088, + SpvExecutionModeRequireFullQuadsKHR = 5089, + SpvExecutionModeSharesInputWithAMDX = 5102, + SpvExecutionModeArithmeticPoisonKHR = 5157, + SpvExecutionModeOutputLinesEXT = 5269, + SpvExecutionModeOutputLinesNV = 5269, + SpvExecutionModeOutputPrimitivesEXT = 5270, + SpvExecutionModeOutputPrimitivesNV = 5270, + SpvExecutionModeDerivativeGroupQuadsKHR = 5289, + SpvExecutionModeDerivativeGroupQuadsNV = 5289, + SpvExecutionModeDerivativeGroupLinearKHR = 5290, + SpvExecutionModeDerivativeGroupLinearNV = 5290, + SpvExecutionModeOutputTrianglesEXT = 5298, + SpvExecutionModeOutputTrianglesNV = 5298, + SpvExecutionModePixelInterlockOrderedEXT = 5366, + SpvExecutionModePixelInterlockUnorderedEXT = 5367, + SpvExecutionModeSampleInterlockOrderedEXT = 5368, + SpvExecutionModeSampleInterlockUnorderedEXT = 5369, + SpvExecutionModeShadingRateInterlockOrderedEXT = 5370, + SpvExecutionModeShadingRateInterlockUnorderedEXT = 5371, + SpvExecutionModeShader64BitIndexingEXT = 5427, + SpvExecutionModeSharedLocalMemorySizeINTEL = 5618, + SpvExecutionModeRoundingModeRTPINTEL = 5620, + SpvExecutionModeRoundingModeRTNINTEL = 5621, + SpvExecutionModeFloatingPointModeALTINTEL = 5622, + SpvExecutionModeFloatingPointModeIEEEINTEL = 5623, + SpvExecutionModeMaxWorkgroupSizeINTEL = 5893, + SpvExecutionModeMaxWorkDimINTEL = 5894, + SpvExecutionModeNoGlobalOffsetINTEL = 5895, + SpvExecutionModeNumSIMDWorkitemsINTEL = 5896, + SpvExecutionModeSchedulerTargetFmaxMhzINTEL = 5903, + SpvExecutionModeMaximallyReconvergesKHR = 6023, + SpvExecutionModeFPFastMathDefault = 6028, + SpvExecutionModeStreamingInterfaceINTEL = 6154, + SpvExecutionModeRegisterMapInterfaceINTEL = 6160, + SpvExecutionModeNamedBarrierCountINTEL = 6417, + SpvExecutionModeMaximumRegistersINTEL = 6461, + SpvExecutionModeMaximumRegistersIdINTEL = 6462, + SpvExecutionModeNamedMaximumRegistersINTEL = 6463, + SpvExecutionModeMax = 0x7fffffff, +} SpvExecutionMode; + +typedef enum SpvStorageClass_ { + SpvStorageClassUniformConstant = 0, + SpvStorageClassInput = 1, + SpvStorageClassUniform = 2, + SpvStorageClassOutput = 3, + SpvStorageClassWorkgroup = 4, + SpvStorageClassCrossWorkgroup = 5, + SpvStorageClassPrivate = 6, + SpvStorageClassFunction = 7, + SpvStorageClassGeneric = 8, + SpvStorageClassPushConstant = 9, + SpvStorageClassAtomicCounter = 10, + SpvStorageClassImage = 11, + SpvStorageClassStorageBuffer = 12, + SpvStorageClassTileImageEXT = 4172, + SpvStorageClassTileAttachmentQCOM = 4491, + SpvStorageClassNodePayloadAMDX = 5068, + SpvStorageClassCallableDataKHR = 5328, + SpvStorageClassCallableDataNV = 5328, + SpvStorageClassIncomingCallableDataKHR = 5329, + SpvStorageClassIncomingCallableDataNV = 5329, + SpvStorageClassRayPayloadKHR = 5338, + SpvStorageClassRayPayloadNV = 5338, + SpvStorageClassHitAttributeKHR = 5339, + SpvStorageClassHitAttributeNV = 5339, + SpvStorageClassIncomingRayPayloadKHR = 5342, + SpvStorageClassIncomingRayPayloadNV = 5342, + SpvStorageClassShaderRecordBufferKHR = 5343, + SpvStorageClassShaderRecordBufferNV = 5343, + SpvStorageClassPhysicalStorageBuffer = 5349, + SpvStorageClassPhysicalStorageBufferEXT = 5349, + SpvStorageClassHitObjectAttributeNV = 5385, + SpvStorageClassTaskPayloadWorkgroupEXT = 5402, + SpvStorageClassHitObjectAttributeEXT = 5411, + SpvStorageClassCodeSectionINTEL = 5605, + SpvStorageClassDeviceOnlyALTERA = 5936, + SpvStorageClassDeviceOnlyINTEL = 5936, + SpvStorageClassHostOnlyALTERA = 5937, + SpvStorageClassHostOnlyINTEL = 5937, + SpvStorageClassMax = 0x7fffffff, +} SpvStorageClass; + +typedef enum SpvDim_ { + SpvDim1D = 0, + SpvDim2D = 1, + SpvDim3D = 2, + SpvDimCube = 3, + SpvDimRect = 4, + SpvDimBuffer = 5, + SpvDimSubpassData = 6, + SpvDimTileImageDataEXT = 4173, + SpvDimMax = 0x7fffffff, +} SpvDim; + +typedef enum SpvSamplerAddressingMode_ { + SpvSamplerAddressingModeNone = 0, + SpvSamplerAddressingModeClampToEdge = 1, + SpvSamplerAddressingModeClamp = 2, + SpvSamplerAddressingModeRepeat = 3, + SpvSamplerAddressingModeRepeatMirrored = 4, + SpvSamplerAddressingModeMax = 0x7fffffff, +} SpvSamplerAddressingMode; + +typedef enum SpvSamplerFilterMode_ { + SpvSamplerFilterModeNearest = 0, + SpvSamplerFilterModeLinear = 1, + SpvSamplerFilterModeMax = 0x7fffffff, +} SpvSamplerFilterMode; + +typedef enum SpvImageFormat_ { + SpvImageFormatUnknown = 0, + SpvImageFormatRgba32f = 1, + SpvImageFormatRgba16f = 2, + SpvImageFormatR32f = 3, + SpvImageFormatRgba8 = 4, + SpvImageFormatRgba8Snorm = 5, + SpvImageFormatRg32f = 6, + SpvImageFormatRg16f = 7, + SpvImageFormatR11fG11fB10f = 8, + SpvImageFormatR16f = 9, + SpvImageFormatRgba16 = 10, + SpvImageFormatRgb10A2 = 11, + SpvImageFormatRg16 = 12, + SpvImageFormatRg8 = 13, + SpvImageFormatR16 = 14, + SpvImageFormatR8 = 15, + SpvImageFormatRgba16Snorm = 16, + SpvImageFormatRg16Snorm = 17, + SpvImageFormatRg8Snorm = 18, + SpvImageFormatR16Snorm = 19, + SpvImageFormatR8Snorm = 20, + SpvImageFormatRgba32i = 21, + SpvImageFormatRgba16i = 22, + SpvImageFormatRgba8i = 23, + SpvImageFormatR32i = 24, + SpvImageFormatRg32i = 25, + SpvImageFormatRg16i = 26, + SpvImageFormatRg8i = 27, + SpvImageFormatR16i = 28, + SpvImageFormatR8i = 29, + SpvImageFormatRgba32ui = 30, + SpvImageFormatRgba16ui = 31, + SpvImageFormatRgba8ui = 32, + SpvImageFormatR32ui = 33, + SpvImageFormatRgb10a2ui = 34, + SpvImageFormatRg32ui = 35, + SpvImageFormatRg16ui = 36, + SpvImageFormatRg8ui = 37, + SpvImageFormatR16ui = 38, + SpvImageFormatR8ui = 39, + SpvImageFormatR64ui = 40, + SpvImageFormatR64i = 41, + SpvImageFormatMax = 0x7fffffff, +} SpvImageFormat; + +typedef enum SpvImageChannelOrder_ { + SpvImageChannelOrderR = 0, + SpvImageChannelOrderA = 1, + SpvImageChannelOrderRG = 2, + SpvImageChannelOrderRA = 3, + SpvImageChannelOrderRGB = 4, + SpvImageChannelOrderRGBA = 5, + SpvImageChannelOrderBGRA = 6, + SpvImageChannelOrderARGB = 7, + SpvImageChannelOrderIntensity = 8, + SpvImageChannelOrderLuminance = 9, + SpvImageChannelOrderRx = 10, + SpvImageChannelOrderRGx = 11, + SpvImageChannelOrderRGBx = 12, + SpvImageChannelOrderDepth = 13, + SpvImageChannelOrderDepthStencil = 14, + SpvImageChannelOrdersRGB = 15, + SpvImageChannelOrdersRGBx = 16, + SpvImageChannelOrdersRGBA = 17, + SpvImageChannelOrdersBGRA = 18, + SpvImageChannelOrderABGR = 19, + SpvImageChannelOrderMax = 0x7fffffff, +} SpvImageChannelOrder; + +typedef enum SpvImageChannelDataType_ { + SpvImageChannelDataTypeSnormInt8 = 0, + SpvImageChannelDataTypeSnormInt16 = 1, + SpvImageChannelDataTypeUnormInt8 = 2, + SpvImageChannelDataTypeUnormInt16 = 3, + SpvImageChannelDataTypeUnormShort565 = 4, + SpvImageChannelDataTypeUnormShort555 = 5, + SpvImageChannelDataTypeUnormInt101010 = 6, + SpvImageChannelDataTypeSignedInt8 = 7, + SpvImageChannelDataTypeSignedInt16 = 8, + SpvImageChannelDataTypeSignedInt32 = 9, + SpvImageChannelDataTypeUnsignedInt8 = 10, + SpvImageChannelDataTypeUnsignedInt16 = 11, + SpvImageChannelDataTypeUnsignedInt32 = 12, + SpvImageChannelDataTypeHalfFloat = 13, + SpvImageChannelDataTypeFloat = 14, + SpvImageChannelDataTypeUnormInt24 = 15, + SpvImageChannelDataTypeUnormInt101010_2 = 16, + SpvImageChannelDataTypeUnormInt10X6EXT = 17, + SpvImageChannelDataTypeUnsignedIntRaw10EXT = 19, + SpvImageChannelDataTypeUnsignedIntRaw12EXT = 20, + SpvImageChannelDataTypeUnormInt2_101010EXT = 21, + SpvImageChannelDataTypeUnsignedInt10X6EXT = 22, + SpvImageChannelDataTypeUnsignedInt12X4EXT = 23, + SpvImageChannelDataTypeUnsignedInt14X2EXT = 24, + SpvImageChannelDataTypeUnormInt12X4EXT = 25, + SpvImageChannelDataTypeUnormInt14X2EXT = 26, + SpvImageChannelDataTypeMax = 0x7fffffff, +} SpvImageChannelDataType; + +typedef enum SpvImageOperandsShift_ { + SpvImageOperandsBiasShift = 0, + SpvImageOperandsLodShift = 1, + SpvImageOperandsGradShift = 2, + SpvImageOperandsConstOffsetShift = 3, + SpvImageOperandsOffsetShift = 4, + SpvImageOperandsConstOffsetsShift = 5, + SpvImageOperandsSampleShift = 6, + SpvImageOperandsMinLodShift = 7, + SpvImageOperandsMakeTexelAvailableShift = 8, + SpvImageOperandsMakeTexelAvailableKHRShift = 8, + SpvImageOperandsMakeTexelVisibleShift = 9, + SpvImageOperandsMakeTexelVisibleKHRShift = 9, + SpvImageOperandsNonPrivateTexelShift = 10, + SpvImageOperandsNonPrivateTexelKHRShift = 10, + SpvImageOperandsVolatileTexelShift = 11, + SpvImageOperandsVolatileTexelKHRShift = 11, + SpvImageOperandsSignExtendShift = 12, + SpvImageOperandsZeroExtendShift = 13, + SpvImageOperandsNontemporalShift = 14, + SpvImageOperandsOffsetsShift = 16, + SpvImageOperandsMax = 0x7fffffff, +} SpvImageOperandsShift; + +typedef enum SpvImageOperandsMask_ { + SpvImageOperandsMaskNone = 0, + SpvImageOperandsBiasMask = 0x00000001, + SpvImageOperandsLodMask = 0x00000002, + SpvImageOperandsGradMask = 0x00000004, + SpvImageOperandsConstOffsetMask = 0x00000008, + SpvImageOperandsOffsetMask = 0x00000010, + SpvImageOperandsConstOffsetsMask = 0x00000020, + SpvImageOperandsSampleMask = 0x00000040, + SpvImageOperandsMinLodMask = 0x00000080, + SpvImageOperandsMakeTexelAvailableMask = 0x00000100, + SpvImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + SpvImageOperandsMakeTexelVisibleMask = 0x00000200, + SpvImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + SpvImageOperandsNonPrivateTexelMask = 0x00000400, + SpvImageOperandsNonPrivateTexelKHRMask = 0x00000400, + SpvImageOperandsVolatileTexelMask = 0x00000800, + SpvImageOperandsVolatileTexelKHRMask = 0x00000800, + SpvImageOperandsSignExtendMask = 0x00001000, + SpvImageOperandsZeroExtendMask = 0x00002000, + SpvImageOperandsNontemporalMask = 0x00004000, + SpvImageOperandsOffsetsMask = 0x00010000, +} SpvImageOperandsMask; + +typedef enum SpvFPFastMathModeShift_ { + SpvFPFastMathModeNotNaNShift = 0, + SpvFPFastMathModeNotInfShift = 1, + SpvFPFastMathModeNSZShift = 2, + SpvFPFastMathModeAllowRecipShift = 3, + SpvFPFastMathModeFastShift = 4, + SpvFPFastMathModeAllowContractShift = 16, + SpvFPFastMathModeAllowContractFastINTELShift = 16, + SpvFPFastMathModeAllowReassocShift = 17, + SpvFPFastMathModeAllowReassocINTELShift = 17, + SpvFPFastMathModeAllowTransformShift = 18, + SpvFPFastMathModeMax = 0x7fffffff, +} SpvFPFastMathModeShift; + +typedef enum SpvFPFastMathModeMask_ { + SpvFPFastMathModeMaskNone = 0, + SpvFPFastMathModeNotNaNMask = 0x00000001, + SpvFPFastMathModeNotInfMask = 0x00000002, + SpvFPFastMathModeNSZMask = 0x00000004, + SpvFPFastMathModeAllowRecipMask = 0x00000008, + SpvFPFastMathModeFastMask = 0x00000010, + SpvFPFastMathModeAllowContractMask = 0x00010000, + SpvFPFastMathModeAllowContractFastINTELMask = 0x00010000, + SpvFPFastMathModeAllowReassocMask = 0x00020000, + SpvFPFastMathModeAllowReassocINTELMask = 0x00020000, + SpvFPFastMathModeAllowTransformMask = 0x00040000, +} SpvFPFastMathModeMask; + +typedef enum SpvFPRoundingMode_ { + SpvFPRoundingModeRTE = 0, + SpvFPRoundingModeRTZ = 1, + SpvFPRoundingModeRTP = 2, + SpvFPRoundingModeRTN = 3, + SpvFPRoundingModeMax = 0x7fffffff, +} SpvFPRoundingMode; + +typedef enum SpvLinkageType_ { + SpvLinkageTypeExport = 0, + SpvLinkageTypeImport = 1, + SpvLinkageTypeLinkOnceODR = 2, + SpvLinkageTypeMax = 0x7fffffff, +} SpvLinkageType; + +typedef enum SpvAccessQualifier_ { + SpvAccessQualifierReadOnly = 0, + SpvAccessQualifierWriteOnly = 1, + SpvAccessQualifierReadWrite = 2, + SpvAccessQualifierMax = 0x7fffffff, +} SpvAccessQualifier; + +typedef enum SpvFunctionParameterAttribute_ { + SpvFunctionParameterAttributeZext = 0, + SpvFunctionParameterAttributeSext = 1, + SpvFunctionParameterAttributeByVal = 2, + SpvFunctionParameterAttributeSret = 3, + SpvFunctionParameterAttributeNoAlias = 4, + SpvFunctionParameterAttributeNoCapture = 5, + SpvFunctionParameterAttributeNoWrite = 6, + SpvFunctionParameterAttributeNoReadWrite = 7, + SpvFunctionParameterAttributeRuntimeAlignedALTERA = 5940, + SpvFunctionParameterAttributeRuntimeAlignedINTEL = 5940, + SpvFunctionParameterAttributeMax = 0x7fffffff, +} SpvFunctionParameterAttribute; + +typedef enum SpvDecoration_ { + SpvDecorationRelaxedPrecision = 0, + SpvDecorationSpecId = 1, + SpvDecorationBlock = 2, + SpvDecorationBufferBlock = 3, + SpvDecorationRowMajor = 4, + SpvDecorationColMajor = 5, + SpvDecorationArrayStride = 6, + SpvDecorationMatrixStride = 7, + SpvDecorationGLSLShared = 8, + SpvDecorationGLSLPacked = 9, + SpvDecorationCPacked = 10, + SpvDecorationBuiltIn = 11, + SpvDecorationNoPerspective = 13, + SpvDecorationFlat = 14, + SpvDecorationPatch = 15, + SpvDecorationCentroid = 16, + SpvDecorationSample = 17, + SpvDecorationInvariant = 18, + SpvDecorationRestrict = 19, + SpvDecorationAliased = 20, + SpvDecorationVolatile = 21, + SpvDecorationConstant = 22, + SpvDecorationCoherent = 23, + SpvDecorationNonWritable = 24, + SpvDecorationNonReadable = 25, + SpvDecorationUniform = 26, + SpvDecorationUniformId = 27, + SpvDecorationSaturatedConversion = 28, + SpvDecorationStream = 29, + SpvDecorationLocation = 30, + SpvDecorationComponent = 31, + SpvDecorationIndex = 32, + SpvDecorationBinding = 33, + SpvDecorationDescriptorSet = 34, + SpvDecorationOffset = 35, + SpvDecorationXfbBuffer = 36, + SpvDecorationXfbStride = 37, + SpvDecorationFuncParamAttr = 38, + SpvDecorationFPRoundingMode = 39, + SpvDecorationFPFastMathMode = 40, + SpvDecorationLinkageAttributes = 41, + SpvDecorationNoContraction = 42, + SpvDecorationInputAttachmentIndex = 43, + SpvDecorationAlignment = 44, + SpvDecorationMaxByteOffset = 45, + SpvDecorationAlignmentId = 46, + SpvDecorationMaxByteOffsetId = 47, + SpvDecorationSaturatedToLargestFloat8NormalConversionEXT = 4216, + SpvDecorationNoSignedWrap = 4469, + SpvDecorationNoUnsignedWrap = 4470, + SpvDecorationWeightTextureQCOM = 4487, + SpvDecorationBlockMatchTextureQCOM = 4488, + SpvDecorationBlockMatchSamplerQCOM = 4499, + SpvDecorationExplicitInterpAMD = 4999, + SpvDecorationNodeSharesPayloadLimitsWithAMDX = 5019, + SpvDecorationNodeMaxPayloadsAMDX = 5020, + SpvDecorationTrackFinishWritingAMDX = 5078, + SpvDecorationPayloadNodeNameAMDX = 5091, + SpvDecorationPayloadNodeBaseIndexAMDX = 5098, + SpvDecorationPayloadNodeSparseArrayAMDX = 5099, + SpvDecorationPayloadNodeArraySizeAMDX = 5100, + SpvDecorationPayloadDispatchIndirectAMDX = 5105, + SpvDecorationArrayStrideIdEXT = 5124, + SpvDecorationOffsetIdEXT = 5125, + SpvDecorationUTFEncodedKHR = 5145, + SpvDecorationOverrideCoverageNV = 5248, + SpvDecorationPassthroughNV = 5250, + SpvDecorationViewportRelativeNV = 5252, + SpvDecorationSecondaryViewportRelativeNV = 5256, + SpvDecorationPerPrimitiveEXT = 5271, + SpvDecorationPerPrimitiveNV = 5271, + SpvDecorationPerViewNV = 5272, + SpvDecorationPerTaskNV = 5273, + SpvDecorationPerVertexKHR = 5285, + SpvDecorationPerVertexNV = 5285, + SpvDecorationNonUniform = 5300, + SpvDecorationNonUniformEXT = 5300, + SpvDecorationRestrictPointer = 5355, + SpvDecorationRestrictPointerEXT = 5355, + SpvDecorationAliasedPointer = 5356, + SpvDecorationAliasedPointerEXT = 5356, + SpvDecorationMemberOffsetNV = 5358, + SpvDecorationHitObjectShaderRecordBufferNV = 5386, + SpvDecorationHitObjectShaderRecordBufferEXT = 5389, + SpvDecorationBankNV = 5397, + SpvDecorationBindlessSamplerNV = 5398, + SpvDecorationBindlessImageNV = 5399, + SpvDecorationBoundSamplerNV = 5400, + SpvDecorationBoundImageNV = 5401, + SpvDecorationSIMTCallINTEL = 5599, + SpvDecorationReferencedIndirectlyINTEL = 5602, + SpvDecorationClobberINTEL = 5607, + SpvDecorationSideEffectsINTEL = 5608, + SpvDecorationVectorComputeVariableINTEL = 5624, + SpvDecorationFuncParamIOKindINTEL = 5625, + SpvDecorationVectorComputeFunctionINTEL = 5626, + SpvDecorationStackCallINTEL = 5627, + SpvDecorationGlobalVariableOffsetINTEL = 5628, + SpvDecorationCounterBuffer = 5634, + SpvDecorationHlslCounterBufferGOOGLE = 5634, + SpvDecorationHlslSemanticGOOGLE = 5635, + SpvDecorationUserSemantic = 5635, + SpvDecorationUserTypeGOOGLE = 5636, + SpvDecorationFunctionRoundingModeINTEL = 5822, + SpvDecorationFunctionDenormModeINTEL = 5823, + SpvDecorationRegisterALTERA = 5825, + SpvDecorationRegisterINTEL = 5825, + SpvDecorationMemoryALTERA = 5826, + SpvDecorationMemoryINTEL = 5826, + SpvDecorationNumbanksALTERA = 5827, + SpvDecorationNumbanksINTEL = 5827, + SpvDecorationBankwidthALTERA = 5828, + SpvDecorationBankwidthINTEL = 5828, + SpvDecorationMaxPrivateCopiesALTERA = 5829, + SpvDecorationMaxPrivateCopiesINTEL = 5829, + SpvDecorationSinglepumpALTERA = 5830, + SpvDecorationSinglepumpINTEL = 5830, + SpvDecorationDoublepumpALTERA = 5831, + SpvDecorationDoublepumpINTEL = 5831, + SpvDecorationMaxReplicatesALTERA = 5832, + SpvDecorationMaxReplicatesINTEL = 5832, + SpvDecorationSimpleDualPortALTERA = 5833, + SpvDecorationSimpleDualPortINTEL = 5833, + SpvDecorationMergeALTERA = 5834, + SpvDecorationMergeINTEL = 5834, + SpvDecorationBankBitsALTERA = 5835, + SpvDecorationBankBitsINTEL = 5835, + SpvDecorationForcePow2DepthALTERA = 5836, + SpvDecorationForcePow2DepthINTEL = 5836, + SpvDecorationStridesizeALTERA = 5883, + SpvDecorationStridesizeINTEL = 5883, + SpvDecorationWordsizeALTERA = 5884, + SpvDecorationWordsizeINTEL = 5884, + SpvDecorationTrueDualPortALTERA = 5885, + SpvDecorationTrueDualPortINTEL = 5885, + SpvDecorationBurstCoalesceALTERA = 5899, + SpvDecorationBurstCoalesceINTEL = 5899, + SpvDecorationCacheSizeALTERA = 5900, + SpvDecorationCacheSizeINTEL = 5900, + SpvDecorationDontStaticallyCoalesceALTERA = 5901, + SpvDecorationDontStaticallyCoalesceINTEL = 5901, + SpvDecorationPrefetchALTERA = 5902, + SpvDecorationPrefetchINTEL = 5902, + SpvDecorationStallEnableALTERA = 5905, + SpvDecorationStallEnableINTEL = 5905, + SpvDecorationFuseLoopsInFunctionALTERA = 5907, + SpvDecorationFuseLoopsInFunctionINTEL = 5907, + SpvDecorationMathOpDSPModeALTERA = 5909, + SpvDecorationMathOpDSPModeINTEL = 5909, + SpvDecorationAliasScopeINTEL = 5914, + SpvDecorationNoAliasINTEL = 5915, + SpvDecorationInitiationIntervalALTERA = 5917, + SpvDecorationInitiationIntervalINTEL = 5917, + SpvDecorationMaxConcurrencyALTERA = 5918, + SpvDecorationMaxConcurrencyINTEL = 5918, + SpvDecorationPipelineEnableALTERA = 5919, + SpvDecorationPipelineEnableINTEL = 5919, + SpvDecorationBufferLocationALTERA = 5921, + SpvDecorationBufferLocationINTEL = 5921, + SpvDecorationIOPipeStorageALTERA = 5944, + SpvDecorationIOPipeStorageINTEL = 5944, + SpvDecorationFunctionFloatingPointModeINTEL = 6080, + SpvDecorationSingleElementVectorINTEL = 6085, + SpvDecorationVectorComputeCallableFunctionINTEL = 6087, + SpvDecorationMediaBlockIOINTEL = 6140, + SpvDecorationStallFreeALTERA = 6151, + SpvDecorationStallFreeINTEL = 6151, + SpvDecorationFPMaxErrorDecorationINTEL = 6170, + SpvDecorationLatencyControlLabelALTERA = 6172, + SpvDecorationLatencyControlLabelINTEL = 6172, + SpvDecorationLatencyControlConstraintALTERA = 6173, + SpvDecorationLatencyControlConstraintINTEL = 6173, + SpvDecorationConduitKernelArgumentALTERA = 6175, + SpvDecorationConduitKernelArgumentINTEL = 6175, + SpvDecorationRegisterMapKernelArgumentALTERA = 6176, + SpvDecorationRegisterMapKernelArgumentINTEL = 6176, + SpvDecorationMMHostInterfaceAddressWidthALTERA = 6177, + SpvDecorationMMHostInterfaceAddressWidthINTEL = 6177, + SpvDecorationMMHostInterfaceDataWidthALTERA = 6178, + SpvDecorationMMHostInterfaceDataWidthINTEL = 6178, + SpvDecorationMMHostInterfaceLatencyALTERA = 6179, + SpvDecorationMMHostInterfaceLatencyINTEL = 6179, + SpvDecorationMMHostInterfaceReadWriteModeALTERA = 6180, + SpvDecorationMMHostInterfaceReadWriteModeINTEL = 6180, + SpvDecorationMMHostInterfaceMaxBurstALTERA = 6181, + SpvDecorationMMHostInterfaceMaxBurstINTEL = 6181, + SpvDecorationMMHostInterfaceWaitRequestALTERA = 6182, + SpvDecorationMMHostInterfaceWaitRequestINTEL = 6182, + SpvDecorationStableKernelArgumentALTERA = 6183, + SpvDecorationStableKernelArgumentINTEL = 6183, + SpvDecorationHostAccessINTEL = 6188, + SpvDecorationInitModeALTERA = 6190, + SpvDecorationInitModeINTEL = 6190, + SpvDecorationImplementInRegisterMapALTERA = 6191, + SpvDecorationImplementInRegisterMapINTEL = 6191, + SpvDecorationConditionalINTEL = 6247, + SpvDecorationCacheControlLoadINTEL = 6442, + SpvDecorationCacheControlStoreINTEL = 6443, + SpvDecorationMax = 0x7fffffff, +} SpvDecoration; + +typedef enum SpvBuiltIn_ { + SpvBuiltInPosition = 0, + SpvBuiltInPointSize = 1, + SpvBuiltInClipDistance = 3, + SpvBuiltInCullDistance = 4, + SpvBuiltInVertexId = 5, + SpvBuiltInInstanceId = 6, + SpvBuiltInPrimitiveId = 7, + SpvBuiltInInvocationId = 8, + SpvBuiltInLayer = 9, + SpvBuiltInViewportIndex = 10, + SpvBuiltInTessLevelOuter = 11, + SpvBuiltInTessLevelInner = 12, + SpvBuiltInTessCoord = 13, + SpvBuiltInPatchVertices = 14, + SpvBuiltInFragCoord = 15, + SpvBuiltInPointCoord = 16, + SpvBuiltInFrontFacing = 17, + SpvBuiltInSampleId = 18, + SpvBuiltInSamplePosition = 19, + SpvBuiltInSampleMask = 20, + SpvBuiltInFragDepth = 22, + SpvBuiltInHelperInvocation = 23, + SpvBuiltInNumWorkgroups = 24, + SpvBuiltInWorkgroupSize = 25, + SpvBuiltInWorkgroupId = 26, + SpvBuiltInLocalInvocationId = 27, + SpvBuiltInGlobalInvocationId = 28, + SpvBuiltInLocalInvocationIndex = 29, + SpvBuiltInWorkDim = 30, + SpvBuiltInGlobalSize = 31, + SpvBuiltInEnqueuedWorkgroupSize = 32, + SpvBuiltInGlobalOffset = 33, + SpvBuiltInGlobalLinearId = 34, + SpvBuiltInSubgroupSize = 36, + SpvBuiltInSubgroupMaxSize = 37, + SpvBuiltInNumSubgroups = 38, + SpvBuiltInNumEnqueuedSubgroups = 39, + SpvBuiltInSubgroupId = 40, + SpvBuiltInSubgroupLocalInvocationId = 41, + SpvBuiltInVertexIndex = 42, + SpvBuiltInInstanceIndex = 43, + SpvBuiltInCoreIDARM = 4160, + SpvBuiltInCoreCountARM = 4161, + SpvBuiltInCoreMaxIDARM = 4162, + SpvBuiltInWarpIDARM = 4163, + SpvBuiltInWarpMaxIDARM = 4164, + SpvBuiltInSubgroupEqMask = 4416, + SpvBuiltInSubgroupEqMaskKHR = 4416, + SpvBuiltInSubgroupGeMask = 4417, + SpvBuiltInSubgroupGeMaskKHR = 4417, + SpvBuiltInSubgroupGtMask = 4418, + SpvBuiltInSubgroupGtMaskKHR = 4418, + SpvBuiltInSubgroupLeMask = 4419, + SpvBuiltInSubgroupLeMaskKHR = 4419, + SpvBuiltInSubgroupLtMask = 4420, + SpvBuiltInSubgroupLtMaskKHR = 4420, + SpvBuiltInBaseVertex = 4424, + SpvBuiltInBaseInstance = 4425, + SpvBuiltInDrawIndex = 4426, + SpvBuiltInPrimitiveShadingRateKHR = 4432, + SpvBuiltInDeviceIndex = 4438, + SpvBuiltInViewIndex = 4440, + SpvBuiltInShadingRateKHR = 4444, + SpvBuiltInTileOffsetQCOM = 4492, + SpvBuiltInTileDimensionQCOM = 4493, + SpvBuiltInTileApronSizeQCOM = 4494, + SpvBuiltInBaryCoordNoPerspAMD = 4992, + SpvBuiltInBaryCoordNoPerspCentroidAMD = 4993, + SpvBuiltInBaryCoordNoPerspSampleAMD = 4994, + SpvBuiltInBaryCoordSmoothAMD = 4995, + SpvBuiltInBaryCoordSmoothCentroidAMD = 4996, + SpvBuiltInBaryCoordSmoothSampleAMD = 4997, + SpvBuiltInBaryCoordPullModelAMD = 4998, + SpvBuiltInFragStencilRefEXT = 5014, + SpvBuiltInRemainingRecursionLevelsAMDX = 5021, + SpvBuiltInShaderIndexAMDX = 5073, + SpvBuiltInSamplerHeapEXT = 5122, + SpvBuiltInResourceHeapEXT = 5123, + SpvBuiltInViewportMaskNV = 5253, + SpvBuiltInSecondaryPositionNV = 5257, + SpvBuiltInSecondaryViewportMaskNV = 5258, + SpvBuiltInPositionPerViewNV = 5261, + SpvBuiltInViewportMaskPerViewNV = 5262, + SpvBuiltInFullyCoveredEXT = 5264, + SpvBuiltInTaskCountNV = 5274, + SpvBuiltInPrimitiveCountNV = 5275, + SpvBuiltInPrimitiveIndicesNV = 5276, + SpvBuiltInClipDistancePerViewNV = 5277, + SpvBuiltInCullDistancePerViewNV = 5278, + SpvBuiltInLayerPerViewNV = 5279, + SpvBuiltInMeshViewCountNV = 5280, + SpvBuiltInMeshViewIndicesNV = 5281, + SpvBuiltInBaryCoordKHR = 5286, + SpvBuiltInBaryCoordNV = 5286, + SpvBuiltInBaryCoordNoPerspKHR = 5287, + SpvBuiltInBaryCoordNoPerspNV = 5287, + SpvBuiltInFragSizeEXT = 5292, + SpvBuiltInFragmentSizeNV = 5292, + SpvBuiltInFragInvocationCountEXT = 5293, + SpvBuiltInInvocationsPerPixelNV = 5293, + SpvBuiltInPrimitivePointIndicesEXT = 5294, + SpvBuiltInPrimitiveLineIndicesEXT = 5295, + SpvBuiltInPrimitiveTriangleIndicesEXT = 5296, + SpvBuiltInCullPrimitiveEXT = 5299, + SpvBuiltInLaunchIdKHR = 5319, + SpvBuiltInLaunchIdNV = 5319, + SpvBuiltInLaunchSizeKHR = 5320, + SpvBuiltInLaunchSizeNV = 5320, + SpvBuiltInWorldRayOriginKHR = 5321, + SpvBuiltInWorldRayOriginNV = 5321, + SpvBuiltInWorldRayDirectionKHR = 5322, + SpvBuiltInWorldRayDirectionNV = 5322, + SpvBuiltInObjectRayOriginKHR = 5323, + SpvBuiltInObjectRayOriginNV = 5323, + SpvBuiltInObjectRayDirectionKHR = 5324, + SpvBuiltInObjectRayDirectionNV = 5324, + SpvBuiltInRayTminKHR = 5325, + SpvBuiltInRayTminNV = 5325, + SpvBuiltInRayTmaxKHR = 5326, + SpvBuiltInRayTmaxNV = 5326, + SpvBuiltInInstanceCustomIndexKHR = 5327, + SpvBuiltInInstanceCustomIndexNV = 5327, + SpvBuiltInObjectToWorldKHR = 5330, + SpvBuiltInObjectToWorldNV = 5330, + SpvBuiltInWorldToObjectKHR = 5331, + SpvBuiltInWorldToObjectNV = 5331, + SpvBuiltInHitTNV = 5332, + SpvBuiltInHitKindKHR = 5333, + SpvBuiltInHitKindNV = 5333, + SpvBuiltInCurrentRayTimeNV = 5334, + SpvBuiltInHitTriangleVertexPositionsKHR = 5335, + SpvBuiltInHitMicroTriangleVertexPositionsNV = 5337, + SpvBuiltInHitMicroTriangleVertexBarycentricsNV = 5344, + SpvBuiltInIncomingRayFlagsKHR = 5351, + SpvBuiltInIncomingRayFlagsNV = 5351, + SpvBuiltInRayGeometryIndexKHR = 5352, + SpvBuiltInHitIsSphereNV = 5359, + SpvBuiltInHitIsLSSNV = 5360, + SpvBuiltInHitSpherePositionNV = 5361, + SpvBuiltInWarpsPerSMNV = 5374, + SpvBuiltInSMCountNV = 5375, + SpvBuiltInWarpIDNV = 5376, + SpvBuiltInSMIDNV = 5377, + SpvBuiltInHitLSSPositionsNV = 5396, + SpvBuiltInHitKindFrontFacingMicroTriangleNV = 5405, + SpvBuiltInHitKindBackFacingMicroTriangleNV = 5406, + SpvBuiltInHitSphereRadiusNV = 5420, + SpvBuiltInHitLSSRadiiNV = 5421, + SpvBuiltInClusterIDNV = 5436, + SpvBuiltInCullMaskKHR = 6021, + SpvBuiltInMax = 0x7fffffff, +} SpvBuiltIn; + +typedef enum SpvSelectionControlShift_ { + SpvSelectionControlFlattenShift = 0, + SpvSelectionControlDontFlattenShift = 1, + SpvSelectionControlMax = 0x7fffffff, +} SpvSelectionControlShift; + +typedef enum SpvSelectionControlMask_ { + SpvSelectionControlMaskNone = 0, + SpvSelectionControlFlattenMask = 0x00000001, + SpvSelectionControlDontFlattenMask = 0x00000002, +} SpvSelectionControlMask; + +typedef enum SpvLoopControlShift_ { + SpvLoopControlUnrollShift = 0, + SpvLoopControlDontUnrollShift = 1, + SpvLoopControlDependencyInfiniteShift = 2, + SpvLoopControlDependencyLengthShift = 3, + SpvLoopControlMinIterationsShift = 4, + SpvLoopControlMaxIterationsShift = 5, + SpvLoopControlIterationMultipleShift = 6, + SpvLoopControlPeelCountShift = 7, + SpvLoopControlPartialCountShift = 8, + SpvLoopControlInitiationIntervalALTERAShift = 16, + SpvLoopControlInitiationIntervalINTELShift = 16, + SpvLoopControlMaxConcurrencyALTERAShift = 17, + SpvLoopControlMaxConcurrencyINTELShift = 17, + SpvLoopControlDependencyArrayALTERAShift = 18, + SpvLoopControlDependencyArrayINTELShift = 18, + SpvLoopControlPipelineEnableALTERAShift = 19, + SpvLoopControlPipelineEnableINTELShift = 19, + SpvLoopControlLoopCoalesceALTERAShift = 20, + SpvLoopControlLoopCoalesceINTELShift = 20, + SpvLoopControlMaxInterleavingALTERAShift = 21, + SpvLoopControlMaxInterleavingINTELShift = 21, + SpvLoopControlSpeculatedIterationsALTERAShift = 22, + SpvLoopControlSpeculatedIterationsINTELShift = 22, + SpvLoopControlNoFusionALTERAShift = 23, + SpvLoopControlNoFusionINTELShift = 23, + SpvLoopControlLoopCountALTERAShift = 24, + SpvLoopControlLoopCountINTELShift = 24, + SpvLoopControlMaxReinvocationDelayALTERAShift = 25, + SpvLoopControlMaxReinvocationDelayINTELShift = 25, + SpvLoopControlMax = 0x7fffffff, +} SpvLoopControlShift; + +typedef enum SpvLoopControlMask_ { + SpvLoopControlMaskNone = 0, + SpvLoopControlUnrollMask = 0x00000001, + SpvLoopControlDontUnrollMask = 0x00000002, + SpvLoopControlDependencyInfiniteMask = 0x00000004, + SpvLoopControlDependencyLengthMask = 0x00000008, + SpvLoopControlMinIterationsMask = 0x00000010, + SpvLoopControlMaxIterationsMask = 0x00000020, + SpvLoopControlIterationMultipleMask = 0x00000040, + SpvLoopControlPeelCountMask = 0x00000080, + SpvLoopControlPartialCountMask = 0x00000100, + SpvLoopControlInitiationIntervalALTERAMask = 0x00010000, + SpvLoopControlInitiationIntervalINTELMask = 0x00010000, + SpvLoopControlMaxConcurrencyALTERAMask = 0x00020000, + SpvLoopControlMaxConcurrencyINTELMask = 0x00020000, + SpvLoopControlDependencyArrayALTERAMask = 0x00040000, + SpvLoopControlDependencyArrayINTELMask = 0x00040000, + SpvLoopControlPipelineEnableALTERAMask = 0x00080000, + SpvLoopControlPipelineEnableINTELMask = 0x00080000, + SpvLoopControlLoopCoalesceALTERAMask = 0x00100000, + SpvLoopControlLoopCoalesceINTELMask = 0x00100000, + SpvLoopControlMaxInterleavingALTERAMask = 0x00200000, + SpvLoopControlMaxInterleavingINTELMask = 0x00200000, + SpvLoopControlSpeculatedIterationsALTERAMask = 0x00400000, + SpvLoopControlSpeculatedIterationsINTELMask = 0x00400000, + SpvLoopControlNoFusionALTERAMask = 0x00800000, + SpvLoopControlNoFusionINTELMask = 0x00800000, + SpvLoopControlLoopCountALTERAMask = 0x01000000, + SpvLoopControlLoopCountINTELMask = 0x01000000, + SpvLoopControlMaxReinvocationDelayALTERAMask = 0x02000000, + SpvLoopControlMaxReinvocationDelayINTELMask = 0x02000000, +} SpvLoopControlMask; + +typedef enum SpvFunctionControlShift_ { + SpvFunctionControlInlineShift = 0, + SpvFunctionControlDontInlineShift = 1, + SpvFunctionControlPureShift = 2, + SpvFunctionControlConstShift = 3, + SpvFunctionControlOptNoneEXTShift = 16, + SpvFunctionControlOptNoneINTELShift = 16, + SpvFunctionControlMax = 0x7fffffff, +} SpvFunctionControlShift; + +typedef enum SpvFunctionControlMask_ { + SpvFunctionControlMaskNone = 0, + SpvFunctionControlInlineMask = 0x00000001, + SpvFunctionControlDontInlineMask = 0x00000002, + SpvFunctionControlPureMask = 0x00000004, + SpvFunctionControlConstMask = 0x00000008, + SpvFunctionControlOptNoneEXTMask = 0x00010000, + SpvFunctionControlOptNoneINTELMask = 0x00010000, +} SpvFunctionControlMask; + +typedef enum SpvMemorySemanticsShift_ { + SpvMemorySemanticsAcquireShift = 1, + SpvMemorySemanticsReleaseShift = 2, + SpvMemorySemanticsAcquireReleaseShift = 3, + SpvMemorySemanticsSequentiallyConsistentShift = 4, + SpvMemorySemanticsUniformMemoryShift = 6, + SpvMemorySemanticsSubgroupMemoryShift = 7, + SpvMemorySemanticsWorkgroupMemoryShift = 8, + SpvMemorySemanticsCrossWorkgroupMemoryShift = 9, + SpvMemorySemanticsAtomicCounterMemoryShift = 10, + SpvMemorySemanticsImageMemoryShift = 11, + SpvMemorySemanticsOutputMemoryShift = 12, + SpvMemorySemanticsOutputMemoryKHRShift = 12, + SpvMemorySemanticsMakeAvailableShift = 13, + SpvMemorySemanticsMakeAvailableKHRShift = 13, + SpvMemorySemanticsMakeVisibleShift = 14, + SpvMemorySemanticsMakeVisibleKHRShift = 14, + SpvMemorySemanticsVolatileShift = 15, + SpvMemorySemanticsMax = 0x7fffffff, +} SpvMemorySemanticsShift; + +typedef enum SpvMemorySemanticsMask_ { + SpvMemorySemanticsMaskNone = 0, + SpvMemorySemanticsAcquireMask = 0x00000002, + SpvMemorySemanticsReleaseMask = 0x00000004, + SpvMemorySemanticsAcquireReleaseMask = 0x00000008, + SpvMemorySemanticsSequentiallyConsistentMask = 0x00000010, + SpvMemorySemanticsUniformMemoryMask = 0x00000040, + SpvMemorySemanticsSubgroupMemoryMask = 0x00000080, + SpvMemorySemanticsWorkgroupMemoryMask = 0x00000100, + SpvMemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + SpvMemorySemanticsAtomicCounterMemoryMask = 0x00000400, + SpvMemorySemanticsImageMemoryMask = 0x00000800, + SpvMemorySemanticsOutputMemoryMask = 0x00001000, + SpvMemorySemanticsOutputMemoryKHRMask = 0x00001000, + SpvMemorySemanticsMakeAvailableMask = 0x00002000, + SpvMemorySemanticsMakeAvailableKHRMask = 0x00002000, + SpvMemorySemanticsMakeVisibleMask = 0x00004000, + SpvMemorySemanticsMakeVisibleKHRMask = 0x00004000, + SpvMemorySemanticsVolatileMask = 0x00008000, +} SpvMemorySemanticsMask; + +typedef enum SpvMemoryAccessShift_ { + SpvMemoryAccessVolatileShift = 0, + SpvMemoryAccessAlignedShift = 1, + SpvMemoryAccessNontemporalShift = 2, + SpvMemoryAccessMakePointerAvailableShift = 3, + SpvMemoryAccessMakePointerAvailableKHRShift = 3, + SpvMemoryAccessMakePointerVisibleShift = 4, + SpvMemoryAccessMakePointerVisibleKHRShift = 4, + SpvMemoryAccessNonPrivatePointerShift = 5, + SpvMemoryAccessNonPrivatePointerKHRShift = 5, + SpvMemoryAccessAliasScopeINTELMaskShift = 16, + SpvMemoryAccessNoAliasINTELMaskShift = 17, + SpvMemoryAccessMax = 0x7fffffff, +} SpvMemoryAccessShift; + +typedef enum SpvMemoryAccessMask_ { + SpvMemoryAccessMaskNone = 0, + SpvMemoryAccessVolatileMask = 0x00000001, + SpvMemoryAccessAlignedMask = 0x00000002, + SpvMemoryAccessNontemporalMask = 0x00000004, + SpvMemoryAccessMakePointerAvailableMask = 0x00000008, + SpvMemoryAccessMakePointerAvailableKHRMask = 0x00000008, + SpvMemoryAccessMakePointerVisibleMask = 0x00000010, + SpvMemoryAccessMakePointerVisibleKHRMask = 0x00000010, + SpvMemoryAccessNonPrivatePointerMask = 0x00000020, + SpvMemoryAccessNonPrivatePointerKHRMask = 0x00000020, + SpvMemoryAccessAliasScopeINTELMaskMask = 0x00010000, + SpvMemoryAccessNoAliasINTELMaskMask = 0x00020000, +} SpvMemoryAccessMask; + +typedef enum SpvScope_ { + SpvScopeCrossDevice = 0, + SpvScopeDevice = 1, + SpvScopeWorkgroup = 2, + SpvScopeSubgroup = 3, + SpvScopeInvocation = 4, + SpvScopeQueueFamily = 5, + SpvScopeQueueFamilyKHR = 5, + SpvScopeShaderCallKHR = 6, + SpvScopeMax = 0x7fffffff, +} SpvScope; + +typedef enum SpvGroupOperation_ { + SpvGroupOperationReduce = 0, + SpvGroupOperationInclusiveScan = 1, + SpvGroupOperationExclusiveScan = 2, + SpvGroupOperationClusteredReduce = 3, + SpvGroupOperationPartitionedReduceEXT = 6, + SpvGroupOperationPartitionedReduceNV = 6, + SpvGroupOperationPartitionedInclusiveScanEXT = 7, + SpvGroupOperationPartitionedInclusiveScanNV = 7, + SpvGroupOperationPartitionedExclusiveScanEXT = 8, + SpvGroupOperationPartitionedExclusiveScanNV = 8, + SpvGroupOperationMax = 0x7fffffff, +} SpvGroupOperation; + +typedef enum SpvKernelEnqueueFlags_ { + SpvKernelEnqueueFlagsNoWait = 0, + SpvKernelEnqueueFlagsWaitKernel = 1, + SpvKernelEnqueueFlagsWaitWorkGroup = 2, + SpvKernelEnqueueFlagsMax = 0x7fffffff, +} SpvKernelEnqueueFlags; + +typedef enum SpvKernelProfilingInfoShift_ { + SpvKernelProfilingInfoCmdExecTimeShift = 0, + SpvKernelProfilingInfoMax = 0x7fffffff, +} SpvKernelProfilingInfoShift; + +typedef enum SpvKernelProfilingInfoMask_ { + SpvKernelProfilingInfoMaskNone = 0, + SpvKernelProfilingInfoCmdExecTimeMask = 0x00000001, +} SpvKernelProfilingInfoMask; + +typedef enum SpvCapability_ { + SpvCapabilityMatrix = 0, + SpvCapabilityShader = 1, + SpvCapabilityGeometry = 2, + SpvCapabilityTessellation = 3, + SpvCapabilityAddresses = 4, + SpvCapabilityLinkage = 5, + SpvCapabilityKernel = 6, + SpvCapabilityVector16 = 7, + SpvCapabilityFloat16Buffer = 8, + SpvCapabilityFloat16 = 9, + SpvCapabilityFloat64 = 10, + SpvCapabilityInt64 = 11, + SpvCapabilityInt64Atomics = 12, + SpvCapabilityImageBasic = 13, + SpvCapabilityImageReadWrite = 14, + SpvCapabilityImageMipmap = 15, + SpvCapabilityPipes = 17, + SpvCapabilityGroups = 18, + SpvCapabilityDeviceEnqueue = 19, + SpvCapabilityLiteralSampler = 20, + SpvCapabilityAtomicStorage = 21, + SpvCapabilityInt16 = 22, + SpvCapabilityTessellationPointSize = 23, + SpvCapabilityGeometryPointSize = 24, + SpvCapabilityImageGatherExtended = 25, + SpvCapabilityStorageImageMultisample = 27, + SpvCapabilityUniformBufferArrayDynamicIndexing = 28, + SpvCapabilitySampledImageArrayDynamicIndexing = 29, + SpvCapabilityStorageBufferArrayDynamicIndexing = 30, + SpvCapabilityStorageImageArrayDynamicIndexing = 31, + SpvCapabilityClipDistance = 32, + SpvCapabilityCullDistance = 33, + SpvCapabilityImageCubeArray = 34, + SpvCapabilitySampleRateShading = 35, + SpvCapabilityImageRect = 36, + SpvCapabilitySampledRect = 37, + SpvCapabilityGenericPointer = 38, + SpvCapabilityInt8 = 39, + SpvCapabilityInputAttachment = 40, + SpvCapabilitySparseResidency = 41, + SpvCapabilityMinLod = 42, + SpvCapabilitySampled1D = 43, + SpvCapabilityImage1D = 44, + SpvCapabilitySampledCubeArray = 45, + SpvCapabilitySampledBuffer = 46, + SpvCapabilityImageBuffer = 47, + SpvCapabilityImageMSArray = 48, + SpvCapabilityStorageImageExtendedFormats = 49, + SpvCapabilityImageQuery = 50, + SpvCapabilityDerivativeControl = 51, + SpvCapabilityInterpolationFunction = 52, + SpvCapabilityTransformFeedback = 53, + SpvCapabilityGeometryStreams = 54, + SpvCapabilityStorageImageReadWithoutFormat = 55, + SpvCapabilityStorageImageWriteWithoutFormat = 56, + SpvCapabilityMultiViewport = 57, + SpvCapabilitySubgroupDispatch = 58, + SpvCapabilityNamedBarrier = 59, + SpvCapabilityPipeStorage = 60, + SpvCapabilityGroupNonUniform = 61, + SpvCapabilityGroupNonUniformVote = 62, + SpvCapabilityGroupNonUniformArithmetic = 63, + SpvCapabilityGroupNonUniformBallot = 64, + SpvCapabilityGroupNonUniformShuffle = 65, + SpvCapabilityGroupNonUniformShuffleRelative = 66, + SpvCapabilityGroupNonUniformClustered = 67, + SpvCapabilityGroupNonUniformQuad = 68, + SpvCapabilityShaderLayer = 69, + SpvCapabilityShaderViewportIndex = 70, + SpvCapabilityUniformDecoration = 71, + SpvCapabilityCoreBuiltinsARM = 4165, + SpvCapabilityTileImageColorReadAccessEXT = 4166, + SpvCapabilityTileImageDepthReadAccessEXT = 4167, + SpvCapabilityTileImageStencilReadAccessEXT = 4168, + SpvCapabilityTensorsARM = 4174, + SpvCapabilityStorageTensorArrayDynamicIndexingARM = 4175, + SpvCapabilityStorageTensorArrayNonUniformIndexingARM = 4176, + SpvCapabilityGraphARM = 4191, + SpvCapabilityCooperativeMatrixLayoutsARM = 4201, + SpvCapabilityFloat8EXT = 4212, + SpvCapabilityFloat8CooperativeMatrixEXT = 4213, + SpvCapabilityFragmentShadingRateKHR = 4422, + SpvCapabilitySubgroupBallotKHR = 4423, + SpvCapabilityDrawParameters = 4427, + SpvCapabilityWorkgroupMemoryExplicitLayoutKHR = 4428, + SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + SpvCapabilitySubgroupVoteKHR = 4431, + SpvCapabilityStorageBuffer16BitAccess = 4433, + SpvCapabilityStorageUniformBufferBlock16 = 4433, + SpvCapabilityStorageUniform16 = 4434, + SpvCapabilityUniformAndStorageBuffer16BitAccess = 4434, + SpvCapabilityStoragePushConstant16 = 4435, + SpvCapabilityStorageInputOutput16 = 4436, + SpvCapabilityDeviceGroup = 4437, + SpvCapabilityMultiView = 4439, + SpvCapabilityVariablePointersStorageBuffer = 4441, + SpvCapabilityVariablePointers = 4442, + SpvCapabilityAtomicStorageOps = 4445, + SpvCapabilitySampleMaskPostDepthCoverage = 4447, + SpvCapabilityStorageBuffer8BitAccess = 4448, + SpvCapabilityUniformAndStorageBuffer8BitAccess = 4449, + SpvCapabilityStoragePushConstant8 = 4450, + SpvCapabilityDenormPreserve = 4464, + SpvCapabilityDenormFlushToZero = 4465, + SpvCapabilitySignedZeroInfNanPreserve = 4466, + SpvCapabilityRoundingModeRTE = 4467, + SpvCapabilityRoundingModeRTZ = 4468, + SpvCapabilityRayQueryProvisionalKHR = 4471, + SpvCapabilityRayQueryKHR = 4472, + SpvCapabilityUntypedPointersKHR = 4473, + SpvCapabilityRayTraversalPrimitiveCullingKHR = 4478, + SpvCapabilityRayTracingKHR = 4479, + SpvCapabilityTextureSampleWeightedQCOM = 4484, + SpvCapabilityTextureBoxFilterQCOM = 4485, + SpvCapabilityTextureBlockMatchQCOM = 4486, + SpvCapabilityTileShadingQCOM = 4495, + SpvCapabilityCooperativeMatrixConversionQCOM = 4496, + SpvCapabilityTextureBlockMatch2QCOM = 4498, + SpvCapabilityFloat16ImageAMD = 5008, + SpvCapabilityImageGatherBiasLodAMD = 5009, + SpvCapabilityFragmentMaskAMD = 5010, + SpvCapabilityStencilExportEXT = 5013, + SpvCapabilityImageReadWriteLodAMD = 5015, + SpvCapabilityInt64ImageEXT = 5016, + SpvCapabilityShaderClockKHR = 5055, + SpvCapabilityShaderEnqueueAMDX = 5067, + SpvCapabilityQuadControlKHR = 5087, + SpvCapabilityInt4TypeINTEL = 5112, + SpvCapabilityInt4CooperativeMatrixINTEL = 5114, + SpvCapabilityBFloat16TypeKHR = 5116, + SpvCapabilityBFloat16DotProductKHR = 5117, + SpvCapabilityBFloat16CooperativeMatrixKHR = 5118, + SpvCapabilityAbortKHR = 5120, + SpvCapabilityDescriptorHeapEXT = 5128, + SpvCapabilityConstantDataKHR = 5146, + SpvCapabilityPoisonFreezeKHR = 5156, + SpvCapabilitySampleMaskOverrideCoverageNV = 5249, + SpvCapabilityGeometryShaderPassthroughNV = 5251, + SpvCapabilityShaderViewportIndexLayerEXT = 5254, + SpvCapabilityShaderViewportIndexLayerNV = 5254, + SpvCapabilityShaderViewportMaskNV = 5255, + SpvCapabilityShaderStereoViewNV = 5259, + SpvCapabilityPerViewAttributesNV = 5260, + SpvCapabilityFragmentFullyCoveredEXT = 5265, + SpvCapabilityMeshShadingNV = 5266, + SpvCapabilityImageFootprintNV = 5282, + SpvCapabilityMeshShadingEXT = 5283, + SpvCapabilityFragmentBarycentricKHR = 5284, + SpvCapabilityFragmentBarycentricNV = 5284, + SpvCapabilityComputeDerivativeGroupQuadsKHR = 5288, + SpvCapabilityComputeDerivativeGroupQuadsNV = 5288, + SpvCapabilityFragmentDensityEXT = 5291, + SpvCapabilityShadingRateNV = 5291, + SpvCapabilityGroupNonUniformPartitionedEXT = 5297, + SpvCapabilityGroupNonUniformPartitionedNV = 5297, + SpvCapabilityShaderNonUniform = 5301, + SpvCapabilityShaderNonUniformEXT = 5301, + SpvCapabilityRuntimeDescriptorArray = 5302, + SpvCapabilityRuntimeDescriptorArrayEXT = 5302, + SpvCapabilityInputAttachmentArrayDynamicIndexing = 5303, + SpvCapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + SpvCapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + SpvCapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + SpvCapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + SpvCapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + SpvCapabilityUniformBufferArrayNonUniformIndexing = 5306, + SpvCapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + SpvCapabilitySampledImageArrayNonUniformIndexing = 5307, + SpvCapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + SpvCapabilityStorageBufferArrayNonUniformIndexing = 5308, + SpvCapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + SpvCapabilityStorageImageArrayNonUniformIndexing = 5309, + SpvCapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + SpvCapabilityInputAttachmentArrayNonUniformIndexing = 5310, + SpvCapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + SpvCapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + SpvCapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + SpvCapabilityRayTracingPositionFetchKHR = 5336, + SpvCapabilityRayTracingNV = 5340, + SpvCapabilityRayTracingMotionBlurNV = 5341, + SpvCapabilityVulkanMemoryModel = 5345, + SpvCapabilityVulkanMemoryModelKHR = 5345, + SpvCapabilityVulkanMemoryModelDeviceScope = 5346, + SpvCapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + SpvCapabilityPhysicalStorageBufferAddresses = 5347, + SpvCapabilityPhysicalStorageBufferAddressesEXT = 5347, + SpvCapabilityComputeDerivativeGroupLinearKHR = 5350, + SpvCapabilityComputeDerivativeGroupLinearNV = 5350, + SpvCapabilityRayTracingProvisionalKHR = 5353, + SpvCapabilityCooperativeMatrixNV = 5357, + SpvCapabilityFragmentShaderSampleInterlockEXT = 5363, + SpvCapabilityFragmentShaderShadingRateInterlockEXT = 5372, + SpvCapabilityShaderSMBuiltinsNV = 5373, + SpvCapabilityFragmentShaderPixelInterlockEXT = 5378, + SpvCapabilityDemoteToHelperInvocation = 5379, + SpvCapabilityDemoteToHelperInvocationEXT = 5379, + SpvCapabilityDisplacementMicromapNV = 5380, + SpvCapabilityRayTracingOpacityMicromapEXT = 5381, + SpvCapabilityShaderInvocationReorderNV = 5383, + SpvCapabilityShaderInvocationReorderEXT = 5388, + SpvCapabilityBindlessTextureNV = 5390, + SpvCapabilityRayQueryPositionFetchKHR = 5391, + SpvCapabilityCooperativeVectorNV = 5394, + SpvCapabilityAtomicFloat16VectorNV = 5404, + SpvCapabilityRayTracingDisplacementMicromapNV = 5409, + SpvCapabilityRawAccessChainsNV = 5414, + SpvCapabilityRayTracingSpheresGeometryNV = 5418, + SpvCapabilityRayTracingLinearSweptSpheresGeometryNV = 5419, + SpvCapabilityPushConstantBanksNV = 5423, + SpvCapabilityLongVectorEXT = 5425, + SpvCapabilityShader64BitIndexingEXT = 5426, + SpvCapabilityCooperativeMatrixReductionsNV = 5430, + SpvCapabilityCooperativeMatrixConversionsNV = 5431, + SpvCapabilityCooperativeMatrixPerElementOperationsNV = 5432, + SpvCapabilityCooperativeMatrixTensorAddressingNV = 5433, + SpvCapabilityCooperativeMatrixBlockLoadsNV = 5434, + SpvCapabilityCooperativeVectorTrainingNV = 5435, + SpvCapabilityRayTracingClusterAccelerationStructureNV = 5437, + SpvCapabilityTensorAddressingNV = 5439, + SpvCapabilitySubgroupShuffleINTEL = 5568, + SpvCapabilitySubgroupBufferBlockIOINTEL = 5569, + SpvCapabilitySubgroupImageBlockIOINTEL = 5570, + SpvCapabilitySubgroupImageMediaBlockIOINTEL = 5579, + SpvCapabilityRoundToInfinityINTEL = 5582, + SpvCapabilityFloatingPointModeINTEL = 5583, + SpvCapabilityIntegerFunctions2INTEL = 5584, + SpvCapabilityFunctionPointersINTEL = 5603, + SpvCapabilityIndirectReferencesINTEL = 5604, + SpvCapabilityAsmINTEL = 5606, + SpvCapabilityAtomicFloat32MinMaxEXT = 5612, + SpvCapabilityAtomicFloat64MinMaxEXT = 5613, + SpvCapabilityAtomicFloat16MinMaxEXT = 5616, + SpvCapabilityVectorComputeINTEL = 5617, + SpvCapabilityVectorAnyINTEL = 5619, + SpvCapabilityExpectAssumeKHR = 5629, + SpvCapabilitySubgroupAvcMotionEstimationINTEL = 5696, + SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + SpvCapabilityVariableLengthArrayINTEL = 5817, + SpvCapabilityFunctionFloatControlINTEL = 5821, + SpvCapabilityFPGAMemoryAttributesALTERA = 5824, + SpvCapabilityFPGAMemoryAttributesINTEL = 5824, + SpvCapabilityFPFastMathModeINTEL = 5837, + SpvCapabilityArbitraryPrecisionIntegersALTERA = 5844, + SpvCapabilityArbitraryPrecisionIntegersINTEL = 5844, + SpvCapabilityArbitraryPrecisionFloatingPointALTERA = 5845, + SpvCapabilityArbitraryPrecisionFloatingPointINTEL = 5845, + SpvCapabilityUnstructuredLoopControlsINTEL = 5886, + SpvCapabilityFPGALoopControlsALTERA = 5888, + SpvCapabilityFPGALoopControlsINTEL = 5888, + SpvCapabilityKernelAttributesINTEL = 5892, + SpvCapabilityFPGAKernelAttributesINTEL = 5897, + SpvCapabilityFPGAMemoryAccessesALTERA = 5898, + SpvCapabilityFPGAMemoryAccessesINTEL = 5898, + SpvCapabilityFPGAClusterAttributesALTERA = 5904, + SpvCapabilityFPGAClusterAttributesINTEL = 5904, + SpvCapabilityLoopFuseALTERA = 5906, + SpvCapabilityLoopFuseINTEL = 5906, + SpvCapabilityFPGADSPControlALTERA = 5908, + SpvCapabilityFPGADSPControlINTEL = 5908, + SpvCapabilityMemoryAccessAliasingINTEL = 5910, + SpvCapabilityFPGAInvocationPipeliningAttributesALTERA = 5916, + SpvCapabilityFPGAInvocationPipeliningAttributesINTEL = 5916, + SpvCapabilityFPGABufferLocationALTERA = 5920, + SpvCapabilityFPGABufferLocationINTEL = 5920, + SpvCapabilityArbitraryPrecisionFixedPointALTERA = 5922, + SpvCapabilityArbitraryPrecisionFixedPointINTEL = 5922, + SpvCapabilityUSMStorageClassesALTERA = 5935, + SpvCapabilityUSMStorageClassesINTEL = 5935, + SpvCapabilityRuntimeAlignedAttributeALTERA = 5939, + SpvCapabilityRuntimeAlignedAttributeINTEL = 5939, + SpvCapabilityIOPipesALTERA = 5943, + SpvCapabilityIOPipesINTEL = 5943, + SpvCapabilityBlockingPipesALTERA = 5945, + SpvCapabilityBlockingPipesINTEL = 5945, + SpvCapabilityFPGARegALTERA = 5948, + SpvCapabilityFPGARegINTEL = 5948, + SpvCapabilityDotProductInputAll = 6016, + SpvCapabilityDotProductInputAllKHR = 6016, + SpvCapabilityDotProductInput4x8Bit = 6017, + SpvCapabilityDotProductInput4x8BitKHR = 6017, + SpvCapabilityDotProductInput4x8BitPacked = 6018, + SpvCapabilityDotProductInput4x8BitPackedKHR = 6018, + SpvCapabilityDotProduct = 6019, + SpvCapabilityDotProductKHR = 6019, + SpvCapabilityRayCullMaskKHR = 6020, + SpvCapabilityCooperativeMatrixKHR = 6022, + SpvCapabilityReplicatedCompositesEXT = 6024, + SpvCapabilityBitInstructions = 6025, + SpvCapabilityGroupNonUniformRotateKHR = 6026, + SpvCapabilityFloatControls2 = 6029, + SpvCapabilityFMAKHR = 6030, + SpvCapabilityAtomicFloat32AddEXT = 6033, + SpvCapabilityAtomicFloat64AddEXT = 6034, + SpvCapabilityLongCompositesINTEL = 6089, + SpvCapabilityOptNoneEXT = 6094, + SpvCapabilityOptNoneINTEL = 6094, + SpvCapabilityAtomicFloat16AddEXT = 6095, + SpvCapabilityDebugInfoModuleINTEL = 6114, + SpvCapabilityBFloat16ConversionINTEL = 6115, + SpvCapabilitySplitBarrierINTEL = 6141, + SpvCapabilityArithmeticFenceEXT = 6144, + SpvCapabilityFPGAClusterAttributesV2ALTERA = 6150, + SpvCapabilityFPGAClusterAttributesV2INTEL = 6150, + SpvCapabilityFPGAKernelAttributesv2INTEL = 6161, + SpvCapabilityTaskSequenceALTERA = 6162, + SpvCapabilityTaskSequenceINTEL = 6162, + SpvCapabilityFPMaxErrorINTEL = 6169, + SpvCapabilityFPGALatencyControlALTERA = 6171, + SpvCapabilityFPGALatencyControlINTEL = 6171, + SpvCapabilityFPGAArgumentInterfacesALTERA = 6174, + SpvCapabilityFPGAArgumentInterfacesINTEL = 6174, + SpvCapabilityGlobalVariableHostAccessINTEL = 6187, + SpvCapabilityGlobalVariableFPGADecorationsALTERA = 6189, + SpvCapabilityGlobalVariableFPGADecorationsINTEL = 6189, + SpvCapabilitySubgroupBufferPrefetchINTEL = 6220, + SpvCapabilitySubgroup2DBlockIOINTEL = 6228, + SpvCapabilitySubgroup2DBlockTransformINTEL = 6229, + SpvCapabilitySubgroup2DBlockTransposeINTEL = 6230, + SpvCapabilitySubgroupMatrixMultiplyAccumulateINTEL = 6236, + SpvCapabilityTernaryBitwiseFunctionINTEL = 6241, + SpvCapabilityUntypedVariableLengthArrayINTEL = 6243, + SpvCapabilitySpecConditionalINTEL = 6245, + SpvCapabilityFunctionVariantsINTEL = 6246, + SpvCapabilityGroupUniformArithmeticKHR = 6400, + SpvCapabilityTensorFloat32RoundingINTEL = 6425, + SpvCapabilityMaskedGatherScatterINTEL = 6427, + SpvCapabilityCacheControlsINTEL = 6441, + SpvCapabilityRegisterLimitsINTEL = 6460, + SpvCapabilityBindlessImagesINTEL = 6528, + SpvCapabilityDotProductFloat16AccFloat32VALVE = 6912, + SpvCapabilityDotProductFloat16AccFloat16VALVE = 6913, + SpvCapabilityDotProductBFloat16AccVALVE = 6914, + SpvCapabilityDotProductFloat8AccFloat32VALVE = 6915, + SpvCapabilityMax = 0x7fffffff, +} SpvCapability; + +typedef enum SpvRayFlagsShift_ { + SpvRayFlagsOpaqueKHRShift = 0, + SpvRayFlagsNoOpaqueKHRShift = 1, + SpvRayFlagsTerminateOnFirstHitKHRShift = 2, + SpvRayFlagsSkipClosestHitShaderKHRShift = 3, + SpvRayFlagsCullBackFacingTrianglesKHRShift = 4, + SpvRayFlagsCullFrontFacingTrianglesKHRShift = 5, + SpvRayFlagsCullOpaqueKHRShift = 6, + SpvRayFlagsCullNoOpaqueKHRShift = 7, + SpvRayFlagsSkipBuiltinPrimitivesNVShift = 8, + SpvRayFlagsSkipTrianglesKHRShift = 8, + SpvRayFlagsSkipAABBsKHRShift = 9, + SpvRayFlagsForceOpacityMicromap2StateEXTShift = 10, + SpvRayFlagsMax = 0x7fffffff, +} SpvRayFlagsShift; + +typedef enum SpvRayFlagsMask_ { + SpvRayFlagsMaskNone = 0, + SpvRayFlagsOpaqueKHRMask = 0x00000001, + SpvRayFlagsNoOpaqueKHRMask = 0x00000002, + SpvRayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + SpvRayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + SpvRayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + SpvRayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + SpvRayFlagsCullOpaqueKHRMask = 0x00000040, + SpvRayFlagsCullNoOpaqueKHRMask = 0x00000080, + SpvRayFlagsSkipBuiltinPrimitivesNVMask = 0x00000100, + SpvRayFlagsSkipTrianglesKHRMask = 0x00000100, + SpvRayFlagsSkipAABBsKHRMask = 0x00000200, + SpvRayFlagsForceOpacityMicromap2StateEXTMask = 0x00000400, +} SpvRayFlagsMask; + +typedef enum SpvRayQueryIntersection_ { + SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + SpvRayQueryIntersectionMax = 0x7fffffff, +} SpvRayQueryIntersection; + +typedef enum SpvRayQueryCommittedIntersectionType_ { + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + SpvRayQueryCommittedIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCommittedIntersectionType; + +typedef enum SpvRayQueryCandidateIntersectionType_ { + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + SpvRayQueryCandidateIntersectionTypeMax = 0x7fffffff, +} SpvRayQueryCandidateIntersectionType; + +typedef enum SpvFragmentShadingRateShift_ { + SpvFragmentShadingRateVertical2PixelsShift = 0, + SpvFragmentShadingRateVertical4PixelsShift = 1, + SpvFragmentShadingRateHorizontal2PixelsShift = 2, + SpvFragmentShadingRateHorizontal4PixelsShift = 3, + SpvFragmentShadingRateMax = 0x7fffffff, +} SpvFragmentShadingRateShift; + +typedef enum SpvFragmentShadingRateMask_ { + SpvFragmentShadingRateMaskNone = 0, + SpvFragmentShadingRateVertical2PixelsMask = 0x00000001, + SpvFragmentShadingRateVertical4PixelsMask = 0x00000002, + SpvFragmentShadingRateHorizontal2PixelsMask = 0x00000004, + SpvFragmentShadingRateHorizontal4PixelsMask = 0x00000008, +} SpvFragmentShadingRateMask; + +typedef enum SpvFPDenormMode_ { + SpvFPDenormModePreserve = 0, + SpvFPDenormModeFlushToZero = 1, + SpvFPDenormModeMax = 0x7fffffff, +} SpvFPDenormMode; + +typedef enum SpvFPOperationMode_ { + SpvFPOperationModeIEEE = 0, + SpvFPOperationModeALT = 1, + SpvFPOperationModeMax = 0x7fffffff, +} SpvFPOperationMode; + +typedef enum SpvQuantizationModes_ { + SpvQuantizationModesTRN = 0, + SpvQuantizationModesTRN_ZERO = 1, + SpvQuantizationModesRND = 2, + SpvQuantizationModesRND_ZERO = 3, + SpvQuantizationModesRND_INF = 4, + SpvQuantizationModesRND_MIN_INF = 5, + SpvQuantizationModesRND_CONV = 6, + SpvQuantizationModesRND_CONV_ODD = 7, + SpvQuantizationModesMax = 0x7fffffff, +} SpvQuantizationModes; + +typedef enum SpvOverflowModes_ { + SpvOverflowModesWRAP = 0, + SpvOverflowModesSAT = 1, + SpvOverflowModesSAT_ZERO = 2, + SpvOverflowModesSAT_SYM = 3, + SpvOverflowModesMax = 0x7fffffff, +} SpvOverflowModes; + +typedef enum SpvPackedVectorFormat_ { + SpvPackedVectorFormatPackedVectorFormat4x8Bit = 0, + SpvPackedVectorFormatPackedVectorFormat4x8BitKHR = 0, + SpvPackedVectorFormatMax = 0x7fffffff, +} SpvPackedVectorFormat; + +typedef enum SpvCooperativeMatrixOperandsShift_ { + SpvCooperativeMatrixOperandsMatrixASignedComponentsKHRShift = 0, + SpvCooperativeMatrixOperandsMatrixBSignedComponentsKHRShift = 1, + SpvCooperativeMatrixOperandsMatrixCSignedComponentsKHRShift = 2, + SpvCooperativeMatrixOperandsMatrixResultSignedComponentsKHRShift = 3, + SpvCooperativeMatrixOperandsSaturatingAccumulationKHRShift = 4, + SpvCooperativeMatrixOperandsMax = 0x7fffffff, +} SpvCooperativeMatrixOperandsShift; + +typedef enum SpvCooperativeMatrixOperandsMask_ { + SpvCooperativeMatrixOperandsMaskNone = 0, + SpvCooperativeMatrixOperandsMatrixASignedComponentsKHRMask = 0x00000001, + SpvCooperativeMatrixOperandsMatrixBSignedComponentsKHRMask = 0x00000002, + SpvCooperativeMatrixOperandsMatrixCSignedComponentsKHRMask = 0x00000004, + SpvCooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask = 0x00000008, + SpvCooperativeMatrixOperandsSaturatingAccumulationKHRMask = 0x00000010, +} SpvCooperativeMatrixOperandsMask; + +typedef enum SpvCooperativeMatrixLayout_ { + SpvCooperativeMatrixLayoutRowMajorKHR = 0, + SpvCooperativeMatrixLayoutColumnMajorKHR = 1, + SpvCooperativeMatrixLayoutRowBlockedInterleavedARM = 4202, + SpvCooperativeMatrixLayoutColumnBlockedInterleavedARM = 4203, + SpvCooperativeMatrixLayoutMax = 0x7fffffff, +} SpvCooperativeMatrixLayout; + +typedef enum SpvCooperativeMatrixUse_ { + SpvCooperativeMatrixUseMatrixAKHR = 0, + SpvCooperativeMatrixUseMatrixBKHR = 1, + SpvCooperativeMatrixUseMatrixAccumulatorKHR = 2, + SpvCooperativeMatrixUseMax = 0x7fffffff, +} SpvCooperativeMatrixUse; + +typedef enum SpvCooperativeMatrixReduceShift_ { + SpvCooperativeMatrixReduceRowShift = 0, + SpvCooperativeMatrixReduceColumnShift = 1, + SpvCooperativeMatrixReduce2x2Shift = 2, + SpvCooperativeMatrixReduceMax = 0x7fffffff, +} SpvCooperativeMatrixReduceShift; + +typedef enum SpvCooperativeMatrixReduceMask_ { + SpvCooperativeMatrixReduceMaskNone = 0, + SpvCooperativeMatrixReduceRowMask = 0x00000001, + SpvCooperativeMatrixReduceColumnMask = 0x00000002, + SpvCooperativeMatrixReduce2x2Mask = 0x00000004, +} SpvCooperativeMatrixReduceMask; + +typedef enum SpvTensorClampMode_ { + SpvTensorClampModeUndefined = 0, + SpvTensorClampModeConstant = 1, + SpvTensorClampModeClampToEdge = 2, + SpvTensorClampModeRepeat = 3, + SpvTensorClampModeRepeatMirrored = 4, + SpvTensorClampModeMax = 0x7fffffff, +} SpvTensorClampMode; + +typedef enum SpvTensorAddressingOperandsShift_ { + SpvTensorAddressingOperandsTensorViewShift = 0, + SpvTensorAddressingOperandsDecodeFuncShift = 1, + SpvTensorAddressingOperandsMax = 0x7fffffff, +} SpvTensorAddressingOperandsShift; + +typedef enum SpvTensorAddressingOperandsMask_ { + SpvTensorAddressingOperandsMaskNone = 0, + SpvTensorAddressingOperandsTensorViewMask = 0x00000001, + SpvTensorAddressingOperandsDecodeFuncMask = 0x00000002, +} SpvTensorAddressingOperandsMask; + +typedef enum SpvTensorOperandsShift_ { + SpvTensorOperandsNontemporalARMShift = 0, + SpvTensorOperandsOutOfBoundsValueARMShift = 1, + SpvTensorOperandsMakeElementAvailableARMShift = 2, + SpvTensorOperandsMakeElementVisibleARMShift = 3, + SpvTensorOperandsNonPrivateElementARMShift = 4, + SpvTensorOperandsMax = 0x7fffffff, +} SpvTensorOperandsShift; + +typedef enum SpvTensorOperandsMask_ { + SpvTensorOperandsMaskNone = 0, + SpvTensorOperandsNontemporalARMMask = 0x00000001, + SpvTensorOperandsOutOfBoundsValueARMMask = 0x00000002, + SpvTensorOperandsMakeElementAvailableARMMask = 0x00000004, + SpvTensorOperandsMakeElementVisibleARMMask = 0x00000008, + SpvTensorOperandsNonPrivateElementARMMask = 0x00000010, +} SpvTensorOperandsMask; + +typedef enum SpvInitializationModeQualifier_ { + SpvInitializationModeQualifierInitOnDeviceReprogramALTERA = 0, + SpvInitializationModeQualifierInitOnDeviceReprogramINTEL = 0, + SpvInitializationModeQualifierInitOnDeviceResetALTERA = 1, + SpvInitializationModeQualifierInitOnDeviceResetINTEL = 1, + SpvInitializationModeQualifierMax = 0x7fffffff, +} SpvInitializationModeQualifier; + +typedef enum SpvHostAccessQualifier_ { + SpvHostAccessQualifierNoneINTEL = 0, + SpvHostAccessQualifierReadINTEL = 1, + SpvHostAccessQualifierWriteINTEL = 2, + SpvHostAccessQualifierReadWriteINTEL = 3, + SpvHostAccessQualifierMax = 0x7fffffff, +} SpvHostAccessQualifier; + +typedef enum SpvLoadCacheControl_ { + SpvLoadCacheControlUncachedINTEL = 0, + SpvLoadCacheControlCachedINTEL = 1, + SpvLoadCacheControlStreamingINTEL = 2, + SpvLoadCacheControlInvalidateAfterReadINTEL = 3, + SpvLoadCacheControlConstCachedINTEL = 4, + SpvLoadCacheControlMax = 0x7fffffff, +} SpvLoadCacheControl; + +typedef enum SpvStoreCacheControl_ { + SpvStoreCacheControlUncachedINTEL = 0, + SpvStoreCacheControlWriteThroughINTEL = 1, + SpvStoreCacheControlWriteBackINTEL = 2, + SpvStoreCacheControlStreamingINTEL = 3, + SpvStoreCacheControlMax = 0x7fffffff, +} SpvStoreCacheControl; + +typedef enum SpvNamedMaximumNumberOfRegisters_ { + SpvNamedMaximumNumberOfRegistersAutoINTEL = 0, + SpvNamedMaximumNumberOfRegistersMax = 0x7fffffff, +} SpvNamedMaximumNumberOfRegisters; + +typedef enum SpvMatrixMultiplyAccumulateOperandsShift_ { + SpvMatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELShift = 0, + SpvMatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELShift = 1, + SpvMatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELShift = 2, + SpvMatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELShift = 3, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELShift = 4, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELShift = 5, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELShift = 6, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELShift = 7, + SpvMatrixMultiplyAccumulateOperandsMatrixATF32INTELShift = 8, + SpvMatrixMultiplyAccumulateOperandsMatrixBTF32INTELShift = 9, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELShift = 10, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELShift = 11, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELShift = 12, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELShift = 13, + SpvMatrixMultiplyAccumulateOperandsMax = 0x7fffffff, +} SpvMatrixMultiplyAccumulateOperandsShift; + +typedef enum SpvMatrixMultiplyAccumulateOperandsMask_ { + SpvMatrixMultiplyAccumulateOperandsMaskNone = 0, + SpvMatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELMask = 0x00000001, + SpvMatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELMask = 0x00000002, + SpvMatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELMask = 0x00000004, + SpvMatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELMask = 0x00000008, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELMask = 0x00000010, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELMask = 0x00000020, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELMask = 0x00000040, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELMask = 0x00000080, + SpvMatrixMultiplyAccumulateOperandsMatrixATF32INTELMask = 0x00000100, + SpvMatrixMultiplyAccumulateOperandsMatrixBTF32INTELMask = 0x00000200, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELMask = 0x00000400, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELMask = 0x00000800, + SpvMatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELMask = 0x00001000, + SpvMatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELMask = 0x00002000, +} SpvMatrixMultiplyAccumulateOperandsMask; + +typedef enum SpvRawAccessChainOperandsShift_ { + SpvRawAccessChainOperandsRobustnessPerComponentNVShift = 0, + SpvRawAccessChainOperandsRobustnessPerElementNVShift = 1, + SpvRawAccessChainOperandsMax = 0x7fffffff, +} SpvRawAccessChainOperandsShift; + +typedef enum SpvRawAccessChainOperandsMask_ { + SpvRawAccessChainOperandsMaskNone = 0, + SpvRawAccessChainOperandsRobustnessPerComponentNVMask = 0x00000001, + SpvRawAccessChainOperandsRobustnessPerElementNVMask = 0x00000002, +} SpvRawAccessChainOperandsMask; + +typedef enum SpvFPEncoding_ { + SpvFPEncodingBFloat16KHR = 0, + SpvFPEncodingFloat8E4M3EXT = 4214, + SpvFPEncodingFloat8E5M2EXT = 4215, + SpvFPEncodingMax = 0x7fffffff, +} SpvFPEncoding; + +typedef enum SpvCooperativeVectorMatrixLayout_ { + SpvCooperativeVectorMatrixLayoutRowMajorNV = 0, + SpvCooperativeVectorMatrixLayoutColumnMajorNV = 1, + SpvCooperativeVectorMatrixLayoutInferencingOptimalNV = 2, + SpvCooperativeVectorMatrixLayoutTrainingOptimalNV = 3, + SpvCooperativeVectorMatrixLayoutMax = 0x7fffffff, +} SpvCooperativeVectorMatrixLayout; + +typedef enum SpvComponentType_ { + SpvComponentTypeFloat16NV = 0, + SpvComponentTypeFloat32NV = 1, + SpvComponentTypeFloat64NV = 2, + SpvComponentTypeSignedInt8NV = 3, + SpvComponentTypeSignedInt16NV = 4, + SpvComponentTypeSignedInt32NV = 5, + SpvComponentTypeSignedInt64NV = 6, + SpvComponentTypeUnsignedInt8NV = 7, + SpvComponentTypeUnsignedInt16NV = 8, + SpvComponentTypeUnsignedInt32NV = 9, + SpvComponentTypeUnsignedInt64NV = 10, + SpvComponentTypeSignedInt8PackedNV = 1000491000, + SpvComponentTypeUnsignedInt8PackedNV = 1000491001, + SpvComponentTypeFloatE4M3NV = 1000491002, + SpvComponentTypeFloatE5M2NV = 1000491003, + SpvComponentTypeMax = 0x7fffffff, +} SpvComponentType; + +typedef enum SpvOp_ { + SpvOpNop = 0, + SpvOpUndef = 1, + SpvOpSourceContinued = 2, + SpvOpSource = 3, + SpvOpSourceExtension = 4, + SpvOpName = 5, + SpvOpMemberName = 6, + SpvOpString = 7, + SpvOpLine = 8, + SpvOpExtension = 10, + SpvOpExtInstImport = 11, + SpvOpExtInst = 12, + SpvOpMemoryModel = 14, + SpvOpEntryPoint = 15, + SpvOpExecutionMode = 16, + SpvOpCapability = 17, + SpvOpTypeVoid = 19, + SpvOpTypeBool = 20, + SpvOpTypeInt = 21, + SpvOpTypeFloat = 22, + SpvOpTypeVector = 23, + SpvOpTypeMatrix = 24, + SpvOpTypeImage = 25, + SpvOpTypeSampler = 26, + SpvOpTypeSampledImage = 27, + SpvOpTypeArray = 28, + SpvOpTypeRuntimeArray = 29, + SpvOpTypeStruct = 30, + SpvOpTypeOpaque = 31, + SpvOpTypePointer = 32, + SpvOpTypeFunction = 33, + SpvOpTypeEvent = 34, + SpvOpTypeDeviceEvent = 35, + SpvOpTypeReserveId = 36, + SpvOpTypeQueue = 37, + SpvOpTypePipe = 38, + SpvOpTypeForwardPointer = 39, + SpvOpConstantTrue = 41, + SpvOpConstantFalse = 42, + SpvOpConstant = 43, + SpvOpConstantComposite = 44, + SpvOpConstantSampler = 45, + SpvOpConstantNull = 46, + SpvOpSpecConstantTrue = 48, + SpvOpSpecConstantFalse = 49, + SpvOpSpecConstant = 50, + SpvOpSpecConstantComposite = 51, + SpvOpSpecConstantOp = 52, + SpvOpFunction = 54, + SpvOpFunctionParameter = 55, + SpvOpFunctionEnd = 56, + SpvOpFunctionCall = 57, + SpvOpVariable = 59, + SpvOpImageTexelPointer = 60, + SpvOpLoad = 61, + SpvOpStore = 62, + SpvOpCopyMemory = 63, + SpvOpCopyMemorySized = 64, + SpvOpAccessChain = 65, + SpvOpInBoundsAccessChain = 66, + SpvOpPtrAccessChain = 67, + SpvOpArrayLength = 68, + SpvOpGenericPtrMemSemantics = 69, + SpvOpInBoundsPtrAccessChain = 70, + SpvOpDecorate = 71, + SpvOpMemberDecorate = 72, + SpvOpDecorationGroup = 73, + SpvOpGroupDecorate = 74, + SpvOpGroupMemberDecorate = 75, + SpvOpVectorExtractDynamic = 77, + SpvOpVectorInsertDynamic = 78, + SpvOpVectorShuffle = 79, + SpvOpCompositeConstruct = 80, + SpvOpCompositeExtract = 81, + SpvOpCompositeInsert = 82, + SpvOpCopyObject = 83, + SpvOpTranspose = 84, + SpvOpSampledImage = 86, + SpvOpImageSampleImplicitLod = 87, + SpvOpImageSampleExplicitLod = 88, + SpvOpImageSampleDrefImplicitLod = 89, + SpvOpImageSampleDrefExplicitLod = 90, + SpvOpImageSampleProjImplicitLod = 91, + SpvOpImageSampleProjExplicitLod = 92, + SpvOpImageSampleProjDrefImplicitLod = 93, + SpvOpImageSampleProjDrefExplicitLod = 94, + SpvOpImageFetch = 95, + SpvOpImageGather = 96, + SpvOpImageDrefGather = 97, + SpvOpImageRead = 98, + SpvOpImageWrite = 99, + SpvOpImage = 100, + SpvOpImageQueryFormat = 101, + SpvOpImageQueryOrder = 102, + SpvOpImageQuerySizeLod = 103, + SpvOpImageQuerySize = 104, + SpvOpImageQueryLod = 105, + SpvOpImageQueryLevels = 106, + SpvOpImageQuerySamples = 107, + SpvOpConvertFToU = 109, + SpvOpConvertFToS = 110, + SpvOpConvertSToF = 111, + SpvOpConvertUToF = 112, + SpvOpUConvert = 113, + SpvOpSConvert = 114, + SpvOpFConvert = 115, + SpvOpQuantizeToF16 = 116, + SpvOpConvertPtrToU = 117, + SpvOpSatConvertSToU = 118, + SpvOpSatConvertUToS = 119, + SpvOpConvertUToPtr = 120, + SpvOpPtrCastToGeneric = 121, + SpvOpGenericCastToPtr = 122, + SpvOpGenericCastToPtrExplicit = 123, + SpvOpBitcast = 124, + SpvOpSNegate = 126, + SpvOpFNegate = 127, + SpvOpIAdd = 128, + SpvOpFAdd = 129, + SpvOpISub = 130, + SpvOpFSub = 131, + SpvOpIMul = 132, + SpvOpFMul = 133, + SpvOpUDiv = 134, + SpvOpSDiv = 135, + SpvOpFDiv = 136, + SpvOpUMod = 137, + SpvOpSRem = 138, + SpvOpSMod = 139, + SpvOpFRem = 140, + SpvOpFMod = 141, + SpvOpVectorTimesScalar = 142, + SpvOpMatrixTimesScalar = 143, + SpvOpVectorTimesMatrix = 144, + SpvOpMatrixTimesVector = 145, + SpvOpMatrixTimesMatrix = 146, + SpvOpOuterProduct = 147, + SpvOpDot = 148, + SpvOpIAddCarry = 149, + SpvOpISubBorrow = 150, + SpvOpUMulExtended = 151, + SpvOpSMulExtended = 152, + SpvOpAny = 154, + SpvOpAll = 155, + SpvOpIsNan = 156, + SpvOpIsInf = 157, + SpvOpIsFinite = 158, + SpvOpIsNormal = 159, + SpvOpSignBitSet = 160, + SpvOpLessOrGreater = 161, + SpvOpOrdered = 162, + SpvOpUnordered = 163, + SpvOpLogicalEqual = 164, + SpvOpLogicalNotEqual = 165, + SpvOpLogicalOr = 166, + SpvOpLogicalAnd = 167, + SpvOpLogicalNot = 168, + SpvOpSelect = 169, + SpvOpIEqual = 170, + SpvOpINotEqual = 171, + SpvOpUGreaterThan = 172, + SpvOpSGreaterThan = 173, + SpvOpUGreaterThanEqual = 174, + SpvOpSGreaterThanEqual = 175, + SpvOpULessThan = 176, + SpvOpSLessThan = 177, + SpvOpULessThanEqual = 178, + SpvOpSLessThanEqual = 179, + SpvOpFOrdEqual = 180, + SpvOpFUnordEqual = 181, + SpvOpFOrdNotEqual = 182, + SpvOpFUnordNotEqual = 183, + SpvOpFOrdLessThan = 184, + SpvOpFUnordLessThan = 185, + SpvOpFOrdGreaterThan = 186, + SpvOpFUnordGreaterThan = 187, + SpvOpFOrdLessThanEqual = 188, + SpvOpFUnordLessThanEqual = 189, + SpvOpFOrdGreaterThanEqual = 190, + SpvOpFUnordGreaterThanEqual = 191, + SpvOpShiftRightLogical = 194, + SpvOpShiftRightArithmetic = 195, + SpvOpShiftLeftLogical = 196, + SpvOpBitwiseOr = 197, + SpvOpBitwiseXor = 198, + SpvOpBitwiseAnd = 199, + SpvOpNot = 200, + SpvOpBitFieldInsert = 201, + SpvOpBitFieldSExtract = 202, + SpvOpBitFieldUExtract = 203, + SpvOpBitReverse = 204, + SpvOpBitCount = 205, + SpvOpDPdx = 207, + SpvOpDPdy = 208, + SpvOpFwidth = 209, + SpvOpDPdxFine = 210, + SpvOpDPdyFine = 211, + SpvOpFwidthFine = 212, + SpvOpDPdxCoarse = 213, + SpvOpDPdyCoarse = 214, + SpvOpFwidthCoarse = 215, + SpvOpEmitVertex = 218, + SpvOpEndPrimitive = 219, + SpvOpEmitStreamVertex = 220, + SpvOpEndStreamPrimitive = 221, + SpvOpControlBarrier = 224, + SpvOpMemoryBarrier = 225, + SpvOpAtomicLoad = 227, + SpvOpAtomicStore = 228, + SpvOpAtomicExchange = 229, + SpvOpAtomicCompareExchange = 230, + SpvOpAtomicCompareExchangeWeak = 231, + SpvOpAtomicIIncrement = 232, + SpvOpAtomicIDecrement = 233, + SpvOpAtomicIAdd = 234, + SpvOpAtomicISub = 235, + SpvOpAtomicSMin = 236, + SpvOpAtomicUMin = 237, + SpvOpAtomicSMax = 238, + SpvOpAtomicUMax = 239, + SpvOpAtomicAnd = 240, + SpvOpAtomicOr = 241, + SpvOpAtomicXor = 242, + SpvOpPhi = 245, + SpvOpLoopMerge = 246, + SpvOpSelectionMerge = 247, + SpvOpLabel = 248, + SpvOpBranch = 249, + SpvOpBranchConditional = 250, + SpvOpSwitch = 251, + SpvOpKill = 252, + SpvOpReturn = 253, + SpvOpReturnValue = 254, + SpvOpUnreachable = 255, + SpvOpLifetimeStart = 256, + SpvOpLifetimeStop = 257, + SpvOpGroupAsyncCopy = 259, + SpvOpGroupWaitEvents = 260, + SpvOpGroupAll = 261, + SpvOpGroupAny = 262, + SpvOpGroupBroadcast = 263, + SpvOpGroupIAdd = 264, + SpvOpGroupFAdd = 265, + SpvOpGroupFMin = 266, + SpvOpGroupUMin = 267, + SpvOpGroupSMin = 268, + SpvOpGroupFMax = 269, + SpvOpGroupUMax = 270, + SpvOpGroupSMax = 271, + SpvOpReadPipe = 274, + SpvOpWritePipe = 275, + SpvOpReservedReadPipe = 276, + SpvOpReservedWritePipe = 277, + SpvOpReserveReadPipePackets = 278, + SpvOpReserveWritePipePackets = 279, + SpvOpCommitReadPipe = 280, + SpvOpCommitWritePipe = 281, + SpvOpIsValidReserveId = 282, + SpvOpGetNumPipePackets = 283, + SpvOpGetMaxPipePackets = 284, + SpvOpGroupReserveReadPipePackets = 285, + SpvOpGroupReserveWritePipePackets = 286, + SpvOpGroupCommitReadPipe = 287, + SpvOpGroupCommitWritePipe = 288, + SpvOpEnqueueMarker = 291, + SpvOpEnqueueKernel = 292, + SpvOpGetKernelNDrangeSubGroupCount = 293, + SpvOpGetKernelNDrangeMaxSubGroupSize = 294, + SpvOpGetKernelWorkGroupSize = 295, + SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296, + SpvOpRetainEvent = 297, + SpvOpReleaseEvent = 298, + SpvOpCreateUserEvent = 299, + SpvOpIsValidEvent = 300, + SpvOpSetUserEventStatus = 301, + SpvOpCaptureEventProfilingInfo = 302, + SpvOpGetDefaultQueue = 303, + SpvOpBuildNDRange = 304, + SpvOpImageSparseSampleImplicitLod = 305, + SpvOpImageSparseSampleExplicitLod = 306, + SpvOpImageSparseSampleDrefImplicitLod = 307, + SpvOpImageSparseSampleDrefExplicitLod = 308, + SpvOpImageSparseSampleProjImplicitLod = 309, + SpvOpImageSparseSampleProjExplicitLod = 310, + SpvOpImageSparseSampleProjDrefImplicitLod = 311, + SpvOpImageSparseSampleProjDrefExplicitLod = 312, + SpvOpImageSparseFetch = 313, + SpvOpImageSparseGather = 314, + SpvOpImageSparseDrefGather = 315, + SpvOpImageSparseTexelsResident = 316, + SpvOpNoLine = 317, + SpvOpAtomicFlagTestAndSet = 318, + SpvOpAtomicFlagClear = 319, + SpvOpImageSparseRead = 320, + SpvOpSizeOf = 321, + SpvOpTypePipeStorage = 322, + SpvOpConstantPipeStorage = 323, + SpvOpCreatePipeFromPipeStorage = 324, + SpvOpGetKernelLocalSizeForSubgroupCount = 325, + SpvOpGetKernelMaxNumSubgroups = 326, + SpvOpTypeNamedBarrier = 327, + SpvOpNamedBarrierInitialize = 328, + SpvOpMemoryNamedBarrier = 329, + SpvOpModuleProcessed = 330, + SpvOpExecutionModeId = 331, + SpvOpDecorateId = 332, + SpvOpGroupNonUniformElect = 333, + SpvOpGroupNonUniformAll = 334, + SpvOpGroupNonUniformAny = 335, + SpvOpGroupNonUniformAllEqual = 336, + SpvOpGroupNonUniformBroadcast = 337, + SpvOpGroupNonUniformBroadcastFirst = 338, + SpvOpGroupNonUniformBallot = 339, + SpvOpGroupNonUniformInverseBallot = 340, + SpvOpGroupNonUniformBallotBitExtract = 341, + SpvOpGroupNonUniformBallotBitCount = 342, + SpvOpGroupNonUniformBallotFindLSB = 343, + SpvOpGroupNonUniformBallotFindMSB = 344, + SpvOpGroupNonUniformShuffle = 345, + SpvOpGroupNonUniformShuffleXor = 346, + SpvOpGroupNonUniformShuffleUp = 347, + SpvOpGroupNonUniformShuffleDown = 348, + SpvOpGroupNonUniformIAdd = 349, + SpvOpGroupNonUniformFAdd = 350, + SpvOpGroupNonUniformIMul = 351, + SpvOpGroupNonUniformFMul = 352, + SpvOpGroupNonUniformSMin = 353, + SpvOpGroupNonUniformUMin = 354, + SpvOpGroupNonUniformFMin = 355, + SpvOpGroupNonUniformSMax = 356, + SpvOpGroupNonUniformUMax = 357, + SpvOpGroupNonUniformFMax = 358, + SpvOpGroupNonUniformBitwiseAnd = 359, + SpvOpGroupNonUniformBitwiseOr = 360, + SpvOpGroupNonUniformBitwiseXor = 361, + SpvOpGroupNonUniformLogicalAnd = 362, + SpvOpGroupNonUniformLogicalOr = 363, + SpvOpGroupNonUniformLogicalXor = 364, + SpvOpGroupNonUniformQuadBroadcast = 365, + SpvOpGroupNonUniformQuadSwap = 366, + SpvOpCopyLogical = 400, + SpvOpPtrEqual = 401, + SpvOpPtrNotEqual = 402, + SpvOpPtrDiff = 403, + SpvOpColorAttachmentReadEXT = 4160, + SpvOpDepthAttachmentReadEXT = 4161, + SpvOpStencilAttachmentReadEXT = 4162, + SpvOpTypeTensorARM = 4163, + SpvOpTensorReadARM = 4164, + SpvOpTensorWriteARM = 4165, + SpvOpTensorQuerySizeARM = 4166, + SpvOpGraphConstantARM = 4181, + SpvOpGraphEntryPointARM = 4182, + SpvOpGraphARM = 4183, + SpvOpGraphInputARM = 4184, + SpvOpGraphSetOutputARM = 4185, + SpvOpGraphEndARM = 4186, + SpvOpTypeGraphARM = 4190, + SpvOpTerminateInvocation = 4416, + SpvOpTypeUntypedPointerKHR = 4417, + SpvOpUntypedVariableKHR = 4418, + SpvOpUntypedAccessChainKHR = 4419, + SpvOpUntypedInBoundsAccessChainKHR = 4420, + SpvOpSubgroupBallotKHR = 4421, + SpvOpSubgroupFirstInvocationKHR = 4422, + SpvOpUntypedPtrAccessChainKHR = 4423, + SpvOpUntypedInBoundsPtrAccessChainKHR = 4424, + SpvOpUntypedArrayLengthKHR = 4425, + SpvOpUntypedPrefetchKHR = 4426, + SpvOpFmaKHR = 4427, + SpvOpSubgroupAllKHR = 4428, + SpvOpSubgroupAnyKHR = 4429, + SpvOpSubgroupAllEqualKHR = 4430, + SpvOpGroupNonUniformRotateKHR = 4431, + SpvOpSubgroupReadInvocationKHR = 4432, + SpvOpExtInstWithForwardRefsKHR = 4433, + SpvOpUntypedGroupAsyncCopyKHR = 4434, + SpvOpTraceRayKHR = 4445, + SpvOpExecuteCallableKHR = 4446, + SpvOpConvertUToAccelerationStructureKHR = 4447, + SpvOpIgnoreIntersectionKHR = 4448, + SpvOpTerminateRayKHR = 4449, + SpvOpSDot = 4450, + SpvOpSDotKHR = 4450, + SpvOpUDot = 4451, + SpvOpUDotKHR = 4451, + SpvOpSUDot = 4452, + SpvOpSUDotKHR = 4452, + SpvOpSDotAccSat = 4453, + SpvOpSDotAccSatKHR = 4453, + SpvOpUDotAccSat = 4454, + SpvOpUDotAccSatKHR = 4454, + SpvOpSUDotAccSat = 4455, + SpvOpSUDotAccSatKHR = 4455, + SpvOpTypeCooperativeMatrixKHR = 4456, + SpvOpCooperativeMatrixLoadKHR = 4457, + SpvOpCooperativeMatrixStoreKHR = 4458, + SpvOpCooperativeMatrixMulAddKHR = 4459, + SpvOpCooperativeMatrixLengthKHR = 4460, + SpvOpConstantCompositeReplicateEXT = 4461, + SpvOpSpecConstantCompositeReplicateEXT = 4462, + SpvOpCompositeConstructReplicateEXT = 4463, + SpvOpTypeRayQueryKHR = 4472, + SpvOpRayQueryInitializeKHR = 4473, + SpvOpRayQueryTerminateKHR = 4474, + SpvOpRayQueryGenerateIntersectionKHR = 4475, + SpvOpRayQueryConfirmIntersectionKHR = 4476, + SpvOpRayQueryProceedKHR = 4477, + SpvOpRayQueryGetIntersectionTypeKHR = 4479, + SpvOpImageSampleWeightedQCOM = 4480, + SpvOpImageBoxFilterQCOM = 4481, + SpvOpImageBlockMatchSSDQCOM = 4482, + SpvOpImageBlockMatchSADQCOM = 4483, + SpvOpBitCastArrayQCOM = 4497, + SpvOpImageBlockMatchWindowSSDQCOM = 4500, + SpvOpImageBlockMatchWindowSADQCOM = 4501, + SpvOpImageBlockMatchGatherSSDQCOM = 4502, + SpvOpImageBlockMatchGatherSADQCOM = 4503, + SpvOpCompositeConstructCoopMatQCOM = 4540, + SpvOpCompositeExtractCoopMatQCOM = 4541, + SpvOpExtractSubArrayQCOM = 4542, + SpvOpGroupIAddNonUniformAMD = 5000, + SpvOpGroupFAddNonUniformAMD = 5001, + SpvOpGroupFMinNonUniformAMD = 5002, + SpvOpGroupUMinNonUniformAMD = 5003, + SpvOpGroupSMinNonUniformAMD = 5004, + SpvOpGroupFMaxNonUniformAMD = 5005, + SpvOpGroupUMaxNonUniformAMD = 5006, + SpvOpGroupSMaxNonUniformAMD = 5007, + SpvOpFragmentMaskFetchAMD = 5011, + SpvOpFragmentFetchAMD = 5012, + SpvOpReadClockKHR = 5056, + SpvOpAllocateNodePayloadsAMDX = 5074, + SpvOpEnqueueNodePayloadsAMDX = 5075, + SpvOpTypeNodePayloadArrayAMDX = 5076, + SpvOpFinishWritingNodePayloadAMDX = 5078, + SpvOpNodePayloadArrayLengthAMDX = 5090, + SpvOpIsNodePayloadValidAMDX = 5101, + SpvOpConstantStringAMDX = 5103, + SpvOpSpecConstantStringAMDX = 5104, + SpvOpGroupNonUniformQuadAllKHR = 5110, + SpvOpGroupNonUniformQuadAnyKHR = 5111, + SpvOpTypeBufferEXT = 5115, + SpvOpBufferPointerEXT = 5119, + SpvOpAbortKHR = 5121, + SpvOpUntypedImageTexelPointerEXT = 5126, + SpvOpMemberDecorateIdEXT = 5127, + SpvOpConstantSizeOfEXT = 5129, + SpvOpConstantDataKHR = 5147, + SpvOpSpecConstantDataKHR = 5148, + SpvOpPoisonKHR = 5158, + SpvOpFreezeKHR = 5159, + SpvOpHitObjectRecordHitMotionNV = 5249, + SpvOpHitObjectRecordHitWithIndexMotionNV = 5250, + SpvOpHitObjectRecordMissMotionNV = 5251, + SpvOpHitObjectGetWorldToObjectNV = 5252, + SpvOpHitObjectGetObjectToWorldNV = 5253, + SpvOpHitObjectGetObjectRayDirectionNV = 5254, + SpvOpHitObjectGetObjectRayOriginNV = 5255, + SpvOpHitObjectTraceRayMotionNV = 5256, + SpvOpHitObjectGetShaderRecordBufferHandleNV = 5257, + SpvOpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + SpvOpHitObjectRecordEmptyNV = 5259, + SpvOpHitObjectTraceRayNV = 5260, + SpvOpHitObjectRecordHitNV = 5261, + SpvOpHitObjectRecordHitWithIndexNV = 5262, + SpvOpHitObjectRecordMissNV = 5263, + SpvOpHitObjectExecuteShaderNV = 5264, + SpvOpHitObjectGetCurrentTimeNV = 5265, + SpvOpHitObjectGetAttributesNV = 5266, + SpvOpHitObjectGetHitKindNV = 5267, + SpvOpHitObjectGetPrimitiveIndexNV = 5268, + SpvOpHitObjectGetGeometryIndexNV = 5269, + SpvOpHitObjectGetInstanceIdNV = 5270, + SpvOpHitObjectGetInstanceCustomIndexNV = 5271, + SpvOpHitObjectGetWorldRayDirectionNV = 5272, + SpvOpHitObjectGetWorldRayOriginNV = 5273, + SpvOpHitObjectGetRayTMaxNV = 5274, + SpvOpHitObjectGetRayTMinNV = 5275, + SpvOpHitObjectIsEmptyNV = 5276, + SpvOpHitObjectIsHitNV = 5277, + SpvOpHitObjectIsMissNV = 5278, + SpvOpReorderThreadWithHitObjectNV = 5279, + SpvOpReorderThreadWithHintNV = 5280, + SpvOpTypeHitObjectNV = 5281, + SpvOpImageSampleFootprintNV = 5283, + SpvOpTypeCooperativeVectorNV = 5288, + SpvOpTypeVectorIdEXT = 5288, + SpvOpCooperativeVectorMatrixMulNV = 5289, + SpvOpCooperativeVectorOuterProductAccumulateNV = 5290, + SpvOpCooperativeVectorReduceSumAccumulateNV = 5291, + SpvOpCooperativeVectorMatrixMulAddNV = 5292, + SpvOpCooperativeMatrixConvertNV = 5293, + SpvOpEmitMeshTasksEXT = 5294, + SpvOpSetMeshOutputsEXT = 5295, + SpvOpGroupNonUniformPartitionEXT = 5296, + SpvOpGroupNonUniformPartitionNV = 5296, + SpvOpWritePackedPrimitiveIndices4x8NV = 5299, + SpvOpFetchMicroTriangleVertexPositionNV = 5300, + SpvOpFetchMicroTriangleVertexBarycentricNV = 5301, + SpvOpCooperativeVectorLoadNV = 5302, + SpvOpCooperativeVectorStoreNV = 5303, + SpvOpHitObjectRecordFromQueryEXT = 5304, + SpvOpHitObjectRecordMissEXT = 5305, + SpvOpHitObjectRecordMissMotionEXT = 5306, + SpvOpHitObjectGetIntersectionTriangleVertexPositionsEXT = 5307, + SpvOpHitObjectGetRayFlagsEXT = 5308, + SpvOpHitObjectSetShaderBindingTableRecordIndexEXT = 5309, + SpvOpHitObjectReorderExecuteShaderEXT = 5310, + SpvOpHitObjectTraceReorderExecuteEXT = 5311, + SpvOpHitObjectTraceMotionReorderExecuteEXT = 5312, + SpvOpTypeHitObjectEXT = 5313, + SpvOpReorderThreadWithHintEXT = 5314, + SpvOpReorderThreadWithHitObjectEXT = 5315, + SpvOpHitObjectTraceRayEXT = 5316, + SpvOpHitObjectTraceRayMotionEXT = 5317, + SpvOpHitObjectRecordEmptyEXT = 5318, + SpvOpHitObjectExecuteShaderEXT = 5319, + SpvOpHitObjectGetCurrentTimeEXT = 5320, + SpvOpHitObjectGetAttributesEXT = 5321, + SpvOpHitObjectGetHitKindEXT = 5322, + SpvOpHitObjectGetPrimitiveIndexEXT = 5323, + SpvOpHitObjectGetGeometryIndexEXT = 5324, + SpvOpHitObjectGetInstanceIdEXT = 5325, + SpvOpHitObjectGetInstanceCustomIndexEXT = 5326, + SpvOpHitObjectGetObjectRayOriginEXT = 5327, + SpvOpHitObjectGetObjectRayDirectionEXT = 5328, + SpvOpHitObjectGetWorldRayDirectionEXT = 5329, + SpvOpHitObjectGetWorldRayOriginEXT = 5330, + SpvOpHitObjectGetObjectToWorldEXT = 5331, + SpvOpHitObjectGetWorldToObjectEXT = 5332, + SpvOpHitObjectGetRayTMaxEXT = 5333, + SpvOpReportIntersectionKHR = 5334, + SpvOpReportIntersectionNV = 5334, + SpvOpIgnoreIntersectionNV = 5335, + SpvOpTerminateRayNV = 5336, + SpvOpTraceNV = 5337, + SpvOpTraceMotionNV = 5338, + SpvOpTraceRayMotionNV = 5339, + SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + SpvOpTypeAccelerationStructureKHR = 5341, + SpvOpTypeAccelerationStructureNV = 5341, + SpvOpExecuteCallableNV = 5344, + SpvOpRayQueryGetClusterIdNV = 5345, + SpvOpRayQueryGetIntersectionClusterIdNV = 5345, + SpvOpHitObjectGetClusterIdNV = 5346, + SpvOpHitObjectGetRayTMinEXT = 5347, + SpvOpHitObjectGetShaderBindingTableRecordIndexEXT = 5348, + SpvOpHitObjectGetShaderRecordBufferHandleEXT = 5349, + SpvOpHitObjectIsEmptyEXT = 5350, + SpvOpHitObjectIsHitEXT = 5351, + SpvOpHitObjectIsMissEXT = 5352, + SpvOpTypeCooperativeMatrixNV = 5358, + SpvOpCooperativeMatrixLoadNV = 5359, + SpvOpCooperativeMatrixStoreNV = 5360, + SpvOpCooperativeMatrixMulAddNV = 5361, + SpvOpCooperativeMatrixLengthNV = 5362, + SpvOpBeginInvocationInterlockEXT = 5364, + SpvOpEndInvocationInterlockEXT = 5365, + SpvOpCooperativeMatrixReduceNV = 5366, + SpvOpCooperativeMatrixLoadTensorNV = 5367, + SpvOpCooperativeMatrixStoreTensorNV = 5368, + SpvOpCooperativeMatrixPerElementOpNV = 5369, + SpvOpTypeTensorLayoutNV = 5370, + SpvOpTypeTensorViewNV = 5371, + SpvOpCreateTensorLayoutNV = 5372, + SpvOpTensorLayoutSetDimensionNV = 5373, + SpvOpTensorLayoutSetStrideNV = 5374, + SpvOpTensorLayoutSliceNV = 5375, + SpvOpTensorLayoutSetClampValueNV = 5376, + SpvOpCreateTensorViewNV = 5377, + SpvOpTensorViewSetDimensionNV = 5378, + SpvOpTensorViewSetStrideNV = 5379, + SpvOpDemoteToHelperInvocation = 5380, + SpvOpDemoteToHelperInvocationEXT = 5380, + SpvOpIsHelperInvocationEXT = 5381, + SpvOpTensorViewSetClipNV = 5382, + SpvOpTensorLayoutSetBlockSizeNV = 5384, + SpvOpCooperativeMatrixTransposeNV = 5390, + SpvOpConvertUToImageNV = 5391, + SpvOpConvertUToSamplerNV = 5392, + SpvOpConvertImageToUNV = 5393, + SpvOpConvertSamplerToUNV = 5394, + SpvOpConvertUToSampledImageNV = 5395, + SpvOpConvertSampledImageToUNV = 5396, + SpvOpSamplerImageAddressingModeNV = 5397, + SpvOpRawAccessChainNV = 5398, + SpvOpRayQueryGetIntersectionSpherePositionNV = 5427, + SpvOpRayQueryGetIntersectionSphereRadiusNV = 5428, + SpvOpRayQueryGetIntersectionLSSPositionsNV = 5429, + SpvOpRayQueryGetIntersectionLSSRadiiNV = 5430, + SpvOpRayQueryGetIntersectionLSSHitValueNV = 5431, + SpvOpHitObjectGetSpherePositionNV = 5432, + SpvOpHitObjectGetSphereRadiusNV = 5433, + SpvOpHitObjectGetLSSPositionsNV = 5434, + SpvOpHitObjectGetLSSRadiiNV = 5435, + SpvOpHitObjectIsSphereHitNV = 5436, + SpvOpHitObjectIsLSSHitNV = 5437, + SpvOpRayQueryIsSphereHitNV = 5438, + SpvOpRayQueryIsLSSHitNV = 5439, + SpvOpSubgroupShuffleINTEL = 5571, + SpvOpSubgroupShuffleDownINTEL = 5572, + SpvOpSubgroupShuffleUpINTEL = 5573, + SpvOpSubgroupShuffleXorINTEL = 5574, + SpvOpSubgroupBlockReadINTEL = 5575, + SpvOpSubgroupBlockWriteINTEL = 5576, + SpvOpSubgroupImageBlockReadINTEL = 5577, + SpvOpSubgroupImageBlockWriteINTEL = 5578, + SpvOpSubgroupImageMediaBlockReadINTEL = 5580, + SpvOpSubgroupImageMediaBlockWriteINTEL = 5581, + SpvOpUCountLeadingZerosINTEL = 5585, + SpvOpUCountTrailingZerosINTEL = 5586, + SpvOpAbsISubINTEL = 5587, + SpvOpAbsUSubINTEL = 5588, + SpvOpIAddSatINTEL = 5589, + SpvOpUAddSatINTEL = 5590, + SpvOpIAverageINTEL = 5591, + SpvOpUAverageINTEL = 5592, + SpvOpIAverageRoundedINTEL = 5593, + SpvOpUAverageRoundedINTEL = 5594, + SpvOpISubSatINTEL = 5595, + SpvOpUSubSatINTEL = 5596, + SpvOpIMul32x16INTEL = 5597, + SpvOpUMul32x16INTEL = 5598, + SpvOpConstantFunctionPointerINTEL = 5600, + SpvOpFunctionPointerCallINTEL = 5601, + SpvOpAsmTargetINTEL = 5609, + SpvOpAsmINTEL = 5610, + SpvOpAsmCallINTEL = 5611, + SpvOpAtomicFMinEXT = 5614, + SpvOpAtomicFMaxEXT = 5615, + SpvOpAssumeTrueKHR = 5630, + SpvOpExpectKHR = 5631, + SpvOpDecorateString = 5632, + SpvOpDecorateStringGOOGLE = 5632, + SpvOpMemberDecorateString = 5633, + SpvOpMemberDecorateStringGOOGLE = 5633, + SpvOpVmeImageINTEL = 5699, + SpvOpTypeVmeImageINTEL = 5700, + SpvOpTypeAvcImePayloadINTEL = 5701, + SpvOpTypeAvcRefPayloadINTEL = 5702, + SpvOpTypeAvcSicPayloadINTEL = 5703, + SpvOpTypeAvcMcePayloadINTEL = 5704, + SpvOpTypeAvcMceResultINTEL = 5705, + SpvOpTypeAvcImeResultINTEL = 5706, + SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + SpvOpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + SpvOpTypeAvcImeDualReferenceStreaminINTEL = 5710, + SpvOpTypeAvcRefResultINTEL = 5711, + SpvOpTypeAvcSicResultINTEL = 5712, + SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + SpvOpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + SpvOpSubgroupAvcMceConvertToImeResultINTEL = 5733, + SpvOpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + SpvOpSubgroupAvcMceConvertToRefResultINTEL = 5735, + SpvOpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + SpvOpSubgroupAvcMceConvertToSicResultINTEL = 5737, + SpvOpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + SpvOpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + SpvOpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + SpvOpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + SpvOpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + SpvOpSubgroupAvcImeInitializeINTEL = 5747, + SpvOpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + SpvOpSubgroupAvcImeSetDualReferenceINTEL = 5749, + SpvOpSubgroupAvcImeRefWindowSizeINTEL = 5750, + SpvOpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + SpvOpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + SpvOpSubgroupAvcImeSetWeightedSadINTEL = 5756, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + SpvOpSubgroupAvcImeConvertToMceResultINTEL = 5765, + SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + SpvOpSubgroupAvcImeGetBorderReachedINTEL = 5776, + SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + SpvOpSubgroupAvcFmeInitializeINTEL = 5781, + SpvOpSubgroupAvcBmeInitializeINTEL = 5782, + SpvOpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + SpvOpSubgroupAvcRefConvertToMceResultINTEL = 5790, + SpvOpSubgroupAvcSicInitializeINTEL = 5791, + SpvOpSubgroupAvcSicConfigureSkcINTEL = 5792, + SpvOpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + SpvOpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + SpvOpSubgroupAvcSicEvaluateIpeINTEL = 5803, + SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + SpvOpSubgroupAvcSicConvertToMceResultINTEL = 5808, + SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + SpvOpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + SpvOpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + SpvOpVariableLengthArrayINTEL = 5818, + SpvOpSaveMemoryINTEL = 5819, + SpvOpRestoreMemoryINTEL = 5820, + SpvOpArbitraryFloatSinCosPiALTERA = 5840, + SpvOpArbitraryFloatSinCosPiINTEL = 5840, + SpvOpArbitraryFloatCastALTERA = 5841, + SpvOpArbitraryFloatCastINTEL = 5841, + SpvOpArbitraryFloatCastFromIntALTERA = 5842, + SpvOpArbitraryFloatCastFromIntINTEL = 5842, + SpvOpArbitraryFloatCastToIntALTERA = 5843, + SpvOpArbitraryFloatCastToIntINTEL = 5843, + SpvOpArbitraryFloatAddALTERA = 5846, + SpvOpArbitraryFloatAddINTEL = 5846, + SpvOpArbitraryFloatSubALTERA = 5847, + SpvOpArbitraryFloatSubINTEL = 5847, + SpvOpArbitraryFloatMulALTERA = 5848, + SpvOpArbitraryFloatMulINTEL = 5848, + SpvOpArbitraryFloatDivALTERA = 5849, + SpvOpArbitraryFloatDivINTEL = 5849, + SpvOpArbitraryFloatGTALTERA = 5850, + SpvOpArbitraryFloatGTINTEL = 5850, + SpvOpArbitraryFloatGEALTERA = 5851, + SpvOpArbitraryFloatGEINTEL = 5851, + SpvOpArbitraryFloatLTALTERA = 5852, + SpvOpArbitraryFloatLTINTEL = 5852, + SpvOpArbitraryFloatLEALTERA = 5853, + SpvOpArbitraryFloatLEINTEL = 5853, + SpvOpArbitraryFloatEQALTERA = 5854, + SpvOpArbitraryFloatEQINTEL = 5854, + SpvOpArbitraryFloatRecipALTERA = 5855, + SpvOpArbitraryFloatRecipINTEL = 5855, + SpvOpArbitraryFloatRSqrtALTERA = 5856, + SpvOpArbitraryFloatRSqrtINTEL = 5856, + SpvOpArbitraryFloatCbrtALTERA = 5857, + SpvOpArbitraryFloatCbrtINTEL = 5857, + SpvOpArbitraryFloatHypotALTERA = 5858, + SpvOpArbitraryFloatHypotINTEL = 5858, + SpvOpArbitraryFloatSqrtALTERA = 5859, + SpvOpArbitraryFloatSqrtINTEL = 5859, + SpvOpArbitraryFloatLogINTEL = 5860, + SpvOpArbitraryFloatLog2INTEL = 5861, + SpvOpArbitraryFloatLog10INTEL = 5862, + SpvOpArbitraryFloatLog1pINTEL = 5863, + SpvOpArbitraryFloatExpINTEL = 5864, + SpvOpArbitraryFloatExp2INTEL = 5865, + SpvOpArbitraryFloatExp10INTEL = 5866, + SpvOpArbitraryFloatExpm1INTEL = 5867, + SpvOpArbitraryFloatSinINTEL = 5868, + SpvOpArbitraryFloatCosINTEL = 5869, + SpvOpArbitraryFloatSinCosINTEL = 5870, + SpvOpArbitraryFloatSinPiINTEL = 5871, + SpvOpArbitraryFloatCosPiINTEL = 5872, + SpvOpArbitraryFloatASinINTEL = 5873, + SpvOpArbitraryFloatASinPiINTEL = 5874, + SpvOpArbitraryFloatACosINTEL = 5875, + SpvOpArbitraryFloatACosPiINTEL = 5876, + SpvOpArbitraryFloatATanINTEL = 5877, + SpvOpArbitraryFloatATanPiINTEL = 5878, + SpvOpArbitraryFloatATan2INTEL = 5879, + SpvOpArbitraryFloatPowINTEL = 5880, + SpvOpArbitraryFloatPowRINTEL = 5881, + SpvOpArbitraryFloatPowNINTEL = 5882, + SpvOpLoopControlINTEL = 5887, + SpvOpAliasDomainDeclINTEL = 5911, + SpvOpAliasScopeDeclINTEL = 5912, + SpvOpAliasScopeListDeclINTEL = 5913, + SpvOpFixedSqrtALTERA = 5923, + SpvOpFixedSqrtINTEL = 5923, + SpvOpFixedRecipALTERA = 5924, + SpvOpFixedRecipINTEL = 5924, + SpvOpFixedRsqrtALTERA = 5925, + SpvOpFixedRsqrtINTEL = 5925, + SpvOpFixedSinALTERA = 5926, + SpvOpFixedSinINTEL = 5926, + SpvOpFixedCosALTERA = 5927, + SpvOpFixedCosINTEL = 5927, + SpvOpFixedSinCosALTERA = 5928, + SpvOpFixedSinCosINTEL = 5928, + SpvOpFixedSinPiALTERA = 5929, + SpvOpFixedSinPiINTEL = 5929, + SpvOpFixedCosPiALTERA = 5930, + SpvOpFixedCosPiINTEL = 5930, + SpvOpFixedSinCosPiALTERA = 5931, + SpvOpFixedSinCosPiINTEL = 5931, + SpvOpFixedLogALTERA = 5932, + SpvOpFixedLogINTEL = 5932, + SpvOpFixedExpALTERA = 5933, + SpvOpFixedExpINTEL = 5933, + SpvOpPtrCastToCrossWorkgroupALTERA = 5934, + SpvOpPtrCastToCrossWorkgroupINTEL = 5934, + SpvOpCrossWorkgroupCastToPtrALTERA = 5938, + SpvOpCrossWorkgroupCastToPtrINTEL = 5938, + SpvOpReadPipeBlockingALTERA = 5946, + SpvOpReadPipeBlockingINTEL = 5946, + SpvOpWritePipeBlockingALTERA = 5947, + SpvOpWritePipeBlockingINTEL = 5947, + SpvOpFPGARegALTERA = 5949, + SpvOpFPGARegINTEL = 5949, + SpvOpRayQueryGetRayTMinKHR = 6016, + SpvOpRayQueryGetRayFlagsKHR = 6017, + SpvOpRayQueryGetIntersectionTKHR = 6018, + SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + SpvOpRayQueryGetIntersectionInstanceIdKHR = 6020, + SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + SpvOpRayQueryGetIntersectionGeometryIndexKHR = 6022, + SpvOpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + SpvOpRayQueryGetIntersectionBarycentricsKHR = 6024, + SpvOpRayQueryGetIntersectionFrontFaceKHR = 6025, + SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + SpvOpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + SpvOpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + SpvOpRayQueryGetWorldRayDirectionKHR = 6029, + SpvOpRayQueryGetWorldRayOriginKHR = 6030, + SpvOpRayQueryGetIntersectionObjectToWorldKHR = 6031, + SpvOpRayQueryGetIntersectionWorldToObjectKHR = 6032, + SpvOpAtomicFAddEXT = 6035, + SpvOpTypeBufferSurfaceINTEL = 6086, + SpvOpTypeStructContinuedINTEL = 6090, + SpvOpConstantCompositeContinuedINTEL = 6091, + SpvOpSpecConstantCompositeContinuedINTEL = 6092, + SpvOpCompositeConstructContinuedINTEL = 6096, + SpvOpConvertFToBF16INTEL = 6116, + SpvOpConvertBF16ToFINTEL = 6117, + SpvOpControlBarrierArriveINTEL = 6142, + SpvOpControlBarrierWaitINTEL = 6143, + SpvOpArithmeticFenceEXT = 6145, + SpvOpTaskSequenceCreateALTERA = 6163, + SpvOpTaskSequenceCreateINTEL = 6163, + SpvOpTaskSequenceAsyncALTERA = 6164, + SpvOpTaskSequenceAsyncINTEL = 6164, + SpvOpTaskSequenceGetALTERA = 6165, + SpvOpTaskSequenceGetINTEL = 6165, + SpvOpTaskSequenceReleaseALTERA = 6166, + SpvOpTaskSequenceReleaseINTEL = 6166, + SpvOpTypeTaskSequenceALTERA = 6199, + SpvOpTypeTaskSequenceINTEL = 6199, + SpvOpSubgroupBlockPrefetchINTEL = 6221, + SpvOpSubgroup2DBlockLoadINTEL = 6231, + SpvOpSubgroup2DBlockLoadTransformINTEL = 6232, + SpvOpSubgroup2DBlockLoadTransposeINTEL = 6233, + SpvOpSubgroup2DBlockPrefetchINTEL = 6234, + SpvOpSubgroup2DBlockStoreINTEL = 6235, + SpvOpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + SpvOpBitwiseFunctionINTEL = 6242, + SpvOpUntypedVariableLengthArrayINTEL = 6244, + SpvOpConditionalExtensionINTEL = 6248, + SpvOpConditionalEntryPointINTEL = 6249, + SpvOpConditionalCapabilityINTEL = 6250, + SpvOpSpecConstantTargetINTEL = 6251, + SpvOpSpecConstantArchitectureINTEL = 6252, + SpvOpSpecConstantCapabilitiesINTEL = 6253, + SpvOpConditionalCopyObjectINTEL = 6254, + SpvOpGroupIMulKHR = 6401, + SpvOpGroupFMulKHR = 6402, + SpvOpGroupBitwiseAndKHR = 6403, + SpvOpGroupBitwiseOrKHR = 6404, + SpvOpGroupBitwiseXorKHR = 6405, + SpvOpGroupLogicalAndKHR = 6406, + SpvOpGroupLogicalOrKHR = 6407, + SpvOpGroupLogicalXorKHR = 6408, + SpvOpRoundFToTF32INTEL = 6426, + SpvOpMaskedGatherINTEL = 6428, + SpvOpMaskedScatterINTEL = 6429, + SpvOpConvertHandleToImageINTEL = 6529, + SpvOpConvertHandleToSamplerINTEL = 6530, + SpvOpConvertHandleToSampledImageINTEL = 6531, + SpvOpFDot2MixAcc32VALVE = 6916, + SpvOpFDot2MixAcc16VALVE = 6917, + SpvOpFDot4MixAcc32VALVE = 6918, + SpvOpMax = 0x7fffffff, +} SpvOp; + +#ifdef SPV_ENABLE_UTILITY_CODE +#ifndef __cplusplus +#include +#endif +inline void SpvHasResultAndType(SpvOp opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case SpvOpNop: *hasResult = false; *hasResultType = false; break; + case SpvOpUndef: *hasResult = true; *hasResultType = true; break; + case SpvOpSourceContinued: *hasResult = false; *hasResultType = false; break; + case SpvOpSource: *hasResult = false; *hasResultType = false; break; + case SpvOpSourceExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpName: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberName: *hasResult = false; *hasResultType = false; break; + case SpvOpString: *hasResult = true; *hasResultType = false; break; + case SpvOpLine: *hasResult = false; *hasResultType = false; break; + case SpvOpExtension: *hasResult = false; *hasResultType = false; break; + case SpvOpExtInstImport: *hasResult = true; *hasResultType = false; break; + case SpvOpExtInst: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryModel: *hasResult = false; *hasResultType = false; break; + case SpvOpEntryPoint: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionMode: *hasResult = false; *hasResultType = false; break; + case SpvOpCapability: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeVoid: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeBool: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeInt: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFloat: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeVector: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampler: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStruct: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePointer: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeFunction: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeQueue: *hasResult = true; *hasResultType = false; break; + case SpvOpTypePipe: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantSampler: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantNull: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstant: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case SpvOpFunction: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case SpvOpFunctionCall: *hasResult = true; *hasResultType = true; break; + case SpvOpVariable: *hasResult = true; *hasResultType = true; break; + case SpvOpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case SpvOpLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpStore: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemory: *hasResult = false; *hasResultType = false; break; + case SpvOpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case SpvOpAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpArrayLength: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case SpvOpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case SpvOpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyObject: *hasResult = true; *hasResultType = true; break; + case SpvOpTranspose: *hasResult = true; *hasResultType = true; break; + case SpvOpSampledImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageRead: *hasResult = true; *hasResultType = true; break; + case SpvOpImageWrite: *hasResult = false; *hasResultType = false; break; + case SpvOpImage: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case SpvOpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToU: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSToF: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToF: *hasResult = true; *hasResultType = true; break; + case SpvOpUConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpSConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpFConvert: *hasResult = true; *hasResultType = true; break; + case SpvOpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case SpvOpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case SpvOpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case SpvOpBitcast: *hasResult = true; *hasResultType = true; break; + case SpvOpSNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpFNegate: *hasResult = true; *hasResultType = true; break; + case SpvOpIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpISub: *hasResult = true; *hasResultType = true; break; + case SpvOpFSub: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpUDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpSDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpFDiv: *hasResult = true; *hasResultType = true; break; + case SpvOpUMod: *hasResult = true; *hasResultType = true; break; + case SpvOpSRem: *hasResult = true; *hasResultType = true; break; + case SpvOpSMod: *hasResult = true; *hasResultType = true; break; + case SpvOpFRem: *hasResult = true; *hasResultType = true; break; + case SpvOpFMod: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case SpvOpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case SpvOpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case SpvOpOuterProduct: *hasResult = true; *hasResultType = true; break; + case SpvOpDot: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddCarry: *hasResult = true; *hasResultType = true; break; + case SpvOpISubBorrow: *hasResult = true; *hasResultType = true; break; + case SpvOpUMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpSMulExtended: *hasResult = true; *hasResultType = true; break; + case SpvOpAny: *hasResult = true; *hasResultType = true; break; + case SpvOpAll: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNan: *hasResult = true; *hasResultType = true; break; + case SpvOpIsInf: *hasResult = true; *hasResultType = true; break; + case SpvOpIsFinite: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNormal: *hasResult = true; *hasResultType = true; break; + case SpvOpSignBitSet: *hasResult = true; *hasResultType = true; break; + case SpvOpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case SpvOpOrdered: *hasResult = true; *hasResultType = true; break; + case SpvOpUnordered: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpLogicalNot: *hasResult = true; *hasResultType = true; break; + case SpvOpSelect: *hasResult = true; *hasResultType = true; break; + case SpvOpIEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpINotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case SpvOpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpNot: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpBitReverse: *hasResult = true; *hasResultType = true; break; + case SpvOpBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdx: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdy: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidth: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyFine: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthFine: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case SpvOpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicStore: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicISub: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicOr: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicXor: *hasResult = true; *hasResultType = true; break; + case SpvOpPhi: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case SpvOpLabel: *hasResult = true; *hasResultType = false; break; + case SpvOpBranch: *hasResult = false; *hasResultType = false; break; + case SpvOpBranchConditional: *hasResult = false; *hasResultType = false; break; + case SpvOpSwitch: *hasResult = false; *hasResultType = false; break; + case SpvOpKill: *hasResult = false; *hasResultType = false; break; + case SpvOpReturn: *hasResult = false; *hasResultType = false; break; + case SpvOpReturnValue: *hasResult = false; *hasResultType = false; break; + case SpvOpUnreachable: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case SpvOpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case SpvOpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case SpvOpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case SpvOpRetainEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case SpvOpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case SpvOpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case SpvOpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case SpvOpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case SpvOpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case SpvOpNoLine: *hasResult = false; *hasResultType = false; break; + case SpvOpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case SpvOpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case SpvOpSizeOf: *hasResult = true; *hasResultType = true; break; + case SpvOpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case SpvOpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case SpvOpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case SpvOpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case SpvOpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case SpvOpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case SpvOpDecorateId: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case SpvOpCopyLogical: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrDiff: *hasResult = true; *hasResultType = true; break; + case SpvOpColorAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpDepthAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpStencilAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeTensorARM: *hasResult = true; *hasResultType = false; break; + case SpvOpTensorReadARM: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorWriteARM: *hasResult = false; *hasResultType = false; break; + case SpvOpTensorQuerySizeARM: *hasResult = true; *hasResultType = true; break; + case SpvOpGraphConstantARM: *hasResult = true; *hasResultType = true; break; + case SpvOpGraphEntryPointARM: *hasResult = false; *hasResultType = false; break; + case SpvOpGraphARM: *hasResult = true; *hasResultType = true; break; + case SpvOpGraphInputARM: *hasResult = true; *hasResultType = true; break; + case SpvOpGraphSetOutputARM: *hasResult = false; *hasResultType = false; break; + case SpvOpGraphEndARM: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeGraphARM: *hasResult = true; *hasResultType = false; break; + case SpvOpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeUntypedPointerKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpUntypedVariableKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedInBoundsAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedInBoundsPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedArrayLengthKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedPrefetchKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpFmaKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpExtInstWithForwardRefsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedGroupAsyncCopyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpSDot: *hasResult = true; *hasResultType = true; break; + case SpvOpUDot: *hasResult = true; *hasResultType = true; break; + case SpvOpSUDot: *hasResult = true; *hasResultType = true; break; + case SpvOpSDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpUDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpSUDotAccSat: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeCooperativeMatrixKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstructReplicateEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpImageSampleWeightedQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBoxFilterQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpBitCastArrayQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchWindowSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchWindowSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchGatherSSDQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpImageBlockMatchGatherSADQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeConstructCoopMatQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpCompositeExtractCoopMatQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpExtractSubArrayQCOM: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case SpvOpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpAllocateNodePayloadsAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpEnqueueNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeNodePayloadArrayAMDX: *hasResult = true; *hasResultType = false; break; + case SpvOpFinishWritingNodePayloadAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpNodePayloadArrayLengthAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpIsNodePayloadValidAMDX: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case SpvOpSpecConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case SpvOpGroupNonUniformQuadAllKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupNonUniformQuadAnyKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeBufferEXT: *hasResult = true; *hasResultType = false; break; + case SpvOpBufferPointerEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpAbortKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpUntypedImageTexelPointerEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpMemberDecorateIdEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantSizeOfEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantDataKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantDataKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpPoisonKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpFreezeKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectRecordHitMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitWithIndexMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetWorldToObjectNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectToWorldNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayOriginNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetShaderRecordBufferHandleNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetShaderBindingTableRecordIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectRecordEmptyNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectTraceRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordHitWithIndexNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectExecuteShaderNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetCurrentTimeNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetAttributesNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetHitKindNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetPrimitiveIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetGeometryIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceIdNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceCustomIndexNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayOriginNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMaxNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMinNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsEmptyNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsMissNV: *hasResult = true; *hasResultType = true; break; + case SpvOpReorderThreadWithHitObjectNV: *hasResult = false; *hasResultType = false; break; + case SpvOpReorderThreadWithHintNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeHitObjectNV: *hasResult = true; *hasResultType = false; break; + case SpvOpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeVectorIdEXT: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeVectorMatrixMulNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeVectorOuterProductAccumulateNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeVectorReduceSumAccumulateNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeVectorMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixConvertNV: *hasResult = true; *hasResultType = true; break; + case SpvOpEmitMeshTasksEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpSetMeshOutputsEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpGroupNonUniformPartitionEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case SpvOpFetchMicroTriangleVertexPositionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpFetchMicroTriangleVertexBarycentricNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeVectorLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeVectorStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordFromQueryEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordMissMotionEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetIntersectionTriangleVertexPositionsEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayFlagsEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectSetShaderBindingTableRecordIndexEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectReorderExecuteShaderEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectTraceReorderExecuteEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectTraceMotionReorderExecuteEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeHitObjectEXT: *hasResult = true; *hasResultType = false; break; + case SpvOpReorderThreadWithHintEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpReorderThreadWithHitObjectEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectTraceRayEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectTraceRayMotionEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectRecordEmptyEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectExecuteShaderEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetCurrentTimeEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetAttributesEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpHitObjectGetHitKindEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetPrimitiveIndexEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetGeometryIndexEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceIdEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetInstanceCustomIndexEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayOriginEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectRayDirectionEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayDirectionEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldRayOriginEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetObjectToWorldEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetWorldToObjectEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMaxEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpReportIntersectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeAccelerationStructureKHR: *hasResult = true; *hasResultType = false; break; + case SpvOpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRayQueryGetIntersectionClusterIdNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetRayTMinEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetShaderBindingTableRecordIndexEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetShaderRecordBufferHandleEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsEmptyEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsHitEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsMissEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case SpvOpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixReduceNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixLoadTensorNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixStoreTensorNV: *hasResult = false; *hasResultType = false; break; + case SpvOpCooperativeMatrixPerElementOpNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeTensorLayoutNV: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeTensorViewNV: *hasResult = true; *hasResultType = false; break; + case SpvOpCreateTensorLayoutNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetStrideNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSliceNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetClampValueNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCreateTensorViewNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorViewSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorViewSetStrideNV: *hasResult = true; *hasResultType = true; break; + case SpvOpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break; + case SpvOpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorViewSetClipNV: *hasResult = true; *hasResultType = true; break; + case SpvOpTensorLayoutSetBlockSizeNV: *hasResult = true; *hasResultType = true; break; + case SpvOpCooperativeMatrixTransposeNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case SpvOpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; + case SpvOpRawAccessChainNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionLSSHitValueNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectGetLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpHitObjectIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmTargetINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break; + case SpvOpExpectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case SpvOpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpArbitraryFloatSinCosPiALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastFromIntALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCastToIntALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatAddALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSubALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatMulALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatDivALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatGTALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatGEALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLTALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLEALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatEQALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatRecipALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatRSqrtALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCbrtALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatHypotALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSqrtALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpFixedSqrtALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedRecipALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedRsqrtALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedCosALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinCosALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinPiALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedCosPiALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedSinCosPiALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedLogALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFixedExpALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpPtrCastToCrossWorkgroupALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpCrossWorkgroupCastToPtrALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpReadPipeBlockingALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpWritePipeBlockingALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpFPGARegALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case SpvOpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpCompositeConstructContinuedINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertFToBF16INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertBF16ToFINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpArithmeticFenceEXT: *hasResult = true; *hasResultType = true; break; + case SpvOpTaskSequenceCreateALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpTaskSequenceAsyncALTERA: *hasResult = false; *hasResultType = false; break; + case SpvOpTaskSequenceGetALTERA: *hasResult = true; *hasResultType = true; break; + case SpvOpTaskSequenceReleaseALTERA: *hasResult = false; *hasResultType = false; break; + case SpvOpTypeTaskSequenceALTERA: *hasResult = true; *hasResultType = false; break; + case SpvOpSubgroupBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockLoadINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockLoadTransformINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockLoadTransposeINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroup2DBlockStoreINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSubgroupMatrixMultiplyAccumulateINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpBitwiseFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpUntypedVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConditionalExtensionINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpConditionalEntryPointINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpConditionalCapabilityINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpSpecConstantTargetINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantArchitectureINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpSpecConstantCapabilitiesINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConditionalCopyObjectINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupIMulKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupFMulKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break; + case SpvOpRoundFToTF32INTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpMaskedGatherINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpMaskedScatterINTEL: *hasResult = false; *hasResultType = false; break; + case SpvOpConvertHandleToImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertHandleToSamplerINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpConvertHandleToSampledImageINTEL: *hasResult = true; *hasResultType = true; break; + case SpvOpFDot2MixAcc32VALVE: *hasResult = true; *hasResultType = true; break; + case SpvOpFDot2MixAcc16VALVE: *hasResult = true; *hasResultType = true; break; + case SpvOpFDot4MixAcc32VALVE: *hasResult = true; *hasResultType = true; break; + } +} +inline const char* SpvSourceLanguageToString(SpvSourceLanguage value) { + switch (value) { + case SpvSourceLanguageUnknown: return "Unknown"; + case SpvSourceLanguageESSL: return "ESSL"; + case SpvSourceLanguageGLSL: return "GLSL"; + case SpvSourceLanguageOpenCL_C: return "OpenCL_C"; + case SpvSourceLanguageOpenCL_CPP: return "OpenCL_CPP"; + case SpvSourceLanguageHLSL: return "HLSL"; + case SpvSourceLanguageCPP_for_OpenCL: return "CPP_for_OpenCL"; + case SpvSourceLanguageSYCL: return "SYCL"; + case SpvSourceLanguageHERO_C: return "HERO_C"; + case SpvSourceLanguageNZSL: return "NZSL"; + case SpvSourceLanguageWGSL: return "WGSL"; + case SpvSourceLanguageSlang: return "Slang"; + case SpvSourceLanguageZig: return "Zig"; + case SpvSourceLanguageRust: return "Rust"; + default: return "Unknown"; + } +} + +inline const char* SpvExecutionModelToString(SpvExecutionModel value) { + switch (value) { + case SpvExecutionModelVertex: return "Vertex"; + case SpvExecutionModelTessellationControl: return "TessellationControl"; + case SpvExecutionModelTessellationEvaluation: return "TessellationEvaluation"; + case SpvExecutionModelGeometry: return "Geometry"; + case SpvExecutionModelFragment: return "Fragment"; + case SpvExecutionModelGLCompute: return "GLCompute"; + case SpvExecutionModelKernel: return "Kernel"; + case SpvExecutionModelTaskNV: return "TaskNV"; + case SpvExecutionModelMeshNV: return "MeshNV"; + case SpvExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case SpvExecutionModelIntersectionKHR: return "IntersectionKHR"; + case SpvExecutionModelAnyHitKHR: return "AnyHitKHR"; + case SpvExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case SpvExecutionModelMissKHR: return "MissKHR"; + case SpvExecutionModelCallableKHR: return "CallableKHR"; + case SpvExecutionModelTaskEXT: return "TaskEXT"; + case SpvExecutionModelMeshEXT: return "MeshEXT"; + default: return "Unknown"; + } +} + +inline const char* SpvAddressingModelToString(SpvAddressingModel value) { + switch (value) { + case SpvAddressingModelLogical: return "Logical"; + case SpvAddressingModelPhysical32: return "Physical32"; + case SpvAddressingModelPhysical64: return "Physical64"; + case SpvAddressingModelPhysicalStorageBuffer64: return "PhysicalStorageBuffer64"; + default: return "Unknown"; + } +} + +inline const char* SpvMemoryModelToString(SpvMemoryModel value) { + switch (value) { + case SpvMemoryModelSimple: return "Simple"; + case SpvMemoryModelGLSL450: return "GLSL450"; + case SpvMemoryModelOpenCL: return "OpenCL"; + case SpvMemoryModelVulkan: return "Vulkan"; + default: return "Unknown"; + } +} + +inline const char* SpvExecutionModeToString(SpvExecutionMode value) { + switch (value) { + case SpvExecutionModeInvocations: return "Invocations"; + case SpvExecutionModeSpacingEqual: return "SpacingEqual"; + case SpvExecutionModeSpacingFractionalEven: return "SpacingFractionalEven"; + case SpvExecutionModeSpacingFractionalOdd: return "SpacingFractionalOdd"; + case SpvExecutionModeVertexOrderCw: return "VertexOrderCw"; + case SpvExecutionModeVertexOrderCcw: return "VertexOrderCcw"; + case SpvExecutionModePixelCenterInteger: return "PixelCenterInteger"; + case SpvExecutionModeOriginUpperLeft: return "OriginUpperLeft"; + case SpvExecutionModeOriginLowerLeft: return "OriginLowerLeft"; + case SpvExecutionModeEarlyFragmentTests: return "EarlyFragmentTests"; + case SpvExecutionModePointMode: return "PointMode"; + case SpvExecutionModeXfb: return "Xfb"; + case SpvExecutionModeDepthReplacing: return "DepthReplacing"; + case SpvExecutionModeDepthGreater: return "DepthGreater"; + case SpvExecutionModeDepthLess: return "DepthLess"; + case SpvExecutionModeDepthUnchanged: return "DepthUnchanged"; + case SpvExecutionModeLocalSize: return "LocalSize"; + case SpvExecutionModeLocalSizeHint: return "LocalSizeHint"; + case SpvExecutionModeInputPoints: return "InputPoints"; + case SpvExecutionModeInputLines: return "InputLines"; + case SpvExecutionModeInputLinesAdjacency: return "InputLinesAdjacency"; + case SpvExecutionModeTriangles: return "Triangles"; + case SpvExecutionModeInputTrianglesAdjacency: return "InputTrianglesAdjacency"; + case SpvExecutionModeQuads: return "Quads"; + case SpvExecutionModeIsolines: return "Isolines"; + case SpvExecutionModeOutputVertices: return "OutputVertices"; + case SpvExecutionModeOutputPoints: return "OutputPoints"; + case SpvExecutionModeOutputLineStrip: return "OutputLineStrip"; + case SpvExecutionModeOutputTriangleStrip: return "OutputTriangleStrip"; + case SpvExecutionModeVecTypeHint: return "VecTypeHint"; + case SpvExecutionModeContractionOff: return "ContractionOff"; + case SpvExecutionModeInitializer: return "Initializer"; + case SpvExecutionModeFinalizer: return "Finalizer"; + case SpvExecutionModeSubgroupSize: return "SubgroupSize"; + case SpvExecutionModeSubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case SpvExecutionModeSubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case SpvExecutionModeLocalSizeId: return "LocalSizeId"; + case SpvExecutionModeLocalSizeHintId: return "LocalSizeHintId"; + case SpvExecutionModeNonCoherentColorAttachmentReadEXT: return "NonCoherentColorAttachmentReadEXT"; + case SpvExecutionModeNonCoherentDepthAttachmentReadEXT: return "NonCoherentDepthAttachmentReadEXT"; + case SpvExecutionModeNonCoherentStencilAttachmentReadEXT: return "NonCoherentStencilAttachmentReadEXT"; + case SpvExecutionModeSubgroupUniformControlFlowKHR: return "SubgroupUniformControlFlowKHR"; + case SpvExecutionModePostDepthCoverage: return "PostDepthCoverage"; + case SpvExecutionModeDenormPreserve: return "DenormPreserve"; + case SpvExecutionModeDenormFlushToZero: return "DenormFlushToZero"; + case SpvExecutionModeSignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case SpvExecutionModeRoundingModeRTE: return "RoundingModeRTE"; + case SpvExecutionModeRoundingModeRTZ: return "RoundingModeRTZ"; + case SpvExecutionModeNonCoherentTileAttachmentReadQCOM: return "NonCoherentTileAttachmentReadQCOM"; + case SpvExecutionModeTileShadingRateQCOM: return "TileShadingRateQCOM"; + case SpvExecutionModeEarlyAndLateFragmentTestsAMD: return "EarlyAndLateFragmentTestsAMD"; + case SpvExecutionModeStencilRefReplacingEXT: return "StencilRefReplacingEXT"; + case SpvExecutionModeCoalescingAMDX: return "CoalescingAMDX"; + case SpvExecutionModeIsApiEntryAMDX: return "IsApiEntryAMDX"; + case SpvExecutionModeMaxNodeRecursionAMDX: return "MaxNodeRecursionAMDX"; + case SpvExecutionModeStaticNumWorkgroupsAMDX: return "StaticNumWorkgroupsAMDX"; + case SpvExecutionModeShaderIndexAMDX: return "ShaderIndexAMDX"; + case SpvExecutionModeMaxNumWorkgroupsAMDX: return "MaxNumWorkgroupsAMDX"; + case SpvExecutionModeStencilRefUnchangedFrontAMD: return "StencilRefUnchangedFrontAMD"; + case SpvExecutionModeStencilRefGreaterFrontAMD: return "StencilRefGreaterFrontAMD"; + case SpvExecutionModeStencilRefLessFrontAMD: return "StencilRefLessFrontAMD"; + case SpvExecutionModeStencilRefUnchangedBackAMD: return "StencilRefUnchangedBackAMD"; + case SpvExecutionModeStencilRefGreaterBackAMD: return "StencilRefGreaterBackAMD"; + case SpvExecutionModeStencilRefLessBackAMD: return "StencilRefLessBackAMD"; + case SpvExecutionModeQuadDerivativesKHR: return "QuadDerivativesKHR"; + case SpvExecutionModeRequireFullQuadsKHR: return "RequireFullQuadsKHR"; + case SpvExecutionModeSharesInputWithAMDX: return "SharesInputWithAMDX"; + case SpvExecutionModeArithmeticPoisonKHR: return "ArithmeticPoisonKHR"; + case SpvExecutionModeOutputLinesEXT: return "OutputLinesEXT"; + case SpvExecutionModeOutputPrimitivesEXT: return "OutputPrimitivesEXT"; + case SpvExecutionModeDerivativeGroupQuadsKHR: return "DerivativeGroupQuadsKHR"; + case SpvExecutionModeDerivativeGroupLinearKHR: return "DerivativeGroupLinearKHR"; + case SpvExecutionModeOutputTrianglesEXT: return "OutputTrianglesEXT"; + case SpvExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case SpvExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case SpvExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case SpvExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case SpvExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case SpvExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + case SpvExecutionModeShader64BitIndexingEXT: return "Shader64BitIndexingEXT"; + case SpvExecutionModeSharedLocalMemorySizeINTEL: return "SharedLocalMemorySizeINTEL"; + case SpvExecutionModeRoundingModeRTPINTEL: return "RoundingModeRTPINTEL"; + case SpvExecutionModeRoundingModeRTNINTEL: return "RoundingModeRTNINTEL"; + case SpvExecutionModeFloatingPointModeALTINTEL: return "FloatingPointModeALTINTEL"; + case SpvExecutionModeFloatingPointModeIEEEINTEL: return "FloatingPointModeIEEEINTEL"; + case SpvExecutionModeMaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case SpvExecutionModeMaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case SpvExecutionModeNoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case SpvExecutionModeNumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + case SpvExecutionModeSchedulerTargetFmaxMhzINTEL: return "SchedulerTargetFmaxMhzINTEL"; + case SpvExecutionModeMaximallyReconvergesKHR: return "MaximallyReconvergesKHR"; + case SpvExecutionModeFPFastMathDefault: return "FPFastMathDefault"; + case SpvExecutionModeStreamingInterfaceINTEL: return "StreamingInterfaceINTEL"; + case SpvExecutionModeRegisterMapInterfaceINTEL: return "RegisterMapInterfaceINTEL"; + case SpvExecutionModeNamedBarrierCountINTEL: return "NamedBarrierCountINTEL"; + case SpvExecutionModeMaximumRegistersINTEL: return "MaximumRegistersINTEL"; + case SpvExecutionModeMaximumRegistersIdINTEL: return "MaximumRegistersIdINTEL"; + case SpvExecutionModeNamedMaximumRegistersINTEL: return "NamedMaximumRegistersINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvStorageClassToString(SpvStorageClass value) { + switch (value) { + case SpvStorageClassUniformConstant: return "UniformConstant"; + case SpvStorageClassInput: return "Input"; + case SpvStorageClassUniform: return "Uniform"; + case SpvStorageClassOutput: return "Output"; + case SpvStorageClassWorkgroup: return "Workgroup"; + case SpvStorageClassCrossWorkgroup: return "CrossWorkgroup"; + case SpvStorageClassPrivate: return "Private"; + case SpvStorageClassFunction: return "Function"; + case SpvStorageClassGeneric: return "Generic"; + case SpvStorageClassPushConstant: return "PushConstant"; + case SpvStorageClassAtomicCounter: return "AtomicCounter"; + case SpvStorageClassImage: return "Image"; + case SpvStorageClassStorageBuffer: return "StorageBuffer"; + case SpvStorageClassTileImageEXT: return "TileImageEXT"; + case SpvStorageClassTileAttachmentQCOM: return "TileAttachmentQCOM"; + case SpvStorageClassNodePayloadAMDX: return "NodePayloadAMDX"; + case SpvStorageClassCallableDataKHR: return "CallableDataKHR"; + case SpvStorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + case SpvStorageClassRayPayloadKHR: return "RayPayloadKHR"; + case SpvStorageClassHitAttributeKHR: return "HitAttributeKHR"; + case SpvStorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case SpvStorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case SpvStorageClassPhysicalStorageBuffer: return "PhysicalStorageBuffer"; + case SpvStorageClassHitObjectAttributeNV: return "HitObjectAttributeNV"; + case SpvStorageClassTaskPayloadWorkgroupEXT: return "TaskPayloadWorkgroupEXT"; + case SpvStorageClassHitObjectAttributeEXT: return "HitObjectAttributeEXT"; + case SpvStorageClassCodeSectionINTEL: return "CodeSectionINTEL"; + case SpvStorageClassDeviceOnlyALTERA: return "DeviceOnlyALTERA"; + case SpvStorageClassHostOnlyALTERA: return "HostOnlyALTERA"; + default: return "Unknown"; + } +} + +inline const char* SpvDimToString(SpvDim value) { + switch (value) { + case SpvDim1D: return "1D"; + case SpvDim2D: return "2D"; + case SpvDim3D: return "3D"; + case SpvDimCube: return "Cube"; + case SpvDimRect: return "Rect"; + case SpvDimBuffer: return "Buffer"; + case SpvDimSubpassData: return "SubpassData"; + case SpvDimTileImageDataEXT: return "TileImageDataEXT"; + default: return "Unknown"; + } +} + +inline const char* SpvSamplerAddressingModeToString(SpvSamplerAddressingMode value) { + switch (value) { + case SpvSamplerAddressingModeNone: return "None"; + case SpvSamplerAddressingModeClampToEdge: return "ClampToEdge"; + case SpvSamplerAddressingModeClamp: return "Clamp"; + case SpvSamplerAddressingModeRepeat: return "Repeat"; + case SpvSamplerAddressingModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SpvSamplerFilterModeToString(SpvSamplerFilterMode value) { + switch (value) { + case SpvSamplerFilterModeNearest: return "Nearest"; + case SpvSamplerFilterModeLinear: return "Linear"; + default: return "Unknown"; + } +} + +inline const char* SpvImageFormatToString(SpvImageFormat value) { + switch (value) { + case SpvImageFormatUnknown: return "Unknown"; + case SpvImageFormatRgba32f: return "Rgba32f"; + case SpvImageFormatRgba16f: return "Rgba16f"; + case SpvImageFormatR32f: return "R32f"; + case SpvImageFormatRgba8: return "Rgba8"; + case SpvImageFormatRgba8Snorm: return "Rgba8Snorm"; + case SpvImageFormatRg32f: return "Rg32f"; + case SpvImageFormatRg16f: return "Rg16f"; + case SpvImageFormatR11fG11fB10f: return "R11fG11fB10f"; + case SpvImageFormatR16f: return "R16f"; + case SpvImageFormatRgba16: return "Rgba16"; + case SpvImageFormatRgb10A2: return "Rgb10A2"; + case SpvImageFormatRg16: return "Rg16"; + case SpvImageFormatRg8: return "Rg8"; + case SpvImageFormatR16: return "R16"; + case SpvImageFormatR8: return "R8"; + case SpvImageFormatRgba16Snorm: return "Rgba16Snorm"; + case SpvImageFormatRg16Snorm: return "Rg16Snorm"; + case SpvImageFormatRg8Snorm: return "Rg8Snorm"; + case SpvImageFormatR16Snorm: return "R16Snorm"; + case SpvImageFormatR8Snorm: return "R8Snorm"; + case SpvImageFormatRgba32i: return "Rgba32i"; + case SpvImageFormatRgba16i: return "Rgba16i"; + case SpvImageFormatRgba8i: return "Rgba8i"; + case SpvImageFormatR32i: return "R32i"; + case SpvImageFormatRg32i: return "Rg32i"; + case SpvImageFormatRg16i: return "Rg16i"; + case SpvImageFormatRg8i: return "Rg8i"; + case SpvImageFormatR16i: return "R16i"; + case SpvImageFormatR8i: return "R8i"; + case SpvImageFormatRgba32ui: return "Rgba32ui"; + case SpvImageFormatRgba16ui: return "Rgba16ui"; + case SpvImageFormatRgba8ui: return "Rgba8ui"; + case SpvImageFormatR32ui: return "R32ui"; + case SpvImageFormatRgb10a2ui: return "Rgb10a2ui"; + case SpvImageFormatRg32ui: return "Rg32ui"; + case SpvImageFormatRg16ui: return "Rg16ui"; + case SpvImageFormatRg8ui: return "Rg8ui"; + case SpvImageFormatR16ui: return "R16ui"; + case SpvImageFormatR8ui: return "R8ui"; + case SpvImageFormatR64ui: return "R64ui"; + case SpvImageFormatR64i: return "R64i"; + default: return "Unknown"; + } +} + +inline const char* SpvImageChannelOrderToString(SpvImageChannelOrder value) { + switch (value) { + case SpvImageChannelOrderR: return "R"; + case SpvImageChannelOrderA: return "A"; + case SpvImageChannelOrderRG: return "RG"; + case SpvImageChannelOrderRA: return "RA"; + case SpvImageChannelOrderRGB: return "RGB"; + case SpvImageChannelOrderRGBA: return "RGBA"; + case SpvImageChannelOrderBGRA: return "BGRA"; + case SpvImageChannelOrderARGB: return "ARGB"; + case SpvImageChannelOrderIntensity: return "Intensity"; + case SpvImageChannelOrderLuminance: return "Luminance"; + case SpvImageChannelOrderRx: return "Rx"; + case SpvImageChannelOrderRGx: return "RGx"; + case SpvImageChannelOrderRGBx: return "RGBx"; + case SpvImageChannelOrderDepth: return "Depth"; + case SpvImageChannelOrderDepthStencil: return "DepthStencil"; + case SpvImageChannelOrdersRGB: return "sRGB"; + case SpvImageChannelOrdersRGBx: return "sRGBx"; + case SpvImageChannelOrdersRGBA: return "sRGBA"; + case SpvImageChannelOrdersBGRA: return "sBGRA"; + case SpvImageChannelOrderABGR: return "ABGR"; + default: return "Unknown"; + } +} + +inline const char* SpvImageChannelDataTypeToString(SpvImageChannelDataType value) { + switch (value) { + case SpvImageChannelDataTypeSnormInt8: return "SnormInt8"; + case SpvImageChannelDataTypeSnormInt16: return "SnormInt16"; + case SpvImageChannelDataTypeUnormInt8: return "UnormInt8"; + case SpvImageChannelDataTypeUnormInt16: return "UnormInt16"; + case SpvImageChannelDataTypeUnormShort565: return "UnormShort565"; + case SpvImageChannelDataTypeUnormShort555: return "UnormShort555"; + case SpvImageChannelDataTypeUnormInt101010: return "UnormInt101010"; + case SpvImageChannelDataTypeSignedInt8: return "SignedInt8"; + case SpvImageChannelDataTypeSignedInt16: return "SignedInt16"; + case SpvImageChannelDataTypeSignedInt32: return "SignedInt32"; + case SpvImageChannelDataTypeUnsignedInt8: return "UnsignedInt8"; + case SpvImageChannelDataTypeUnsignedInt16: return "UnsignedInt16"; + case SpvImageChannelDataTypeUnsignedInt32: return "UnsignedInt32"; + case SpvImageChannelDataTypeHalfFloat: return "HalfFloat"; + case SpvImageChannelDataTypeFloat: return "Float"; + case SpvImageChannelDataTypeUnormInt24: return "UnormInt24"; + case SpvImageChannelDataTypeUnormInt101010_2: return "UnormInt101010_2"; + case SpvImageChannelDataTypeUnormInt10X6EXT: return "UnormInt10X6EXT"; + case SpvImageChannelDataTypeUnsignedIntRaw10EXT: return "UnsignedIntRaw10EXT"; + case SpvImageChannelDataTypeUnsignedIntRaw12EXT: return "UnsignedIntRaw12EXT"; + case SpvImageChannelDataTypeUnormInt2_101010EXT: return "UnormInt2_101010EXT"; + case SpvImageChannelDataTypeUnsignedInt10X6EXT: return "UnsignedInt10X6EXT"; + case SpvImageChannelDataTypeUnsignedInt12X4EXT: return "UnsignedInt12X4EXT"; + case SpvImageChannelDataTypeUnsignedInt14X2EXT: return "UnsignedInt14X2EXT"; + case SpvImageChannelDataTypeUnormInt12X4EXT: return "UnormInt12X4EXT"; + case SpvImageChannelDataTypeUnormInt14X2EXT: return "UnormInt14X2EXT"; + default: return "Unknown"; + } +} + +inline const char* SpvFPRoundingModeToString(SpvFPRoundingMode value) { + switch (value) { + case SpvFPRoundingModeRTE: return "RTE"; + case SpvFPRoundingModeRTZ: return "RTZ"; + case SpvFPRoundingModeRTP: return "RTP"; + case SpvFPRoundingModeRTN: return "RTN"; + default: return "Unknown"; + } +} + +inline const char* SpvLinkageTypeToString(SpvLinkageType value) { + switch (value) { + case SpvLinkageTypeExport: return "Export"; + case SpvLinkageTypeImport: return "Import"; + case SpvLinkageTypeLinkOnceODR: return "LinkOnceODR"; + default: return "Unknown"; + } +} + +inline const char* SpvAccessQualifierToString(SpvAccessQualifier value) { + switch (value) { + case SpvAccessQualifierReadOnly: return "ReadOnly"; + case SpvAccessQualifierWriteOnly: return "WriteOnly"; + case SpvAccessQualifierReadWrite: return "ReadWrite"; + default: return "Unknown"; + } +} + +inline const char* SpvFunctionParameterAttributeToString(SpvFunctionParameterAttribute value) { + switch (value) { + case SpvFunctionParameterAttributeZext: return "Zext"; + case SpvFunctionParameterAttributeSext: return "Sext"; + case SpvFunctionParameterAttributeByVal: return "ByVal"; + case SpvFunctionParameterAttributeSret: return "Sret"; + case SpvFunctionParameterAttributeNoAlias: return "NoAlias"; + case SpvFunctionParameterAttributeNoCapture: return "NoCapture"; + case SpvFunctionParameterAttributeNoWrite: return "NoWrite"; + case SpvFunctionParameterAttributeNoReadWrite: return "NoReadWrite"; + case SpvFunctionParameterAttributeRuntimeAlignedALTERA: return "RuntimeAlignedALTERA"; + default: return "Unknown"; + } +} + +inline const char* SpvDecorationToString(SpvDecoration value) { + switch (value) { + case SpvDecorationRelaxedPrecision: return "RelaxedPrecision"; + case SpvDecorationSpecId: return "SpecId"; + case SpvDecorationBlock: return "Block"; + case SpvDecorationBufferBlock: return "BufferBlock"; + case SpvDecorationRowMajor: return "RowMajor"; + case SpvDecorationColMajor: return "ColMajor"; + case SpvDecorationArrayStride: return "ArrayStride"; + case SpvDecorationMatrixStride: return "MatrixStride"; + case SpvDecorationGLSLShared: return "GLSLShared"; + case SpvDecorationGLSLPacked: return "GLSLPacked"; + case SpvDecorationCPacked: return "CPacked"; + case SpvDecorationBuiltIn: return "BuiltIn"; + case SpvDecorationNoPerspective: return "NoPerspective"; + case SpvDecorationFlat: return "Flat"; + case SpvDecorationPatch: return "Patch"; + case SpvDecorationCentroid: return "Centroid"; + case SpvDecorationSample: return "Sample"; + case SpvDecorationInvariant: return "Invariant"; + case SpvDecorationRestrict: return "Restrict"; + case SpvDecorationAliased: return "Aliased"; + case SpvDecorationVolatile: return "Volatile"; + case SpvDecorationConstant: return "Constant"; + case SpvDecorationCoherent: return "Coherent"; + case SpvDecorationNonWritable: return "NonWritable"; + case SpvDecorationNonReadable: return "NonReadable"; + case SpvDecorationUniform: return "Uniform"; + case SpvDecorationUniformId: return "UniformId"; + case SpvDecorationSaturatedConversion: return "SaturatedConversion"; + case SpvDecorationStream: return "Stream"; + case SpvDecorationLocation: return "Location"; + case SpvDecorationComponent: return "Component"; + case SpvDecorationIndex: return "Index"; + case SpvDecorationBinding: return "Binding"; + case SpvDecorationDescriptorSet: return "DescriptorSet"; + case SpvDecorationOffset: return "Offset"; + case SpvDecorationXfbBuffer: return "XfbBuffer"; + case SpvDecorationXfbStride: return "XfbStride"; + case SpvDecorationFuncParamAttr: return "FuncParamAttr"; + case SpvDecorationFPRoundingMode: return "FPRoundingMode"; + case SpvDecorationFPFastMathMode: return "FPFastMathMode"; + case SpvDecorationLinkageAttributes: return "LinkageAttributes"; + case SpvDecorationNoContraction: return "NoContraction"; + case SpvDecorationInputAttachmentIndex: return "InputAttachmentIndex"; + case SpvDecorationAlignment: return "Alignment"; + case SpvDecorationMaxByteOffset: return "MaxByteOffset"; + case SpvDecorationAlignmentId: return "AlignmentId"; + case SpvDecorationMaxByteOffsetId: return "MaxByteOffsetId"; + case SpvDecorationSaturatedToLargestFloat8NormalConversionEXT: return "SaturatedToLargestFloat8NormalConversionEXT"; + case SpvDecorationNoSignedWrap: return "NoSignedWrap"; + case SpvDecorationNoUnsignedWrap: return "NoUnsignedWrap"; + case SpvDecorationWeightTextureQCOM: return "WeightTextureQCOM"; + case SpvDecorationBlockMatchTextureQCOM: return "BlockMatchTextureQCOM"; + case SpvDecorationBlockMatchSamplerQCOM: return "BlockMatchSamplerQCOM"; + case SpvDecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case SpvDecorationNodeSharesPayloadLimitsWithAMDX: return "NodeSharesPayloadLimitsWithAMDX"; + case SpvDecorationNodeMaxPayloadsAMDX: return "NodeMaxPayloadsAMDX"; + case SpvDecorationTrackFinishWritingAMDX: return "TrackFinishWritingAMDX"; + case SpvDecorationPayloadNodeNameAMDX: return "PayloadNodeNameAMDX"; + case SpvDecorationPayloadNodeBaseIndexAMDX: return "PayloadNodeBaseIndexAMDX"; + case SpvDecorationPayloadNodeSparseArrayAMDX: return "PayloadNodeSparseArrayAMDX"; + case SpvDecorationPayloadNodeArraySizeAMDX: return "PayloadNodeArraySizeAMDX"; + case SpvDecorationPayloadDispatchIndirectAMDX: return "PayloadDispatchIndirectAMDX"; + case SpvDecorationArrayStrideIdEXT: return "ArrayStrideIdEXT"; + case SpvDecorationOffsetIdEXT: return "OffsetIdEXT"; + case SpvDecorationUTFEncodedKHR: return "UTFEncodedKHR"; + case SpvDecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case SpvDecorationPassthroughNV: return "PassthroughNV"; + case SpvDecorationViewportRelativeNV: return "ViewportRelativeNV"; + case SpvDecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case SpvDecorationPerPrimitiveEXT: return "PerPrimitiveEXT"; + case SpvDecorationPerViewNV: return "PerViewNV"; + case SpvDecorationPerTaskNV: return "PerTaskNV"; + case SpvDecorationPerVertexKHR: return "PerVertexKHR"; + case SpvDecorationNonUniform: return "NonUniform"; + case SpvDecorationRestrictPointer: return "RestrictPointer"; + case SpvDecorationAliasedPointer: return "AliasedPointer"; + case SpvDecorationMemberOffsetNV: return "MemberOffsetNV"; + case SpvDecorationHitObjectShaderRecordBufferNV: return "HitObjectShaderRecordBufferNV"; + case SpvDecorationHitObjectShaderRecordBufferEXT: return "HitObjectShaderRecordBufferEXT"; + case SpvDecorationBankNV: return "BankNV"; + case SpvDecorationBindlessSamplerNV: return "BindlessSamplerNV"; + case SpvDecorationBindlessImageNV: return "BindlessImageNV"; + case SpvDecorationBoundSamplerNV: return "BoundSamplerNV"; + case SpvDecorationBoundImageNV: return "BoundImageNV"; + case SpvDecorationSIMTCallINTEL: return "SIMTCallINTEL"; + case SpvDecorationReferencedIndirectlyINTEL: return "ReferencedIndirectlyINTEL"; + case SpvDecorationClobberINTEL: return "ClobberINTEL"; + case SpvDecorationSideEffectsINTEL: return "SideEffectsINTEL"; + case SpvDecorationVectorComputeVariableINTEL: return "VectorComputeVariableINTEL"; + case SpvDecorationFuncParamIOKindINTEL: return "FuncParamIOKindINTEL"; + case SpvDecorationVectorComputeFunctionINTEL: return "VectorComputeFunctionINTEL"; + case SpvDecorationStackCallINTEL: return "StackCallINTEL"; + case SpvDecorationGlobalVariableOffsetINTEL: return "GlobalVariableOffsetINTEL"; + case SpvDecorationCounterBuffer: return "CounterBuffer"; + case SpvDecorationHlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; + case SpvDecorationUserTypeGOOGLE: return "UserTypeGOOGLE"; + case SpvDecorationFunctionRoundingModeINTEL: return "FunctionRoundingModeINTEL"; + case SpvDecorationFunctionDenormModeINTEL: return "FunctionDenormModeINTEL"; + case SpvDecorationRegisterALTERA: return "RegisterALTERA"; + case SpvDecorationMemoryALTERA: return "MemoryALTERA"; + case SpvDecorationNumbanksALTERA: return "NumbanksALTERA"; + case SpvDecorationBankwidthALTERA: return "BankwidthALTERA"; + case SpvDecorationMaxPrivateCopiesALTERA: return "MaxPrivateCopiesALTERA"; + case SpvDecorationSinglepumpALTERA: return "SinglepumpALTERA"; + case SpvDecorationDoublepumpALTERA: return "DoublepumpALTERA"; + case SpvDecorationMaxReplicatesALTERA: return "MaxReplicatesALTERA"; + case SpvDecorationSimpleDualPortALTERA: return "SimpleDualPortALTERA"; + case SpvDecorationMergeALTERA: return "MergeALTERA"; + case SpvDecorationBankBitsALTERA: return "BankBitsALTERA"; + case SpvDecorationForcePow2DepthALTERA: return "ForcePow2DepthALTERA"; + case SpvDecorationStridesizeALTERA: return "StridesizeALTERA"; + case SpvDecorationWordsizeALTERA: return "WordsizeALTERA"; + case SpvDecorationTrueDualPortALTERA: return "TrueDualPortALTERA"; + case SpvDecorationBurstCoalesceALTERA: return "BurstCoalesceALTERA"; + case SpvDecorationCacheSizeALTERA: return "CacheSizeALTERA"; + case SpvDecorationDontStaticallyCoalesceALTERA: return "DontStaticallyCoalesceALTERA"; + case SpvDecorationPrefetchALTERA: return "PrefetchALTERA"; + case SpvDecorationStallEnableALTERA: return "StallEnableALTERA"; + case SpvDecorationFuseLoopsInFunctionALTERA: return "FuseLoopsInFunctionALTERA"; + case SpvDecorationMathOpDSPModeALTERA: return "MathOpDSPModeALTERA"; + case SpvDecorationAliasScopeINTEL: return "AliasScopeINTEL"; + case SpvDecorationNoAliasINTEL: return "NoAliasINTEL"; + case SpvDecorationInitiationIntervalALTERA: return "InitiationIntervalALTERA"; + case SpvDecorationMaxConcurrencyALTERA: return "MaxConcurrencyALTERA"; + case SpvDecorationPipelineEnableALTERA: return "PipelineEnableALTERA"; + case SpvDecorationBufferLocationALTERA: return "BufferLocationALTERA"; + case SpvDecorationIOPipeStorageALTERA: return "IOPipeStorageALTERA"; + case SpvDecorationFunctionFloatingPointModeINTEL: return "FunctionFloatingPointModeINTEL"; + case SpvDecorationSingleElementVectorINTEL: return "SingleElementVectorINTEL"; + case SpvDecorationVectorComputeCallableFunctionINTEL: return "VectorComputeCallableFunctionINTEL"; + case SpvDecorationMediaBlockIOINTEL: return "MediaBlockIOINTEL"; + case SpvDecorationStallFreeALTERA: return "StallFreeALTERA"; + case SpvDecorationFPMaxErrorDecorationINTEL: return "FPMaxErrorDecorationINTEL"; + case SpvDecorationLatencyControlLabelALTERA: return "LatencyControlLabelALTERA"; + case SpvDecorationLatencyControlConstraintALTERA: return "LatencyControlConstraintALTERA"; + case SpvDecorationConduitKernelArgumentALTERA: return "ConduitKernelArgumentALTERA"; + case SpvDecorationRegisterMapKernelArgumentALTERA: return "RegisterMapKernelArgumentALTERA"; + case SpvDecorationMMHostInterfaceAddressWidthALTERA: return "MMHostInterfaceAddressWidthALTERA"; + case SpvDecorationMMHostInterfaceDataWidthALTERA: return "MMHostInterfaceDataWidthALTERA"; + case SpvDecorationMMHostInterfaceLatencyALTERA: return "MMHostInterfaceLatencyALTERA"; + case SpvDecorationMMHostInterfaceReadWriteModeALTERA: return "MMHostInterfaceReadWriteModeALTERA"; + case SpvDecorationMMHostInterfaceMaxBurstALTERA: return "MMHostInterfaceMaxBurstALTERA"; + case SpvDecorationMMHostInterfaceWaitRequestALTERA: return "MMHostInterfaceWaitRequestALTERA"; + case SpvDecorationStableKernelArgumentALTERA: return "StableKernelArgumentALTERA"; + case SpvDecorationHostAccessINTEL: return "HostAccessINTEL"; + case SpvDecorationInitModeALTERA: return "InitModeALTERA"; + case SpvDecorationImplementInRegisterMapALTERA: return "ImplementInRegisterMapALTERA"; + case SpvDecorationConditionalINTEL: return "ConditionalINTEL"; + case SpvDecorationCacheControlLoadINTEL: return "CacheControlLoadINTEL"; + case SpvDecorationCacheControlStoreINTEL: return "CacheControlStoreINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvBuiltInToString(SpvBuiltIn value) { + switch (value) { + case SpvBuiltInPosition: return "Position"; + case SpvBuiltInPointSize: return "PointSize"; + case SpvBuiltInClipDistance: return "ClipDistance"; + case SpvBuiltInCullDistance: return "CullDistance"; + case SpvBuiltInVertexId: return "VertexId"; + case SpvBuiltInInstanceId: return "InstanceId"; + case SpvBuiltInPrimitiveId: return "PrimitiveId"; + case SpvBuiltInInvocationId: return "InvocationId"; + case SpvBuiltInLayer: return "Layer"; + case SpvBuiltInViewportIndex: return "ViewportIndex"; + case SpvBuiltInTessLevelOuter: return "TessLevelOuter"; + case SpvBuiltInTessLevelInner: return "TessLevelInner"; + case SpvBuiltInTessCoord: return "TessCoord"; + case SpvBuiltInPatchVertices: return "PatchVertices"; + case SpvBuiltInFragCoord: return "FragCoord"; + case SpvBuiltInPointCoord: return "PointCoord"; + case SpvBuiltInFrontFacing: return "FrontFacing"; + case SpvBuiltInSampleId: return "SampleId"; + case SpvBuiltInSamplePosition: return "SamplePosition"; + case SpvBuiltInSampleMask: return "SampleMask"; + case SpvBuiltInFragDepth: return "FragDepth"; + case SpvBuiltInHelperInvocation: return "HelperInvocation"; + case SpvBuiltInNumWorkgroups: return "NumWorkgroups"; + case SpvBuiltInWorkgroupSize: return "WorkgroupSize"; + case SpvBuiltInWorkgroupId: return "WorkgroupId"; + case SpvBuiltInLocalInvocationId: return "LocalInvocationId"; + case SpvBuiltInGlobalInvocationId: return "GlobalInvocationId"; + case SpvBuiltInLocalInvocationIndex: return "LocalInvocationIndex"; + case SpvBuiltInWorkDim: return "WorkDim"; + case SpvBuiltInGlobalSize: return "GlobalSize"; + case SpvBuiltInEnqueuedWorkgroupSize: return "EnqueuedWorkgroupSize"; + case SpvBuiltInGlobalOffset: return "GlobalOffset"; + case SpvBuiltInGlobalLinearId: return "GlobalLinearId"; + case SpvBuiltInSubgroupSize: return "SubgroupSize"; + case SpvBuiltInSubgroupMaxSize: return "SubgroupMaxSize"; + case SpvBuiltInNumSubgroups: return "NumSubgroups"; + case SpvBuiltInNumEnqueuedSubgroups: return "NumEnqueuedSubgroups"; + case SpvBuiltInSubgroupId: return "SubgroupId"; + case SpvBuiltInSubgroupLocalInvocationId: return "SubgroupLocalInvocationId"; + case SpvBuiltInVertexIndex: return "VertexIndex"; + case SpvBuiltInInstanceIndex: return "InstanceIndex"; + case SpvBuiltInCoreIDARM: return "CoreIDARM"; + case SpvBuiltInCoreCountARM: return "CoreCountARM"; + case SpvBuiltInCoreMaxIDARM: return "CoreMaxIDARM"; + case SpvBuiltInWarpIDARM: return "WarpIDARM"; + case SpvBuiltInWarpMaxIDARM: return "WarpMaxIDARM"; + case SpvBuiltInSubgroupEqMask: return "SubgroupEqMask"; + case SpvBuiltInSubgroupGeMask: return "SubgroupGeMask"; + case SpvBuiltInSubgroupGtMask: return "SubgroupGtMask"; + case SpvBuiltInSubgroupLeMask: return "SubgroupLeMask"; + case SpvBuiltInSubgroupLtMask: return "SubgroupLtMask"; + case SpvBuiltInBaseVertex: return "BaseVertex"; + case SpvBuiltInBaseInstance: return "BaseInstance"; + case SpvBuiltInDrawIndex: return "DrawIndex"; + case SpvBuiltInPrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + case SpvBuiltInDeviceIndex: return "DeviceIndex"; + case SpvBuiltInViewIndex: return "ViewIndex"; + case SpvBuiltInShadingRateKHR: return "ShadingRateKHR"; + case SpvBuiltInTileOffsetQCOM: return "TileOffsetQCOM"; + case SpvBuiltInTileDimensionQCOM: return "TileDimensionQCOM"; + case SpvBuiltInTileApronSizeQCOM: return "TileApronSizeQCOM"; + case SpvBuiltInBaryCoordNoPerspAMD: return "BaryCoordNoPerspAMD"; + case SpvBuiltInBaryCoordNoPerspCentroidAMD: return "BaryCoordNoPerspCentroidAMD"; + case SpvBuiltInBaryCoordNoPerspSampleAMD: return "BaryCoordNoPerspSampleAMD"; + case SpvBuiltInBaryCoordSmoothAMD: return "BaryCoordSmoothAMD"; + case SpvBuiltInBaryCoordSmoothCentroidAMD: return "BaryCoordSmoothCentroidAMD"; + case SpvBuiltInBaryCoordSmoothSampleAMD: return "BaryCoordSmoothSampleAMD"; + case SpvBuiltInBaryCoordPullModelAMD: return "BaryCoordPullModelAMD"; + case SpvBuiltInFragStencilRefEXT: return "FragStencilRefEXT"; + case SpvBuiltInRemainingRecursionLevelsAMDX: return "RemainingRecursionLevelsAMDX"; + case SpvBuiltInShaderIndexAMDX: return "ShaderIndexAMDX"; + case SpvBuiltInSamplerHeapEXT: return "SamplerHeapEXT"; + case SpvBuiltInResourceHeapEXT: return "ResourceHeapEXT"; + case SpvBuiltInViewportMaskNV: return "ViewportMaskNV"; + case SpvBuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case SpvBuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case SpvBuiltInPositionPerViewNV: return "PositionPerViewNV"; + case SpvBuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case SpvBuiltInFullyCoveredEXT: return "FullyCoveredEXT"; + case SpvBuiltInTaskCountNV: return "TaskCountNV"; + case SpvBuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case SpvBuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case SpvBuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case SpvBuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case SpvBuiltInLayerPerViewNV: return "LayerPerViewNV"; + case SpvBuiltInMeshViewCountNV: return "MeshViewCountNV"; + case SpvBuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case SpvBuiltInBaryCoordKHR: return "BaryCoordKHR"; + case SpvBuiltInBaryCoordNoPerspKHR: return "BaryCoordNoPerspKHR"; + case SpvBuiltInFragSizeEXT: return "FragSizeEXT"; + case SpvBuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + case SpvBuiltInPrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case SpvBuiltInPrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case SpvBuiltInPrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case SpvBuiltInCullPrimitiveEXT: return "CullPrimitiveEXT"; + case SpvBuiltInLaunchIdKHR: return "LaunchIdKHR"; + case SpvBuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case SpvBuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case SpvBuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case SpvBuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case SpvBuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case SpvBuiltInRayTminKHR: return "RayTminKHR"; + case SpvBuiltInRayTmaxKHR: return "RayTmaxKHR"; + case SpvBuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case SpvBuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case SpvBuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case SpvBuiltInHitTNV: return "HitTNV"; + case SpvBuiltInHitKindKHR: return "HitKindKHR"; + case SpvBuiltInCurrentRayTimeNV: return "CurrentRayTimeNV"; + case SpvBuiltInHitTriangleVertexPositionsKHR: return "HitTriangleVertexPositionsKHR"; + case SpvBuiltInHitMicroTriangleVertexPositionsNV: return "HitMicroTriangleVertexPositionsNV"; + case SpvBuiltInHitMicroTriangleVertexBarycentricsNV: return "HitMicroTriangleVertexBarycentricsNV"; + case SpvBuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case SpvBuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case SpvBuiltInHitIsSphereNV: return "HitIsSphereNV"; + case SpvBuiltInHitIsLSSNV: return "HitIsLSSNV"; + case SpvBuiltInHitSpherePositionNV: return "HitSpherePositionNV"; + case SpvBuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case SpvBuiltInSMCountNV: return "SMCountNV"; + case SpvBuiltInWarpIDNV: return "WarpIDNV"; + case SpvBuiltInSMIDNV: return "SMIDNV"; + case SpvBuiltInHitLSSPositionsNV: return "HitLSSPositionsNV"; + case SpvBuiltInHitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case SpvBuiltInHitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + case SpvBuiltInHitSphereRadiusNV: return "HitSphereRadiusNV"; + case SpvBuiltInHitLSSRadiiNV: return "HitLSSRadiiNV"; + case SpvBuiltInClusterIDNV: return "ClusterIDNV"; + case SpvBuiltInCullMaskKHR: return "CullMaskKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvScopeToString(SpvScope value) { + switch (value) { + case SpvScopeCrossDevice: return "CrossDevice"; + case SpvScopeDevice: return "Device"; + case SpvScopeWorkgroup: return "Workgroup"; + case SpvScopeSubgroup: return "Subgroup"; + case SpvScopeInvocation: return "Invocation"; + case SpvScopeQueueFamily: return "QueueFamily"; + case SpvScopeShaderCallKHR: return "ShaderCallKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvGroupOperationToString(SpvGroupOperation value) { + switch (value) { + case SpvGroupOperationReduce: return "Reduce"; + case SpvGroupOperationInclusiveScan: return "InclusiveScan"; + case SpvGroupOperationExclusiveScan: return "ExclusiveScan"; + case SpvGroupOperationClusteredReduce: return "ClusteredReduce"; + case SpvGroupOperationPartitionedReduceEXT: return "PartitionedReduceEXT"; + case SpvGroupOperationPartitionedInclusiveScanEXT: return "PartitionedInclusiveScanEXT"; + case SpvGroupOperationPartitionedExclusiveScanEXT: return "PartitionedExclusiveScanEXT"; + default: return "Unknown"; + } +} + +inline const char* SpvKernelEnqueueFlagsToString(SpvKernelEnqueueFlags value) { + switch (value) { + case SpvKernelEnqueueFlagsNoWait: return "NoWait"; + case SpvKernelEnqueueFlagsWaitKernel: return "WaitKernel"; + case SpvKernelEnqueueFlagsWaitWorkGroup: return "WaitWorkGroup"; + default: return "Unknown"; + } +} + +inline const char* SpvCapabilityToString(SpvCapability value) { + switch (value) { + case SpvCapabilityMatrix: return "Matrix"; + case SpvCapabilityShader: return "Shader"; + case SpvCapabilityGeometry: return "Geometry"; + case SpvCapabilityTessellation: return "Tessellation"; + case SpvCapabilityAddresses: return "Addresses"; + case SpvCapabilityLinkage: return "Linkage"; + case SpvCapabilityKernel: return "Kernel"; + case SpvCapabilityVector16: return "Vector16"; + case SpvCapabilityFloat16Buffer: return "Float16Buffer"; + case SpvCapabilityFloat16: return "Float16"; + case SpvCapabilityFloat64: return "Float64"; + case SpvCapabilityInt64: return "Int64"; + case SpvCapabilityInt64Atomics: return "Int64Atomics"; + case SpvCapabilityImageBasic: return "ImageBasic"; + case SpvCapabilityImageReadWrite: return "ImageReadWrite"; + case SpvCapabilityImageMipmap: return "ImageMipmap"; + case SpvCapabilityPipes: return "Pipes"; + case SpvCapabilityGroups: return "Groups"; + case SpvCapabilityDeviceEnqueue: return "DeviceEnqueue"; + case SpvCapabilityLiteralSampler: return "LiteralSampler"; + case SpvCapabilityAtomicStorage: return "AtomicStorage"; + case SpvCapabilityInt16: return "Int16"; + case SpvCapabilityTessellationPointSize: return "TessellationPointSize"; + case SpvCapabilityGeometryPointSize: return "GeometryPointSize"; + case SpvCapabilityImageGatherExtended: return "ImageGatherExtended"; + case SpvCapabilityStorageImageMultisample: return "StorageImageMultisample"; + case SpvCapabilityUniformBufferArrayDynamicIndexing: return "UniformBufferArrayDynamicIndexing"; + case SpvCapabilitySampledImageArrayDynamicIndexing: return "SampledImageArrayDynamicIndexing"; + case SpvCapabilityStorageBufferArrayDynamicIndexing: return "StorageBufferArrayDynamicIndexing"; + case SpvCapabilityStorageImageArrayDynamicIndexing: return "StorageImageArrayDynamicIndexing"; + case SpvCapabilityClipDistance: return "ClipDistance"; + case SpvCapabilityCullDistance: return "CullDistance"; + case SpvCapabilityImageCubeArray: return "ImageCubeArray"; + case SpvCapabilitySampleRateShading: return "SampleRateShading"; + case SpvCapabilityImageRect: return "ImageRect"; + case SpvCapabilitySampledRect: return "SampledRect"; + case SpvCapabilityGenericPointer: return "GenericPointer"; + case SpvCapabilityInt8: return "Int8"; + case SpvCapabilityInputAttachment: return "InputAttachment"; + case SpvCapabilitySparseResidency: return "SparseResidency"; + case SpvCapabilityMinLod: return "MinLod"; + case SpvCapabilitySampled1D: return "Sampled1D"; + case SpvCapabilityImage1D: return "Image1D"; + case SpvCapabilitySampledCubeArray: return "SampledCubeArray"; + case SpvCapabilitySampledBuffer: return "SampledBuffer"; + case SpvCapabilityImageBuffer: return "ImageBuffer"; + case SpvCapabilityImageMSArray: return "ImageMSArray"; + case SpvCapabilityStorageImageExtendedFormats: return "StorageImageExtendedFormats"; + case SpvCapabilityImageQuery: return "ImageQuery"; + case SpvCapabilityDerivativeControl: return "DerivativeControl"; + case SpvCapabilityInterpolationFunction: return "InterpolationFunction"; + case SpvCapabilityTransformFeedback: return "TransformFeedback"; + case SpvCapabilityGeometryStreams: return "GeometryStreams"; + case SpvCapabilityStorageImageReadWithoutFormat: return "StorageImageReadWithoutFormat"; + case SpvCapabilityStorageImageWriteWithoutFormat: return "StorageImageWriteWithoutFormat"; + case SpvCapabilityMultiViewport: return "MultiViewport"; + case SpvCapabilitySubgroupDispatch: return "SubgroupDispatch"; + case SpvCapabilityNamedBarrier: return "NamedBarrier"; + case SpvCapabilityPipeStorage: return "PipeStorage"; + case SpvCapabilityGroupNonUniform: return "GroupNonUniform"; + case SpvCapabilityGroupNonUniformVote: return "GroupNonUniformVote"; + case SpvCapabilityGroupNonUniformArithmetic: return "GroupNonUniformArithmetic"; + case SpvCapabilityGroupNonUniformBallot: return "GroupNonUniformBallot"; + case SpvCapabilityGroupNonUniformShuffle: return "GroupNonUniformShuffle"; + case SpvCapabilityGroupNonUniformShuffleRelative: return "GroupNonUniformShuffleRelative"; + case SpvCapabilityGroupNonUniformClustered: return "GroupNonUniformClustered"; + case SpvCapabilityGroupNonUniformQuad: return "GroupNonUniformQuad"; + case SpvCapabilityShaderLayer: return "ShaderLayer"; + case SpvCapabilityShaderViewportIndex: return "ShaderViewportIndex"; + case SpvCapabilityUniformDecoration: return "UniformDecoration"; + case SpvCapabilityCoreBuiltinsARM: return "CoreBuiltinsARM"; + case SpvCapabilityTileImageColorReadAccessEXT: return "TileImageColorReadAccessEXT"; + case SpvCapabilityTileImageDepthReadAccessEXT: return "TileImageDepthReadAccessEXT"; + case SpvCapabilityTileImageStencilReadAccessEXT: return "TileImageStencilReadAccessEXT"; + case SpvCapabilityTensorsARM: return "TensorsARM"; + case SpvCapabilityStorageTensorArrayDynamicIndexingARM: return "StorageTensorArrayDynamicIndexingARM"; + case SpvCapabilityStorageTensorArrayNonUniformIndexingARM: return "StorageTensorArrayNonUniformIndexingARM"; + case SpvCapabilityGraphARM: return "GraphARM"; + case SpvCapabilityCooperativeMatrixLayoutsARM: return "CooperativeMatrixLayoutsARM"; + case SpvCapabilityFloat8EXT: return "Float8EXT"; + case SpvCapabilityFloat8CooperativeMatrixEXT: return "Float8CooperativeMatrixEXT"; + case SpvCapabilityFragmentShadingRateKHR: return "FragmentShadingRateKHR"; + case SpvCapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case SpvCapabilityDrawParameters: return "DrawParameters"; + case SpvCapabilityWorkgroupMemoryExplicitLayoutKHR: return "WorkgroupMemoryExplicitLayoutKHR"; + case SpvCapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: return "WorkgroupMemoryExplicitLayout8BitAccessKHR"; + case SpvCapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: return "WorkgroupMemoryExplicitLayout16BitAccessKHR"; + case SpvCapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + case SpvCapabilityStorageBuffer16BitAccess: return "StorageBuffer16BitAccess"; + case SpvCapabilityStorageUniform16: return "StorageUniform16"; + case SpvCapabilityStoragePushConstant16: return "StoragePushConstant16"; + case SpvCapabilityStorageInputOutput16: return "StorageInputOutput16"; + case SpvCapabilityDeviceGroup: return "DeviceGroup"; + case SpvCapabilityMultiView: return "MultiView"; + case SpvCapabilityVariablePointersStorageBuffer: return "VariablePointersStorageBuffer"; + case SpvCapabilityVariablePointers: return "VariablePointers"; + case SpvCapabilityAtomicStorageOps: return "AtomicStorageOps"; + case SpvCapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case SpvCapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case SpvCapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case SpvCapabilityStoragePushConstant8: return "StoragePushConstant8"; + case SpvCapabilityDenormPreserve: return "DenormPreserve"; + case SpvCapabilityDenormFlushToZero: return "DenormFlushToZero"; + case SpvCapabilitySignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case SpvCapabilityRoundingModeRTE: return "RoundingModeRTE"; + case SpvCapabilityRoundingModeRTZ: return "RoundingModeRTZ"; + case SpvCapabilityRayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case SpvCapabilityRayQueryKHR: return "RayQueryKHR"; + case SpvCapabilityUntypedPointersKHR: return "UntypedPointersKHR"; + case SpvCapabilityRayTraversalPrimitiveCullingKHR: return "RayTraversalPrimitiveCullingKHR"; + case SpvCapabilityRayTracingKHR: return "RayTracingKHR"; + case SpvCapabilityTextureSampleWeightedQCOM: return "TextureSampleWeightedQCOM"; + case SpvCapabilityTextureBoxFilterQCOM: return "TextureBoxFilterQCOM"; + case SpvCapabilityTextureBlockMatchQCOM: return "TextureBlockMatchQCOM"; + case SpvCapabilityTileShadingQCOM: return "TileShadingQCOM"; + case SpvCapabilityCooperativeMatrixConversionQCOM: return "CooperativeMatrixConversionQCOM"; + case SpvCapabilityTextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + case SpvCapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case SpvCapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case SpvCapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case SpvCapabilityStencilExportEXT: return "StencilExportEXT"; + case SpvCapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + case SpvCapabilityInt64ImageEXT: return "Int64ImageEXT"; + case SpvCapabilityShaderClockKHR: return "ShaderClockKHR"; + case SpvCapabilityShaderEnqueueAMDX: return "ShaderEnqueueAMDX"; + case SpvCapabilityQuadControlKHR: return "QuadControlKHR"; + case SpvCapabilityInt4TypeINTEL: return "Int4TypeINTEL"; + case SpvCapabilityInt4CooperativeMatrixINTEL: return "Int4CooperativeMatrixINTEL"; + case SpvCapabilityBFloat16TypeKHR: return "BFloat16TypeKHR"; + case SpvCapabilityBFloat16DotProductKHR: return "BFloat16DotProductKHR"; + case SpvCapabilityBFloat16CooperativeMatrixKHR: return "BFloat16CooperativeMatrixKHR"; + case SpvCapabilityAbortKHR: return "AbortKHR"; + case SpvCapabilityDescriptorHeapEXT: return "DescriptorHeapEXT"; + case SpvCapabilityConstantDataKHR: return "ConstantDataKHR"; + case SpvCapabilityPoisonFreezeKHR: return "PoisonFreezeKHR"; + case SpvCapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case SpvCapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case SpvCapabilityShaderViewportIndexLayerEXT: return "ShaderViewportIndexLayerEXT"; + case SpvCapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case SpvCapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case SpvCapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case SpvCapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + case SpvCapabilityMeshShadingNV: return "MeshShadingNV"; + case SpvCapabilityImageFootprintNV: return "ImageFootprintNV"; + case SpvCapabilityMeshShadingEXT: return "MeshShadingEXT"; + case SpvCapabilityFragmentBarycentricKHR: return "FragmentBarycentricKHR"; + case SpvCapabilityComputeDerivativeGroupQuadsKHR: return "ComputeDerivativeGroupQuadsKHR"; + case SpvCapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + case SpvCapabilityGroupNonUniformPartitionedEXT: return "GroupNonUniformPartitionedEXT"; + case SpvCapabilityShaderNonUniform: return "ShaderNonUniform"; + case SpvCapabilityRuntimeDescriptorArray: return "RuntimeDescriptorArray"; + case SpvCapabilityInputAttachmentArrayDynamicIndexing: return "InputAttachmentArrayDynamicIndexing"; + case SpvCapabilityUniformTexelBufferArrayDynamicIndexing: return "UniformTexelBufferArrayDynamicIndexing"; + case SpvCapabilityStorageTexelBufferArrayDynamicIndexing: return "StorageTexelBufferArrayDynamicIndexing"; + case SpvCapabilityUniformBufferArrayNonUniformIndexing: return "UniformBufferArrayNonUniformIndexing"; + case SpvCapabilitySampledImageArrayNonUniformIndexing: return "SampledImageArrayNonUniformIndexing"; + case SpvCapabilityStorageBufferArrayNonUniformIndexing: return "StorageBufferArrayNonUniformIndexing"; + case SpvCapabilityStorageImageArrayNonUniformIndexing: return "StorageImageArrayNonUniformIndexing"; + case SpvCapabilityInputAttachmentArrayNonUniformIndexing: return "InputAttachmentArrayNonUniformIndexing"; + case SpvCapabilityUniformTexelBufferArrayNonUniformIndexing: return "UniformTexelBufferArrayNonUniformIndexing"; + case SpvCapabilityStorageTexelBufferArrayNonUniformIndexing: return "StorageTexelBufferArrayNonUniformIndexing"; + case SpvCapabilityRayTracingPositionFetchKHR: return "RayTracingPositionFetchKHR"; + case SpvCapabilityRayTracingNV: return "RayTracingNV"; + case SpvCapabilityRayTracingMotionBlurNV: return "RayTracingMotionBlurNV"; + case SpvCapabilityVulkanMemoryModel: return "VulkanMemoryModel"; + case SpvCapabilityVulkanMemoryModelDeviceScope: return "VulkanMemoryModelDeviceScope"; + case SpvCapabilityPhysicalStorageBufferAddresses: return "PhysicalStorageBufferAddresses"; + case SpvCapabilityComputeDerivativeGroupLinearKHR: return "ComputeDerivativeGroupLinearKHR"; + case SpvCapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case SpvCapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case SpvCapabilityFragmentShaderSampleInterlockEXT: return "FragmentShaderSampleInterlockEXT"; + case SpvCapabilityFragmentShaderShadingRateInterlockEXT: return "FragmentShaderShadingRateInterlockEXT"; + case SpvCapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + case SpvCapabilityFragmentShaderPixelInterlockEXT: return "FragmentShaderPixelInterlockEXT"; + case SpvCapabilityDemoteToHelperInvocation: return "DemoteToHelperInvocation"; + case SpvCapabilityDisplacementMicromapNV: return "DisplacementMicromapNV"; + case SpvCapabilityRayTracingOpacityMicromapEXT: return "RayTracingOpacityMicromapEXT"; + case SpvCapabilityShaderInvocationReorderNV: return "ShaderInvocationReorderNV"; + case SpvCapabilityShaderInvocationReorderEXT: return "ShaderInvocationReorderEXT"; + case SpvCapabilityBindlessTextureNV: return "BindlessTextureNV"; + case SpvCapabilityRayQueryPositionFetchKHR: return "RayQueryPositionFetchKHR"; + case SpvCapabilityCooperativeVectorNV: return "CooperativeVectorNV"; + case SpvCapabilityAtomicFloat16VectorNV: return "AtomicFloat16VectorNV"; + case SpvCapabilityRayTracingDisplacementMicromapNV: return "RayTracingDisplacementMicromapNV"; + case SpvCapabilityRawAccessChainsNV: return "RawAccessChainsNV"; + case SpvCapabilityRayTracingSpheresGeometryNV: return "RayTracingSpheresGeometryNV"; + case SpvCapabilityRayTracingLinearSweptSpheresGeometryNV: return "RayTracingLinearSweptSpheresGeometryNV"; + case SpvCapabilityPushConstantBanksNV: return "PushConstantBanksNV"; + case SpvCapabilityLongVectorEXT: return "LongVectorEXT"; + case SpvCapabilityShader64BitIndexingEXT: return "Shader64BitIndexingEXT"; + case SpvCapabilityCooperativeMatrixReductionsNV: return "CooperativeMatrixReductionsNV"; + case SpvCapabilityCooperativeMatrixConversionsNV: return "CooperativeMatrixConversionsNV"; + case SpvCapabilityCooperativeMatrixPerElementOperationsNV: return "CooperativeMatrixPerElementOperationsNV"; + case SpvCapabilityCooperativeMatrixTensorAddressingNV: return "CooperativeMatrixTensorAddressingNV"; + case SpvCapabilityCooperativeMatrixBlockLoadsNV: return "CooperativeMatrixBlockLoadsNV"; + case SpvCapabilityCooperativeVectorTrainingNV: return "CooperativeVectorTrainingNV"; + case SpvCapabilityRayTracingClusterAccelerationStructureNV: return "RayTracingClusterAccelerationStructureNV"; + case SpvCapabilityTensorAddressingNV: return "TensorAddressingNV"; + case SpvCapabilitySubgroupShuffleINTEL: return "SubgroupShuffleINTEL"; + case SpvCapabilitySubgroupBufferBlockIOINTEL: return "SubgroupBufferBlockIOINTEL"; + case SpvCapabilitySubgroupImageBlockIOINTEL: return "SubgroupImageBlockIOINTEL"; + case SpvCapabilitySubgroupImageMediaBlockIOINTEL: return "SubgroupImageMediaBlockIOINTEL"; + case SpvCapabilityRoundToInfinityINTEL: return "RoundToInfinityINTEL"; + case SpvCapabilityFloatingPointModeINTEL: return "FloatingPointModeINTEL"; + case SpvCapabilityIntegerFunctions2INTEL: return "IntegerFunctions2INTEL"; + case SpvCapabilityFunctionPointersINTEL: return "FunctionPointersINTEL"; + case SpvCapabilityIndirectReferencesINTEL: return "IndirectReferencesINTEL"; + case SpvCapabilityAsmINTEL: return "AsmINTEL"; + case SpvCapabilityAtomicFloat32MinMaxEXT: return "AtomicFloat32MinMaxEXT"; + case SpvCapabilityAtomicFloat64MinMaxEXT: return "AtomicFloat64MinMaxEXT"; + case SpvCapabilityAtomicFloat16MinMaxEXT: return "AtomicFloat16MinMaxEXT"; + case SpvCapabilityVectorComputeINTEL: return "VectorComputeINTEL"; + case SpvCapabilityVectorAnyINTEL: return "VectorAnyINTEL"; + case SpvCapabilityExpectAssumeKHR: return "ExpectAssumeKHR"; + case SpvCapabilitySubgroupAvcMotionEstimationINTEL: return "SubgroupAvcMotionEstimationINTEL"; + case SpvCapabilitySubgroupAvcMotionEstimationIntraINTEL: return "SubgroupAvcMotionEstimationIntraINTEL"; + case SpvCapabilitySubgroupAvcMotionEstimationChromaINTEL: return "SubgroupAvcMotionEstimationChromaINTEL"; + case SpvCapabilityVariableLengthArrayINTEL: return "VariableLengthArrayINTEL"; + case SpvCapabilityFunctionFloatControlINTEL: return "FunctionFloatControlINTEL"; + case SpvCapabilityFPGAMemoryAttributesALTERA: return "FPGAMemoryAttributesALTERA"; + case SpvCapabilityFPFastMathModeINTEL: return "FPFastMathModeINTEL"; + case SpvCapabilityArbitraryPrecisionIntegersALTERA: return "ArbitraryPrecisionIntegersALTERA"; + case SpvCapabilityArbitraryPrecisionFloatingPointALTERA: return "ArbitraryPrecisionFloatingPointALTERA"; + case SpvCapabilityUnstructuredLoopControlsINTEL: return "UnstructuredLoopControlsINTEL"; + case SpvCapabilityFPGALoopControlsALTERA: return "FPGALoopControlsALTERA"; + case SpvCapabilityKernelAttributesINTEL: return "KernelAttributesINTEL"; + case SpvCapabilityFPGAKernelAttributesINTEL: return "FPGAKernelAttributesINTEL"; + case SpvCapabilityFPGAMemoryAccessesALTERA: return "FPGAMemoryAccessesALTERA"; + case SpvCapabilityFPGAClusterAttributesALTERA: return "FPGAClusterAttributesALTERA"; + case SpvCapabilityLoopFuseALTERA: return "LoopFuseALTERA"; + case SpvCapabilityFPGADSPControlALTERA: return "FPGADSPControlALTERA"; + case SpvCapabilityMemoryAccessAliasingINTEL: return "MemoryAccessAliasingINTEL"; + case SpvCapabilityFPGAInvocationPipeliningAttributesALTERA: return "FPGAInvocationPipeliningAttributesALTERA"; + case SpvCapabilityFPGABufferLocationALTERA: return "FPGABufferLocationALTERA"; + case SpvCapabilityArbitraryPrecisionFixedPointALTERA: return "ArbitraryPrecisionFixedPointALTERA"; + case SpvCapabilityUSMStorageClassesALTERA: return "USMStorageClassesALTERA"; + case SpvCapabilityRuntimeAlignedAttributeALTERA: return "RuntimeAlignedAttributeALTERA"; + case SpvCapabilityIOPipesALTERA: return "IOPipesALTERA"; + case SpvCapabilityBlockingPipesALTERA: return "BlockingPipesALTERA"; + case SpvCapabilityFPGARegALTERA: return "FPGARegALTERA"; + case SpvCapabilityDotProductInputAll: return "DotProductInputAll"; + case SpvCapabilityDotProductInput4x8Bit: return "DotProductInput4x8Bit"; + case SpvCapabilityDotProductInput4x8BitPacked: return "DotProductInput4x8BitPacked"; + case SpvCapabilityDotProduct: return "DotProduct"; + case SpvCapabilityRayCullMaskKHR: return "RayCullMaskKHR"; + case SpvCapabilityCooperativeMatrixKHR: return "CooperativeMatrixKHR"; + case SpvCapabilityReplicatedCompositesEXT: return "ReplicatedCompositesEXT"; + case SpvCapabilityBitInstructions: return "BitInstructions"; + case SpvCapabilityGroupNonUniformRotateKHR: return "GroupNonUniformRotateKHR"; + case SpvCapabilityFloatControls2: return "FloatControls2"; + case SpvCapabilityFMAKHR: return "FMAKHR"; + case SpvCapabilityAtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case SpvCapabilityAtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + case SpvCapabilityLongCompositesINTEL: return "LongCompositesINTEL"; + case SpvCapabilityOptNoneEXT: return "OptNoneEXT"; + case SpvCapabilityAtomicFloat16AddEXT: return "AtomicFloat16AddEXT"; + case SpvCapabilityDebugInfoModuleINTEL: return "DebugInfoModuleINTEL"; + case SpvCapabilityBFloat16ConversionINTEL: return "BFloat16ConversionINTEL"; + case SpvCapabilitySplitBarrierINTEL: return "SplitBarrierINTEL"; + case SpvCapabilityArithmeticFenceEXT: return "ArithmeticFenceEXT"; + case SpvCapabilityFPGAClusterAttributesV2ALTERA: return "FPGAClusterAttributesV2ALTERA"; + case SpvCapabilityFPGAKernelAttributesv2INTEL: return "FPGAKernelAttributesv2INTEL"; + case SpvCapabilityTaskSequenceALTERA: return "TaskSequenceALTERA"; + case SpvCapabilityFPMaxErrorINTEL: return "FPMaxErrorINTEL"; + case SpvCapabilityFPGALatencyControlALTERA: return "FPGALatencyControlALTERA"; + case SpvCapabilityFPGAArgumentInterfacesALTERA: return "FPGAArgumentInterfacesALTERA"; + case SpvCapabilityGlobalVariableHostAccessINTEL: return "GlobalVariableHostAccessINTEL"; + case SpvCapabilityGlobalVariableFPGADecorationsALTERA: return "GlobalVariableFPGADecorationsALTERA"; + case SpvCapabilitySubgroupBufferPrefetchINTEL: return "SubgroupBufferPrefetchINTEL"; + case SpvCapabilitySubgroup2DBlockIOINTEL: return "Subgroup2DBlockIOINTEL"; + case SpvCapabilitySubgroup2DBlockTransformINTEL: return "Subgroup2DBlockTransformINTEL"; + case SpvCapabilitySubgroup2DBlockTransposeINTEL: return "Subgroup2DBlockTransposeINTEL"; + case SpvCapabilitySubgroupMatrixMultiplyAccumulateINTEL: return "SubgroupMatrixMultiplyAccumulateINTEL"; + case SpvCapabilityTernaryBitwiseFunctionINTEL: return "TernaryBitwiseFunctionINTEL"; + case SpvCapabilityUntypedVariableLengthArrayINTEL: return "UntypedVariableLengthArrayINTEL"; + case SpvCapabilitySpecConditionalINTEL: return "SpecConditionalINTEL"; + case SpvCapabilityFunctionVariantsINTEL: return "FunctionVariantsINTEL"; + case SpvCapabilityGroupUniformArithmeticKHR: return "GroupUniformArithmeticKHR"; + case SpvCapabilityTensorFloat32RoundingINTEL: return "TensorFloat32RoundingINTEL"; + case SpvCapabilityMaskedGatherScatterINTEL: return "MaskedGatherScatterINTEL"; + case SpvCapabilityCacheControlsINTEL: return "CacheControlsINTEL"; + case SpvCapabilityRegisterLimitsINTEL: return "RegisterLimitsINTEL"; + case SpvCapabilityBindlessImagesINTEL: return "BindlessImagesINTEL"; + case SpvCapabilityDotProductFloat16AccFloat32VALVE: return "DotProductFloat16AccFloat32VALVE"; + case SpvCapabilityDotProductFloat16AccFloat16VALVE: return "DotProductFloat16AccFloat16VALVE"; + case SpvCapabilityDotProductBFloat16AccVALVE: return "DotProductBFloat16AccVALVE"; + case SpvCapabilityDotProductFloat8AccFloat32VALVE: return "DotProductFloat8AccFloat32VALVE"; + default: return "Unknown"; + } +} + +inline const char* SpvRayQueryIntersectionToString(SpvRayQueryIntersection value) { + switch (value) { + case SpvRayQueryIntersectionRayQueryCandidateIntersectionKHR: return "RayQueryCandidateIntersectionKHR"; + case SpvRayQueryIntersectionRayQueryCommittedIntersectionKHR: return "RayQueryCommittedIntersectionKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvRayQueryCommittedIntersectionTypeToString(SpvRayQueryCommittedIntersectionType value) { + switch (value) { + case SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR: return "RayQueryCommittedIntersectionNoneKHR"; + case SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR: return "RayQueryCommittedIntersectionTriangleKHR"; + case SpvRayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR: return "RayQueryCommittedIntersectionGeneratedKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvRayQueryCandidateIntersectionTypeToString(SpvRayQueryCandidateIntersectionType value) { + switch (value) { + case SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR: return "RayQueryCandidateIntersectionTriangleKHR"; + case SpvRayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR: return "RayQueryCandidateIntersectionAABBKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvFPDenormModeToString(SpvFPDenormMode value) { + switch (value) { + case SpvFPDenormModePreserve: return "Preserve"; + case SpvFPDenormModeFlushToZero: return "FlushToZero"; + default: return "Unknown"; + } +} + +inline const char* SpvFPOperationModeToString(SpvFPOperationMode value) { + switch (value) { + case SpvFPOperationModeIEEE: return "IEEE"; + case SpvFPOperationModeALT: return "ALT"; + default: return "Unknown"; + } +} + +inline const char* SpvQuantizationModesToString(SpvQuantizationModes value) { + switch (value) { + case SpvQuantizationModesTRN: return "TRN"; + case SpvQuantizationModesTRN_ZERO: return "TRN_ZERO"; + case SpvQuantizationModesRND: return "RND"; + case SpvQuantizationModesRND_ZERO: return "RND_ZERO"; + case SpvQuantizationModesRND_INF: return "RND_INF"; + case SpvQuantizationModesRND_MIN_INF: return "RND_MIN_INF"; + case SpvQuantizationModesRND_CONV: return "RND_CONV"; + case SpvQuantizationModesRND_CONV_ODD: return "RND_CONV_ODD"; + default: return "Unknown"; + } +} + +inline const char* SpvOverflowModesToString(SpvOverflowModes value) { + switch (value) { + case SpvOverflowModesWRAP: return "WRAP"; + case SpvOverflowModesSAT: return "SAT"; + case SpvOverflowModesSAT_ZERO: return "SAT_ZERO"; + case SpvOverflowModesSAT_SYM: return "SAT_SYM"; + default: return "Unknown"; + } +} + +inline const char* SpvPackedVectorFormatToString(SpvPackedVectorFormat value) { + switch (value) { + case SpvPackedVectorFormatPackedVectorFormat4x8Bit: return "PackedVectorFormat4x8Bit"; + default: return "Unknown"; + } +} + +inline const char* SpvCooperativeMatrixLayoutToString(SpvCooperativeMatrixLayout value) { + switch (value) { + case SpvCooperativeMatrixLayoutRowMajorKHR: return "RowMajorKHR"; + case SpvCooperativeMatrixLayoutColumnMajorKHR: return "ColumnMajorKHR"; + case SpvCooperativeMatrixLayoutRowBlockedInterleavedARM: return "RowBlockedInterleavedARM"; + case SpvCooperativeMatrixLayoutColumnBlockedInterleavedARM: return "ColumnBlockedInterleavedARM"; + default: return "Unknown"; + } +} + +inline const char* SpvCooperativeMatrixUseToString(SpvCooperativeMatrixUse value) { + switch (value) { + case SpvCooperativeMatrixUseMatrixAKHR: return "MatrixAKHR"; + case SpvCooperativeMatrixUseMatrixBKHR: return "MatrixBKHR"; + case SpvCooperativeMatrixUseMatrixAccumulatorKHR: return "MatrixAccumulatorKHR"; + default: return "Unknown"; + } +} + +inline const char* SpvTensorClampModeToString(SpvTensorClampMode value) { + switch (value) { + case SpvTensorClampModeUndefined: return "Undefined"; + case SpvTensorClampModeConstant: return "Constant"; + case SpvTensorClampModeClampToEdge: return "ClampToEdge"; + case SpvTensorClampModeRepeat: return "Repeat"; + case SpvTensorClampModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SpvInitializationModeQualifierToString(SpvInitializationModeQualifier value) { + switch (value) { + case SpvInitializationModeQualifierInitOnDeviceReprogramALTERA: return "InitOnDeviceReprogramALTERA"; + case SpvInitializationModeQualifierInitOnDeviceResetALTERA: return "InitOnDeviceResetALTERA"; + default: return "Unknown"; + } +} + +inline const char* SpvHostAccessQualifierToString(SpvHostAccessQualifier value) { + switch (value) { + case SpvHostAccessQualifierNoneINTEL: return "NoneINTEL"; + case SpvHostAccessQualifierReadINTEL: return "ReadINTEL"; + case SpvHostAccessQualifierWriteINTEL: return "WriteINTEL"; + case SpvHostAccessQualifierReadWriteINTEL: return "ReadWriteINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvLoadCacheControlToString(SpvLoadCacheControl value) { + switch (value) { + case SpvLoadCacheControlUncachedINTEL: return "UncachedINTEL"; + case SpvLoadCacheControlCachedINTEL: return "CachedINTEL"; + case SpvLoadCacheControlStreamingINTEL: return "StreamingINTEL"; + case SpvLoadCacheControlInvalidateAfterReadINTEL: return "InvalidateAfterReadINTEL"; + case SpvLoadCacheControlConstCachedINTEL: return "ConstCachedINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvStoreCacheControlToString(SpvStoreCacheControl value) { + switch (value) { + case SpvStoreCacheControlUncachedINTEL: return "UncachedINTEL"; + case SpvStoreCacheControlWriteThroughINTEL: return "WriteThroughINTEL"; + case SpvStoreCacheControlWriteBackINTEL: return "WriteBackINTEL"; + case SpvStoreCacheControlStreamingINTEL: return "StreamingINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvNamedMaximumNumberOfRegistersToString(SpvNamedMaximumNumberOfRegisters value) { + switch (value) { + case SpvNamedMaximumNumberOfRegistersAutoINTEL: return "AutoINTEL"; + default: return "Unknown"; + } +} + +inline const char* SpvFPEncodingToString(SpvFPEncoding value) { + switch (value) { + case SpvFPEncodingBFloat16KHR: return "BFloat16KHR"; + case SpvFPEncodingFloat8E4M3EXT: return "Float8E4M3EXT"; + case SpvFPEncodingFloat8E5M2EXT: return "Float8E5M2EXT"; + default: return "Unknown"; + } +} + +inline const char* SpvCooperativeVectorMatrixLayoutToString(SpvCooperativeVectorMatrixLayout value) { + switch (value) { + case SpvCooperativeVectorMatrixLayoutRowMajorNV: return "RowMajorNV"; + case SpvCooperativeVectorMatrixLayoutColumnMajorNV: return "ColumnMajorNV"; + case SpvCooperativeVectorMatrixLayoutInferencingOptimalNV: return "InferencingOptimalNV"; + case SpvCooperativeVectorMatrixLayoutTrainingOptimalNV: return "TrainingOptimalNV"; + default: return "Unknown"; + } +} + +inline const char* SpvComponentTypeToString(SpvComponentType value) { + switch (value) { + case SpvComponentTypeFloat16NV: return "Float16NV"; + case SpvComponentTypeFloat32NV: return "Float32NV"; + case SpvComponentTypeFloat64NV: return "Float64NV"; + case SpvComponentTypeSignedInt8NV: return "SignedInt8NV"; + case SpvComponentTypeSignedInt16NV: return "SignedInt16NV"; + case SpvComponentTypeSignedInt32NV: return "SignedInt32NV"; + case SpvComponentTypeSignedInt64NV: return "SignedInt64NV"; + case SpvComponentTypeUnsignedInt8NV: return "UnsignedInt8NV"; + case SpvComponentTypeUnsignedInt16NV: return "UnsignedInt16NV"; + case SpvComponentTypeUnsignedInt32NV: return "UnsignedInt32NV"; + case SpvComponentTypeUnsignedInt64NV: return "UnsignedInt64NV"; + case SpvComponentTypeSignedInt8PackedNV: return "SignedInt8PackedNV"; + case SpvComponentTypeUnsignedInt8PackedNV: return "UnsignedInt8PackedNV"; + case SpvComponentTypeFloatE4M3NV: return "FloatE4M3NV"; + case SpvComponentTypeFloatE5M2NV: return "FloatE5M2NV"; + default: return "Unknown"; + } +} + +inline const char* SpvOpToString(SpvOp value) { + switch (value) { + case SpvOpNop: return "OpNop"; + case SpvOpUndef: return "OpUndef"; + case SpvOpSourceContinued: return "OpSourceContinued"; + case SpvOpSource: return "OpSource"; + case SpvOpSourceExtension: return "OpSourceExtension"; + case SpvOpName: return "OpName"; + case SpvOpMemberName: return "OpMemberName"; + case SpvOpString: return "OpString"; + case SpvOpLine: return "OpLine"; + case SpvOpExtension: return "OpExtension"; + case SpvOpExtInstImport: return "OpExtInstImport"; + case SpvOpExtInst: return "OpExtInst"; + case SpvOpMemoryModel: return "OpMemoryModel"; + case SpvOpEntryPoint: return "OpEntryPoint"; + case SpvOpExecutionMode: return "OpExecutionMode"; + case SpvOpCapability: return "OpCapability"; + case SpvOpTypeVoid: return "OpTypeVoid"; + case SpvOpTypeBool: return "OpTypeBool"; + case SpvOpTypeInt: return "OpTypeInt"; + case SpvOpTypeFloat: return "OpTypeFloat"; + case SpvOpTypeVector: return "OpTypeVector"; + case SpvOpTypeMatrix: return "OpTypeMatrix"; + case SpvOpTypeImage: return "OpTypeImage"; + case SpvOpTypeSampler: return "OpTypeSampler"; + case SpvOpTypeSampledImage: return "OpTypeSampledImage"; + case SpvOpTypeArray: return "OpTypeArray"; + case SpvOpTypeRuntimeArray: return "OpTypeRuntimeArray"; + case SpvOpTypeStruct: return "OpTypeStruct"; + case SpvOpTypeOpaque: return "OpTypeOpaque"; + case SpvOpTypePointer: return "OpTypePointer"; + case SpvOpTypeFunction: return "OpTypeFunction"; + case SpvOpTypeEvent: return "OpTypeEvent"; + case SpvOpTypeDeviceEvent: return "OpTypeDeviceEvent"; + case SpvOpTypeReserveId: return "OpTypeReserveId"; + case SpvOpTypeQueue: return "OpTypeQueue"; + case SpvOpTypePipe: return "OpTypePipe"; + case SpvOpTypeForwardPointer: return "OpTypeForwardPointer"; + case SpvOpConstantTrue: return "OpConstantTrue"; + case SpvOpConstantFalse: return "OpConstantFalse"; + case SpvOpConstant: return "OpConstant"; + case SpvOpConstantComposite: return "OpConstantComposite"; + case SpvOpConstantSampler: return "OpConstantSampler"; + case SpvOpConstantNull: return "OpConstantNull"; + case SpvOpSpecConstantTrue: return "OpSpecConstantTrue"; + case SpvOpSpecConstantFalse: return "OpSpecConstantFalse"; + case SpvOpSpecConstant: return "OpSpecConstant"; + case SpvOpSpecConstantComposite: return "OpSpecConstantComposite"; + case SpvOpSpecConstantOp: return "OpSpecConstantOp"; + case SpvOpFunction: return "OpFunction"; + case SpvOpFunctionParameter: return "OpFunctionParameter"; + case SpvOpFunctionEnd: return "OpFunctionEnd"; + case SpvOpFunctionCall: return "OpFunctionCall"; + case SpvOpVariable: return "OpVariable"; + case SpvOpImageTexelPointer: return "OpImageTexelPointer"; + case SpvOpLoad: return "OpLoad"; + case SpvOpStore: return "OpStore"; + case SpvOpCopyMemory: return "OpCopyMemory"; + case SpvOpCopyMemorySized: return "OpCopyMemorySized"; + case SpvOpAccessChain: return "OpAccessChain"; + case SpvOpInBoundsAccessChain: return "OpInBoundsAccessChain"; + case SpvOpPtrAccessChain: return "OpPtrAccessChain"; + case SpvOpArrayLength: return "OpArrayLength"; + case SpvOpGenericPtrMemSemantics: return "OpGenericPtrMemSemantics"; + case SpvOpInBoundsPtrAccessChain: return "OpInBoundsPtrAccessChain"; + case SpvOpDecorate: return "OpDecorate"; + case SpvOpMemberDecorate: return "OpMemberDecorate"; + case SpvOpDecorationGroup: return "OpDecorationGroup"; + case SpvOpGroupDecorate: return "OpGroupDecorate"; + case SpvOpGroupMemberDecorate: return "OpGroupMemberDecorate"; + case SpvOpVectorExtractDynamic: return "OpVectorExtractDynamic"; + case SpvOpVectorInsertDynamic: return "OpVectorInsertDynamic"; + case SpvOpVectorShuffle: return "OpVectorShuffle"; + case SpvOpCompositeConstruct: return "OpCompositeConstruct"; + case SpvOpCompositeExtract: return "OpCompositeExtract"; + case SpvOpCompositeInsert: return "OpCompositeInsert"; + case SpvOpCopyObject: return "OpCopyObject"; + case SpvOpTranspose: return "OpTranspose"; + case SpvOpSampledImage: return "OpSampledImage"; + case SpvOpImageSampleImplicitLod: return "OpImageSampleImplicitLod"; + case SpvOpImageSampleExplicitLod: return "OpImageSampleExplicitLod"; + case SpvOpImageSampleDrefImplicitLod: return "OpImageSampleDrefImplicitLod"; + case SpvOpImageSampleDrefExplicitLod: return "OpImageSampleDrefExplicitLod"; + case SpvOpImageSampleProjImplicitLod: return "OpImageSampleProjImplicitLod"; + case SpvOpImageSampleProjExplicitLod: return "OpImageSampleProjExplicitLod"; + case SpvOpImageSampleProjDrefImplicitLod: return "OpImageSampleProjDrefImplicitLod"; + case SpvOpImageSampleProjDrefExplicitLod: return "OpImageSampleProjDrefExplicitLod"; + case SpvOpImageFetch: return "OpImageFetch"; + case SpvOpImageGather: return "OpImageGather"; + case SpvOpImageDrefGather: return "OpImageDrefGather"; + case SpvOpImageRead: return "OpImageRead"; + case SpvOpImageWrite: return "OpImageWrite"; + case SpvOpImage: return "OpImage"; + case SpvOpImageQueryFormat: return "OpImageQueryFormat"; + case SpvOpImageQueryOrder: return "OpImageQueryOrder"; + case SpvOpImageQuerySizeLod: return "OpImageQuerySizeLod"; + case SpvOpImageQuerySize: return "OpImageQuerySize"; + case SpvOpImageQueryLod: return "OpImageQueryLod"; + case SpvOpImageQueryLevels: return "OpImageQueryLevels"; + case SpvOpImageQuerySamples: return "OpImageQuerySamples"; + case SpvOpConvertFToU: return "OpConvertFToU"; + case SpvOpConvertFToS: return "OpConvertFToS"; + case SpvOpConvertSToF: return "OpConvertSToF"; + case SpvOpConvertUToF: return "OpConvertUToF"; + case SpvOpUConvert: return "OpUConvert"; + case SpvOpSConvert: return "OpSConvert"; + case SpvOpFConvert: return "OpFConvert"; + case SpvOpQuantizeToF16: return "OpQuantizeToF16"; + case SpvOpConvertPtrToU: return "OpConvertPtrToU"; + case SpvOpSatConvertSToU: return "OpSatConvertSToU"; + case SpvOpSatConvertUToS: return "OpSatConvertUToS"; + case SpvOpConvertUToPtr: return "OpConvertUToPtr"; + case SpvOpPtrCastToGeneric: return "OpPtrCastToGeneric"; + case SpvOpGenericCastToPtr: return "OpGenericCastToPtr"; + case SpvOpGenericCastToPtrExplicit: return "OpGenericCastToPtrExplicit"; + case SpvOpBitcast: return "OpBitcast"; + case SpvOpSNegate: return "OpSNegate"; + case SpvOpFNegate: return "OpFNegate"; + case SpvOpIAdd: return "OpIAdd"; + case SpvOpFAdd: return "OpFAdd"; + case SpvOpISub: return "OpISub"; + case SpvOpFSub: return "OpFSub"; + case SpvOpIMul: return "OpIMul"; + case SpvOpFMul: return "OpFMul"; + case SpvOpUDiv: return "OpUDiv"; + case SpvOpSDiv: return "OpSDiv"; + case SpvOpFDiv: return "OpFDiv"; + case SpvOpUMod: return "OpUMod"; + case SpvOpSRem: return "OpSRem"; + case SpvOpSMod: return "OpSMod"; + case SpvOpFRem: return "OpFRem"; + case SpvOpFMod: return "OpFMod"; + case SpvOpVectorTimesScalar: return "OpVectorTimesScalar"; + case SpvOpMatrixTimesScalar: return "OpMatrixTimesScalar"; + case SpvOpVectorTimesMatrix: return "OpVectorTimesMatrix"; + case SpvOpMatrixTimesVector: return "OpMatrixTimesVector"; + case SpvOpMatrixTimesMatrix: return "OpMatrixTimesMatrix"; + case SpvOpOuterProduct: return "OpOuterProduct"; + case SpvOpDot: return "OpDot"; + case SpvOpIAddCarry: return "OpIAddCarry"; + case SpvOpISubBorrow: return "OpISubBorrow"; + case SpvOpUMulExtended: return "OpUMulExtended"; + case SpvOpSMulExtended: return "OpSMulExtended"; + case SpvOpAny: return "OpAny"; + case SpvOpAll: return "OpAll"; + case SpvOpIsNan: return "OpIsNan"; + case SpvOpIsInf: return "OpIsInf"; + case SpvOpIsFinite: return "OpIsFinite"; + case SpvOpIsNormal: return "OpIsNormal"; + case SpvOpSignBitSet: return "OpSignBitSet"; + case SpvOpLessOrGreater: return "OpLessOrGreater"; + case SpvOpOrdered: return "OpOrdered"; + case SpvOpUnordered: return "OpUnordered"; + case SpvOpLogicalEqual: return "OpLogicalEqual"; + case SpvOpLogicalNotEqual: return "OpLogicalNotEqual"; + case SpvOpLogicalOr: return "OpLogicalOr"; + case SpvOpLogicalAnd: return "OpLogicalAnd"; + case SpvOpLogicalNot: return "OpLogicalNot"; + case SpvOpSelect: return "OpSelect"; + case SpvOpIEqual: return "OpIEqual"; + case SpvOpINotEqual: return "OpINotEqual"; + case SpvOpUGreaterThan: return "OpUGreaterThan"; + case SpvOpSGreaterThan: return "OpSGreaterThan"; + case SpvOpUGreaterThanEqual: return "OpUGreaterThanEqual"; + case SpvOpSGreaterThanEqual: return "OpSGreaterThanEqual"; + case SpvOpULessThan: return "OpULessThan"; + case SpvOpSLessThan: return "OpSLessThan"; + case SpvOpULessThanEqual: return "OpULessThanEqual"; + case SpvOpSLessThanEqual: return "OpSLessThanEqual"; + case SpvOpFOrdEqual: return "OpFOrdEqual"; + case SpvOpFUnordEqual: return "OpFUnordEqual"; + case SpvOpFOrdNotEqual: return "OpFOrdNotEqual"; + case SpvOpFUnordNotEqual: return "OpFUnordNotEqual"; + case SpvOpFOrdLessThan: return "OpFOrdLessThan"; + case SpvOpFUnordLessThan: return "OpFUnordLessThan"; + case SpvOpFOrdGreaterThan: return "OpFOrdGreaterThan"; + case SpvOpFUnordGreaterThan: return "OpFUnordGreaterThan"; + case SpvOpFOrdLessThanEqual: return "OpFOrdLessThanEqual"; + case SpvOpFUnordLessThanEqual: return "OpFUnordLessThanEqual"; + case SpvOpFOrdGreaterThanEqual: return "OpFOrdGreaterThanEqual"; + case SpvOpFUnordGreaterThanEqual: return "OpFUnordGreaterThanEqual"; + case SpvOpShiftRightLogical: return "OpShiftRightLogical"; + case SpvOpShiftRightArithmetic: return "OpShiftRightArithmetic"; + case SpvOpShiftLeftLogical: return "OpShiftLeftLogical"; + case SpvOpBitwiseOr: return "OpBitwiseOr"; + case SpvOpBitwiseXor: return "OpBitwiseXor"; + case SpvOpBitwiseAnd: return "OpBitwiseAnd"; + case SpvOpNot: return "OpNot"; + case SpvOpBitFieldInsert: return "OpBitFieldInsert"; + case SpvOpBitFieldSExtract: return "OpBitFieldSExtract"; + case SpvOpBitFieldUExtract: return "OpBitFieldUExtract"; + case SpvOpBitReverse: return "OpBitReverse"; + case SpvOpBitCount: return "OpBitCount"; + case SpvOpDPdx: return "OpDPdx"; + case SpvOpDPdy: return "OpDPdy"; + case SpvOpFwidth: return "OpFwidth"; + case SpvOpDPdxFine: return "OpDPdxFine"; + case SpvOpDPdyFine: return "OpDPdyFine"; + case SpvOpFwidthFine: return "OpFwidthFine"; + case SpvOpDPdxCoarse: return "OpDPdxCoarse"; + case SpvOpDPdyCoarse: return "OpDPdyCoarse"; + case SpvOpFwidthCoarse: return "OpFwidthCoarse"; + case SpvOpEmitVertex: return "OpEmitVertex"; + case SpvOpEndPrimitive: return "OpEndPrimitive"; + case SpvOpEmitStreamVertex: return "OpEmitStreamVertex"; + case SpvOpEndStreamPrimitive: return "OpEndStreamPrimitive"; + case SpvOpControlBarrier: return "OpControlBarrier"; + case SpvOpMemoryBarrier: return "OpMemoryBarrier"; + case SpvOpAtomicLoad: return "OpAtomicLoad"; + case SpvOpAtomicStore: return "OpAtomicStore"; + case SpvOpAtomicExchange: return "OpAtomicExchange"; + case SpvOpAtomicCompareExchange: return "OpAtomicCompareExchange"; + case SpvOpAtomicCompareExchangeWeak: return "OpAtomicCompareExchangeWeak"; + case SpvOpAtomicIIncrement: return "OpAtomicIIncrement"; + case SpvOpAtomicIDecrement: return "OpAtomicIDecrement"; + case SpvOpAtomicIAdd: return "OpAtomicIAdd"; + case SpvOpAtomicISub: return "OpAtomicISub"; + case SpvOpAtomicSMin: return "OpAtomicSMin"; + case SpvOpAtomicUMin: return "OpAtomicUMin"; + case SpvOpAtomicSMax: return "OpAtomicSMax"; + case SpvOpAtomicUMax: return "OpAtomicUMax"; + case SpvOpAtomicAnd: return "OpAtomicAnd"; + case SpvOpAtomicOr: return "OpAtomicOr"; + case SpvOpAtomicXor: return "OpAtomicXor"; + case SpvOpPhi: return "OpPhi"; + case SpvOpLoopMerge: return "OpLoopMerge"; + case SpvOpSelectionMerge: return "OpSelectionMerge"; + case SpvOpLabel: return "OpLabel"; + case SpvOpBranch: return "OpBranch"; + case SpvOpBranchConditional: return "OpBranchConditional"; + case SpvOpSwitch: return "OpSwitch"; + case SpvOpKill: return "OpKill"; + case SpvOpReturn: return "OpReturn"; + case SpvOpReturnValue: return "OpReturnValue"; + case SpvOpUnreachable: return "OpUnreachable"; + case SpvOpLifetimeStart: return "OpLifetimeStart"; + case SpvOpLifetimeStop: return "OpLifetimeStop"; + case SpvOpGroupAsyncCopy: return "OpGroupAsyncCopy"; + case SpvOpGroupWaitEvents: return "OpGroupWaitEvents"; + case SpvOpGroupAll: return "OpGroupAll"; + case SpvOpGroupAny: return "OpGroupAny"; + case SpvOpGroupBroadcast: return "OpGroupBroadcast"; + case SpvOpGroupIAdd: return "OpGroupIAdd"; + case SpvOpGroupFAdd: return "OpGroupFAdd"; + case SpvOpGroupFMin: return "OpGroupFMin"; + case SpvOpGroupUMin: return "OpGroupUMin"; + case SpvOpGroupSMin: return "OpGroupSMin"; + case SpvOpGroupFMax: return "OpGroupFMax"; + case SpvOpGroupUMax: return "OpGroupUMax"; + case SpvOpGroupSMax: return "OpGroupSMax"; + case SpvOpReadPipe: return "OpReadPipe"; + case SpvOpWritePipe: return "OpWritePipe"; + case SpvOpReservedReadPipe: return "OpReservedReadPipe"; + case SpvOpReservedWritePipe: return "OpReservedWritePipe"; + case SpvOpReserveReadPipePackets: return "OpReserveReadPipePackets"; + case SpvOpReserveWritePipePackets: return "OpReserveWritePipePackets"; + case SpvOpCommitReadPipe: return "OpCommitReadPipe"; + case SpvOpCommitWritePipe: return "OpCommitWritePipe"; + case SpvOpIsValidReserveId: return "OpIsValidReserveId"; + case SpvOpGetNumPipePackets: return "OpGetNumPipePackets"; + case SpvOpGetMaxPipePackets: return "OpGetMaxPipePackets"; + case SpvOpGroupReserveReadPipePackets: return "OpGroupReserveReadPipePackets"; + case SpvOpGroupReserveWritePipePackets: return "OpGroupReserveWritePipePackets"; + case SpvOpGroupCommitReadPipe: return "OpGroupCommitReadPipe"; + case SpvOpGroupCommitWritePipe: return "OpGroupCommitWritePipe"; + case SpvOpEnqueueMarker: return "OpEnqueueMarker"; + case SpvOpEnqueueKernel: return "OpEnqueueKernel"; + case SpvOpGetKernelNDrangeSubGroupCount: return "OpGetKernelNDrangeSubGroupCount"; + case SpvOpGetKernelNDrangeMaxSubGroupSize: return "OpGetKernelNDrangeMaxSubGroupSize"; + case SpvOpGetKernelWorkGroupSize: return "OpGetKernelWorkGroupSize"; + case SpvOpGetKernelPreferredWorkGroupSizeMultiple: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case SpvOpRetainEvent: return "OpRetainEvent"; + case SpvOpReleaseEvent: return "OpReleaseEvent"; + case SpvOpCreateUserEvent: return "OpCreateUserEvent"; + case SpvOpIsValidEvent: return "OpIsValidEvent"; + case SpvOpSetUserEventStatus: return "OpSetUserEventStatus"; + case SpvOpCaptureEventProfilingInfo: return "OpCaptureEventProfilingInfo"; + case SpvOpGetDefaultQueue: return "OpGetDefaultQueue"; + case SpvOpBuildNDRange: return "OpBuildNDRange"; + case SpvOpImageSparseSampleImplicitLod: return "OpImageSparseSampleImplicitLod"; + case SpvOpImageSparseSampleExplicitLod: return "OpImageSparseSampleExplicitLod"; + case SpvOpImageSparseSampleDrefImplicitLod: return "OpImageSparseSampleDrefImplicitLod"; + case SpvOpImageSparseSampleDrefExplicitLod: return "OpImageSparseSampleDrefExplicitLod"; + case SpvOpImageSparseSampleProjImplicitLod: return "OpImageSparseSampleProjImplicitLod"; + case SpvOpImageSparseSampleProjExplicitLod: return "OpImageSparseSampleProjExplicitLod"; + case SpvOpImageSparseSampleProjDrefImplicitLod: return "OpImageSparseSampleProjDrefImplicitLod"; + case SpvOpImageSparseSampleProjDrefExplicitLod: return "OpImageSparseSampleProjDrefExplicitLod"; + case SpvOpImageSparseFetch: return "OpImageSparseFetch"; + case SpvOpImageSparseGather: return "OpImageSparseGather"; + case SpvOpImageSparseDrefGather: return "OpImageSparseDrefGather"; + case SpvOpImageSparseTexelsResident: return "OpImageSparseTexelsResident"; + case SpvOpNoLine: return "OpNoLine"; + case SpvOpAtomicFlagTestAndSet: return "OpAtomicFlagTestAndSet"; + case SpvOpAtomicFlagClear: return "OpAtomicFlagClear"; + case SpvOpImageSparseRead: return "OpImageSparseRead"; + case SpvOpSizeOf: return "OpSizeOf"; + case SpvOpTypePipeStorage: return "OpTypePipeStorage"; + case SpvOpConstantPipeStorage: return "OpConstantPipeStorage"; + case SpvOpCreatePipeFromPipeStorage: return "OpCreatePipeFromPipeStorage"; + case SpvOpGetKernelLocalSizeForSubgroupCount: return "OpGetKernelLocalSizeForSubgroupCount"; + case SpvOpGetKernelMaxNumSubgroups: return "OpGetKernelMaxNumSubgroups"; + case SpvOpTypeNamedBarrier: return "OpTypeNamedBarrier"; + case SpvOpNamedBarrierInitialize: return "OpNamedBarrierInitialize"; + case SpvOpMemoryNamedBarrier: return "OpMemoryNamedBarrier"; + case SpvOpModuleProcessed: return "OpModuleProcessed"; + case SpvOpExecutionModeId: return "OpExecutionModeId"; + case SpvOpDecorateId: return "OpDecorateId"; + case SpvOpGroupNonUniformElect: return "OpGroupNonUniformElect"; + case SpvOpGroupNonUniformAll: return "OpGroupNonUniformAll"; + case SpvOpGroupNonUniformAny: return "OpGroupNonUniformAny"; + case SpvOpGroupNonUniformAllEqual: return "OpGroupNonUniformAllEqual"; + case SpvOpGroupNonUniformBroadcast: return "OpGroupNonUniformBroadcast"; + case SpvOpGroupNonUniformBroadcastFirst: return "OpGroupNonUniformBroadcastFirst"; + case SpvOpGroupNonUniformBallot: return "OpGroupNonUniformBallot"; + case SpvOpGroupNonUniformInverseBallot: return "OpGroupNonUniformInverseBallot"; + case SpvOpGroupNonUniformBallotBitExtract: return "OpGroupNonUniformBallotBitExtract"; + case SpvOpGroupNonUniformBallotBitCount: return "OpGroupNonUniformBallotBitCount"; + case SpvOpGroupNonUniformBallotFindLSB: return "OpGroupNonUniformBallotFindLSB"; + case SpvOpGroupNonUniformBallotFindMSB: return "OpGroupNonUniformBallotFindMSB"; + case SpvOpGroupNonUniformShuffle: return "OpGroupNonUniformShuffle"; + case SpvOpGroupNonUniformShuffleXor: return "OpGroupNonUniformShuffleXor"; + case SpvOpGroupNonUniformShuffleUp: return "OpGroupNonUniformShuffleUp"; + case SpvOpGroupNonUniformShuffleDown: return "OpGroupNonUniformShuffleDown"; + case SpvOpGroupNonUniformIAdd: return "OpGroupNonUniformIAdd"; + case SpvOpGroupNonUniformFAdd: return "OpGroupNonUniformFAdd"; + case SpvOpGroupNonUniformIMul: return "OpGroupNonUniformIMul"; + case SpvOpGroupNonUniformFMul: return "OpGroupNonUniformFMul"; + case SpvOpGroupNonUniformSMin: return "OpGroupNonUniformSMin"; + case SpvOpGroupNonUniformUMin: return "OpGroupNonUniformUMin"; + case SpvOpGroupNonUniformFMin: return "OpGroupNonUniformFMin"; + case SpvOpGroupNonUniformSMax: return "OpGroupNonUniformSMax"; + case SpvOpGroupNonUniformUMax: return "OpGroupNonUniformUMax"; + case SpvOpGroupNonUniformFMax: return "OpGroupNonUniformFMax"; + case SpvOpGroupNonUniformBitwiseAnd: return "OpGroupNonUniformBitwiseAnd"; + case SpvOpGroupNonUniformBitwiseOr: return "OpGroupNonUniformBitwiseOr"; + case SpvOpGroupNonUniformBitwiseXor: return "OpGroupNonUniformBitwiseXor"; + case SpvOpGroupNonUniformLogicalAnd: return "OpGroupNonUniformLogicalAnd"; + case SpvOpGroupNonUniformLogicalOr: return "OpGroupNonUniformLogicalOr"; + case SpvOpGroupNonUniformLogicalXor: return "OpGroupNonUniformLogicalXor"; + case SpvOpGroupNonUniformQuadBroadcast: return "OpGroupNonUniformQuadBroadcast"; + case SpvOpGroupNonUniformQuadSwap: return "OpGroupNonUniformQuadSwap"; + case SpvOpCopyLogical: return "OpCopyLogical"; + case SpvOpPtrEqual: return "OpPtrEqual"; + case SpvOpPtrNotEqual: return "OpPtrNotEqual"; + case SpvOpPtrDiff: return "OpPtrDiff"; + case SpvOpColorAttachmentReadEXT: return "OpColorAttachmentReadEXT"; + case SpvOpDepthAttachmentReadEXT: return "OpDepthAttachmentReadEXT"; + case SpvOpStencilAttachmentReadEXT: return "OpStencilAttachmentReadEXT"; + case SpvOpTypeTensorARM: return "OpTypeTensorARM"; + case SpvOpTensorReadARM: return "OpTensorReadARM"; + case SpvOpTensorWriteARM: return "OpTensorWriteARM"; + case SpvOpTensorQuerySizeARM: return "OpTensorQuerySizeARM"; + case SpvOpGraphConstantARM: return "OpGraphConstantARM"; + case SpvOpGraphEntryPointARM: return "OpGraphEntryPointARM"; + case SpvOpGraphARM: return "OpGraphARM"; + case SpvOpGraphInputARM: return "OpGraphInputARM"; + case SpvOpGraphSetOutputARM: return "OpGraphSetOutputARM"; + case SpvOpGraphEndARM: return "OpGraphEndARM"; + case SpvOpTypeGraphARM: return "OpTypeGraphARM"; + case SpvOpTerminateInvocation: return "OpTerminateInvocation"; + case SpvOpTypeUntypedPointerKHR: return "OpTypeUntypedPointerKHR"; + case SpvOpUntypedVariableKHR: return "OpUntypedVariableKHR"; + case SpvOpUntypedAccessChainKHR: return "OpUntypedAccessChainKHR"; + case SpvOpUntypedInBoundsAccessChainKHR: return "OpUntypedInBoundsAccessChainKHR"; + case SpvOpSubgroupBallotKHR: return "OpSubgroupBallotKHR"; + case SpvOpSubgroupFirstInvocationKHR: return "OpSubgroupFirstInvocationKHR"; + case SpvOpUntypedPtrAccessChainKHR: return "OpUntypedPtrAccessChainKHR"; + case SpvOpUntypedInBoundsPtrAccessChainKHR: return "OpUntypedInBoundsPtrAccessChainKHR"; + case SpvOpUntypedArrayLengthKHR: return "OpUntypedArrayLengthKHR"; + case SpvOpUntypedPrefetchKHR: return "OpUntypedPrefetchKHR"; + case SpvOpFmaKHR: return "OpFmaKHR"; + case SpvOpSubgroupAllKHR: return "OpSubgroupAllKHR"; + case SpvOpSubgroupAnyKHR: return "OpSubgroupAnyKHR"; + case SpvOpSubgroupAllEqualKHR: return "OpSubgroupAllEqualKHR"; + case SpvOpGroupNonUniformRotateKHR: return "OpGroupNonUniformRotateKHR"; + case SpvOpSubgroupReadInvocationKHR: return "OpSubgroupReadInvocationKHR"; + case SpvOpExtInstWithForwardRefsKHR: return "OpExtInstWithForwardRefsKHR"; + case SpvOpUntypedGroupAsyncCopyKHR: return "OpUntypedGroupAsyncCopyKHR"; + case SpvOpTraceRayKHR: return "OpTraceRayKHR"; + case SpvOpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case SpvOpConvertUToAccelerationStructureKHR: return "OpConvertUToAccelerationStructureKHR"; + case SpvOpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case SpvOpTerminateRayKHR: return "OpTerminateRayKHR"; + case SpvOpSDot: return "OpSDot"; + case SpvOpUDot: return "OpUDot"; + case SpvOpSUDot: return "OpSUDot"; + case SpvOpSDotAccSat: return "OpSDotAccSat"; + case SpvOpUDotAccSat: return "OpUDotAccSat"; + case SpvOpSUDotAccSat: return "OpSUDotAccSat"; + case SpvOpTypeCooperativeMatrixKHR: return "OpTypeCooperativeMatrixKHR"; + case SpvOpCooperativeMatrixLoadKHR: return "OpCooperativeMatrixLoadKHR"; + case SpvOpCooperativeMatrixStoreKHR: return "OpCooperativeMatrixStoreKHR"; + case SpvOpCooperativeMatrixMulAddKHR: return "OpCooperativeMatrixMulAddKHR"; + case SpvOpCooperativeMatrixLengthKHR: return "OpCooperativeMatrixLengthKHR"; + case SpvOpConstantCompositeReplicateEXT: return "OpConstantCompositeReplicateEXT"; + case SpvOpSpecConstantCompositeReplicateEXT: return "OpSpecConstantCompositeReplicateEXT"; + case SpvOpCompositeConstructReplicateEXT: return "OpCompositeConstructReplicateEXT"; + case SpvOpTypeRayQueryKHR: return "OpTypeRayQueryKHR"; + case SpvOpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case SpvOpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case SpvOpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case SpvOpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case SpvOpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case SpvOpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case SpvOpImageSampleWeightedQCOM: return "OpImageSampleWeightedQCOM"; + case SpvOpImageBoxFilterQCOM: return "OpImageBoxFilterQCOM"; + case SpvOpImageBlockMatchSSDQCOM: return "OpImageBlockMatchSSDQCOM"; + case SpvOpImageBlockMatchSADQCOM: return "OpImageBlockMatchSADQCOM"; + case SpvOpBitCastArrayQCOM: return "OpBitCastArrayQCOM"; + case SpvOpImageBlockMatchWindowSSDQCOM: return "OpImageBlockMatchWindowSSDQCOM"; + case SpvOpImageBlockMatchWindowSADQCOM: return "OpImageBlockMatchWindowSADQCOM"; + case SpvOpImageBlockMatchGatherSSDQCOM: return "OpImageBlockMatchGatherSSDQCOM"; + case SpvOpImageBlockMatchGatherSADQCOM: return "OpImageBlockMatchGatherSADQCOM"; + case SpvOpCompositeConstructCoopMatQCOM: return "OpCompositeConstructCoopMatQCOM"; + case SpvOpCompositeExtractCoopMatQCOM: return "OpCompositeExtractCoopMatQCOM"; + case SpvOpExtractSubArrayQCOM: return "OpExtractSubArrayQCOM"; + case SpvOpGroupIAddNonUniformAMD: return "OpGroupIAddNonUniformAMD"; + case SpvOpGroupFAddNonUniformAMD: return "OpGroupFAddNonUniformAMD"; + case SpvOpGroupFMinNonUniformAMD: return "OpGroupFMinNonUniformAMD"; + case SpvOpGroupUMinNonUniformAMD: return "OpGroupUMinNonUniformAMD"; + case SpvOpGroupSMinNonUniformAMD: return "OpGroupSMinNonUniformAMD"; + case SpvOpGroupFMaxNonUniformAMD: return "OpGroupFMaxNonUniformAMD"; + case SpvOpGroupUMaxNonUniformAMD: return "OpGroupUMaxNonUniformAMD"; + case SpvOpGroupSMaxNonUniformAMD: return "OpGroupSMaxNonUniformAMD"; + case SpvOpFragmentMaskFetchAMD: return "OpFragmentMaskFetchAMD"; + case SpvOpFragmentFetchAMD: return "OpFragmentFetchAMD"; + case SpvOpReadClockKHR: return "OpReadClockKHR"; + case SpvOpAllocateNodePayloadsAMDX: return "OpAllocateNodePayloadsAMDX"; + case SpvOpEnqueueNodePayloadsAMDX: return "OpEnqueueNodePayloadsAMDX"; + case SpvOpTypeNodePayloadArrayAMDX: return "OpTypeNodePayloadArrayAMDX"; + case SpvOpFinishWritingNodePayloadAMDX: return "OpFinishWritingNodePayloadAMDX"; + case SpvOpNodePayloadArrayLengthAMDX: return "OpNodePayloadArrayLengthAMDX"; + case SpvOpIsNodePayloadValidAMDX: return "OpIsNodePayloadValidAMDX"; + case SpvOpConstantStringAMDX: return "OpConstantStringAMDX"; + case SpvOpSpecConstantStringAMDX: return "OpSpecConstantStringAMDX"; + case SpvOpGroupNonUniformQuadAllKHR: return "OpGroupNonUniformQuadAllKHR"; + case SpvOpGroupNonUniformQuadAnyKHR: return "OpGroupNonUniformQuadAnyKHR"; + case SpvOpTypeBufferEXT: return "OpTypeBufferEXT"; + case SpvOpBufferPointerEXT: return "OpBufferPointerEXT"; + case SpvOpAbortKHR: return "OpAbortKHR"; + case SpvOpUntypedImageTexelPointerEXT: return "OpUntypedImageTexelPointerEXT"; + case SpvOpMemberDecorateIdEXT: return "OpMemberDecorateIdEXT"; + case SpvOpConstantSizeOfEXT: return "OpConstantSizeOfEXT"; + case SpvOpConstantDataKHR: return "OpConstantDataKHR"; + case SpvOpSpecConstantDataKHR: return "OpSpecConstantDataKHR"; + case SpvOpPoisonKHR: return "OpPoisonKHR"; + case SpvOpFreezeKHR: return "OpFreezeKHR"; + case SpvOpHitObjectRecordHitMotionNV: return "OpHitObjectRecordHitMotionNV"; + case SpvOpHitObjectRecordHitWithIndexMotionNV: return "OpHitObjectRecordHitWithIndexMotionNV"; + case SpvOpHitObjectRecordMissMotionNV: return "OpHitObjectRecordMissMotionNV"; + case SpvOpHitObjectGetWorldToObjectNV: return "OpHitObjectGetWorldToObjectNV"; + case SpvOpHitObjectGetObjectToWorldNV: return "OpHitObjectGetObjectToWorldNV"; + case SpvOpHitObjectGetObjectRayDirectionNV: return "OpHitObjectGetObjectRayDirectionNV"; + case SpvOpHitObjectGetObjectRayOriginNV: return "OpHitObjectGetObjectRayOriginNV"; + case SpvOpHitObjectTraceRayMotionNV: return "OpHitObjectTraceRayMotionNV"; + case SpvOpHitObjectGetShaderRecordBufferHandleNV: return "OpHitObjectGetShaderRecordBufferHandleNV"; + case SpvOpHitObjectGetShaderBindingTableRecordIndexNV: return "OpHitObjectGetShaderBindingTableRecordIndexNV"; + case SpvOpHitObjectRecordEmptyNV: return "OpHitObjectRecordEmptyNV"; + case SpvOpHitObjectTraceRayNV: return "OpHitObjectTraceRayNV"; + case SpvOpHitObjectRecordHitNV: return "OpHitObjectRecordHitNV"; + case SpvOpHitObjectRecordHitWithIndexNV: return "OpHitObjectRecordHitWithIndexNV"; + case SpvOpHitObjectRecordMissNV: return "OpHitObjectRecordMissNV"; + case SpvOpHitObjectExecuteShaderNV: return "OpHitObjectExecuteShaderNV"; + case SpvOpHitObjectGetCurrentTimeNV: return "OpHitObjectGetCurrentTimeNV"; + case SpvOpHitObjectGetAttributesNV: return "OpHitObjectGetAttributesNV"; + case SpvOpHitObjectGetHitKindNV: return "OpHitObjectGetHitKindNV"; + case SpvOpHitObjectGetPrimitiveIndexNV: return "OpHitObjectGetPrimitiveIndexNV"; + case SpvOpHitObjectGetGeometryIndexNV: return "OpHitObjectGetGeometryIndexNV"; + case SpvOpHitObjectGetInstanceIdNV: return "OpHitObjectGetInstanceIdNV"; + case SpvOpHitObjectGetInstanceCustomIndexNV: return "OpHitObjectGetInstanceCustomIndexNV"; + case SpvOpHitObjectGetWorldRayDirectionNV: return "OpHitObjectGetWorldRayDirectionNV"; + case SpvOpHitObjectGetWorldRayOriginNV: return "OpHitObjectGetWorldRayOriginNV"; + case SpvOpHitObjectGetRayTMaxNV: return "OpHitObjectGetRayTMaxNV"; + case SpvOpHitObjectGetRayTMinNV: return "OpHitObjectGetRayTMinNV"; + case SpvOpHitObjectIsEmptyNV: return "OpHitObjectIsEmptyNV"; + case SpvOpHitObjectIsHitNV: return "OpHitObjectIsHitNV"; + case SpvOpHitObjectIsMissNV: return "OpHitObjectIsMissNV"; + case SpvOpReorderThreadWithHitObjectNV: return "OpReorderThreadWithHitObjectNV"; + case SpvOpReorderThreadWithHintNV: return "OpReorderThreadWithHintNV"; + case SpvOpTypeHitObjectNV: return "OpTypeHitObjectNV"; + case SpvOpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case SpvOpTypeCooperativeVectorNV: return "OpTypeCooperativeVectorNV"; + case SpvOpCooperativeVectorMatrixMulNV: return "OpCooperativeVectorMatrixMulNV"; + case SpvOpCooperativeVectorOuterProductAccumulateNV: return "OpCooperativeVectorOuterProductAccumulateNV"; + case SpvOpCooperativeVectorReduceSumAccumulateNV: return "OpCooperativeVectorReduceSumAccumulateNV"; + case SpvOpCooperativeVectorMatrixMulAddNV: return "OpCooperativeVectorMatrixMulAddNV"; + case SpvOpCooperativeMatrixConvertNV: return "OpCooperativeMatrixConvertNV"; + case SpvOpEmitMeshTasksEXT: return "OpEmitMeshTasksEXT"; + case SpvOpSetMeshOutputsEXT: return "OpSetMeshOutputsEXT"; + case SpvOpGroupNonUniformPartitionEXT: return "OpGroupNonUniformPartitionEXT"; + case SpvOpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + case SpvOpFetchMicroTriangleVertexPositionNV: return "OpFetchMicroTriangleVertexPositionNV"; + case SpvOpFetchMicroTriangleVertexBarycentricNV: return "OpFetchMicroTriangleVertexBarycentricNV"; + case SpvOpCooperativeVectorLoadNV: return "OpCooperativeVectorLoadNV"; + case SpvOpCooperativeVectorStoreNV: return "OpCooperativeVectorStoreNV"; + case SpvOpHitObjectRecordFromQueryEXT: return "OpHitObjectRecordFromQueryEXT"; + case SpvOpHitObjectRecordMissEXT: return "OpHitObjectRecordMissEXT"; + case SpvOpHitObjectRecordMissMotionEXT: return "OpHitObjectRecordMissMotionEXT"; + case SpvOpHitObjectGetIntersectionTriangleVertexPositionsEXT: return "OpHitObjectGetIntersectionTriangleVertexPositionsEXT"; + case SpvOpHitObjectGetRayFlagsEXT: return "OpHitObjectGetRayFlagsEXT"; + case SpvOpHitObjectSetShaderBindingTableRecordIndexEXT: return "OpHitObjectSetShaderBindingTableRecordIndexEXT"; + case SpvOpHitObjectReorderExecuteShaderEXT: return "OpHitObjectReorderExecuteShaderEXT"; + case SpvOpHitObjectTraceReorderExecuteEXT: return "OpHitObjectTraceReorderExecuteEXT"; + case SpvOpHitObjectTraceMotionReorderExecuteEXT: return "OpHitObjectTraceMotionReorderExecuteEXT"; + case SpvOpTypeHitObjectEXT: return "OpTypeHitObjectEXT"; + case SpvOpReorderThreadWithHintEXT: return "OpReorderThreadWithHintEXT"; + case SpvOpReorderThreadWithHitObjectEXT: return "OpReorderThreadWithHitObjectEXT"; + case SpvOpHitObjectTraceRayEXT: return "OpHitObjectTraceRayEXT"; + case SpvOpHitObjectTraceRayMotionEXT: return "OpHitObjectTraceRayMotionEXT"; + case SpvOpHitObjectRecordEmptyEXT: return "OpHitObjectRecordEmptyEXT"; + case SpvOpHitObjectExecuteShaderEXT: return "OpHitObjectExecuteShaderEXT"; + case SpvOpHitObjectGetCurrentTimeEXT: return "OpHitObjectGetCurrentTimeEXT"; + case SpvOpHitObjectGetAttributesEXT: return "OpHitObjectGetAttributesEXT"; + case SpvOpHitObjectGetHitKindEXT: return "OpHitObjectGetHitKindEXT"; + case SpvOpHitObjectGetPrimitiveIndexEXT: return "OpHitObjectGetPrimitiveIndexEXT"; + case SpvOpHitObjectGetGeometryIndexEXT: return "OpHitObjectGetGeometryIndexEXT"; + case SpvOpHitObjectGetInstanceIdEXT: return "OpHitObjectGetInstanceIdEXT"; + case SpvOpHitObjectGetInstanceCustomIndexEXT: return "OpHitObjectGetInstanceCustomIndexEXT"; + case SpvOpHitObjectGetObjectRayOriginEXT: return "OpHitObjectGetObjectRayOriginEXT"; + case SpvOpHitObjectGetObjectRayDirectionEXT: return "OpHitObjectGetObjectRayDirectionEXT"; + case SpvOpHitObjectGetWorldRayDirectionEXT: return "OpHitObjectGetWorldRayDirectionEXT"; + case SpvOpHitObjectGetWorldRayOriginEXT: return "OpHitObjectGetWorldRayOriginEXT"; + case SpvOpHitObjectGetObjectToWorldEXT: return "OpHitObjectGetObjectToWorldEXT"; + case SpvOpHitObjectGetWorldToObjectEXT: return "OpHitObjectGetWorldToObjectEXT"; + case SpvOpHitObjectGetRayTMaxEXT: return "OpHitObjectGetRayTMaxEXT"; + case SpvOpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case SpvOpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV"; + case SpvOpTerminateRayNV: return "OpTerminateRayNV"; + case SpvOpTraceNV: return "OpTraceNV"; + case SpvOpTraceMotionNV: return "OpTraceMotionNV"; + case SpvOpTraceRayMotionNV: return "OpTraceRayMotionNV"; + case SpvOpRayQueryGetIntersectionTriangleVertexPositionsKHR: return "OpRayQueryGetIntersectionTriangleVertexPositionsKHR"; + case SpvOpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case SpvOpExecuteCallableNV: return "OpExecuteCallableNV"; + case SpvOpRayQueryGetClusterIdNV: return "OpRayQueryGetClusterIdNV"; + case SpvOpHitObjectGetClusterIdNV: return "OpHitObjectGetClusterIdNV"; + case SpvOpHitObjectGetRayTMinEXT: return "OpHitObjectGetRayTMinEXT"; + case SpvOpHitObjectGetShaderBindingTableRecordIndexEXT: return "OpHitObjectGetShaderBindingTableRecordIndexEXT"; + case SpvOpHitObjectGetShaderRecordBufferHandleEXT: return "OpHitObjectGetShaderRecordBufferHandleEXT"; + case SpvOpHitObjectIsEmptyEXT: return "OpHitObjectIsEmptyEXT"; + case SpvOpHitObjectIsHitEXT: return "OpHitObjectIsHitEXT"; + case SpvOpHitObjectIsMissEXT: return "OpHitObjectIsMissEXT"; + case SpvOpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case SpvOpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case SpvOpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case SpvOpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case SpvOpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case SpvOpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case SpvOpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + case SpvOpCooperativeMatrixReduceNV: return "OpCooperativeMatrixReduceNV"; + case SpvOpCooperativeMatrixLoadTensorNV: return "OpCooperativeMatrixLoadTensorNV"; + case SpvOpCooperativeMatrixStoreTensorNV: return "OpCooperativeMatrixStoreTensorNV"; + case SpvOpCooperativeMatrixPerElementOpNV: return "OpCooperativeMatrixPerElementOpNV"; + case SpvOpTypeTensorLayoutNV: return "OpTypeTensorLayoutNV"; + case SpvOpTypeTensorViewNV: return "OpTypeTensorViewNV"; + case SpvOpCreateTensorLayoutNV: return "OpCreateTensorLayoutNV"; + case SpvOpTensorLayoutSetDimensionNV: return "OpTensorLayoutSetDimensionNV"; + case SpvOpTensorLayoutSetStrideNV: return "OpTensorLayoutSetStrideNV"; + case SpvOpTensorLayoutSliceNV: return "OpTensorLayoutSliceNV"; + case SpvOpTensorLayoutSetClampValueNV: return "OpTensorLayoutSetClampValueNV"; + case SpvOpCreateTensorViewNV: return "OpCreateTensorViewNV"; + case SpvOpTensorViewSetDimensionNV: return "OpTensorViewSetDimensionNV"; + case SpvOpTensorViewSetStrideNV: return "OpTensorViewSetStrideNV"; + case SpvOpDemoteToHelperInvocation: return "OpDemoteToHelperInvocation"; + case SpvOpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + case SpvOpTensorViewSetClipNV: return "OpTensorViewSetClipNV"; + case SpvOpTensorLayoutSetBlockSizeNV: return "OpTensorLayoutSetBlockSizeNV"; + case SpvOpCooperativeMatrixTransposeNV: return "OpCooperativeMatrixTransposeNV"; + case SpvOpConvertUToImageNV: return "OpConvertUToImageNV"; + case SpvOpConvertUToSamplerNV: return "OpConvertUToSamplerNV"; + case SpvOpConvertImageToUNV: return "OpConvertImageToUNV"; + case SpvOpConvertSamplerToUNV: return "OpConvertSamplerToUNV"; + case SpvOpConvertUToSampledImageNV: return "OpConvertUToSampledImageNV"; + case SpvOpConvertSampledImageToUNV: return "OpConvertSampledImageToUNV"; + case SpvOpSamplerImageAddressingModeNV: return "OpSamplerImageAddressingModeNV"; + case SpvOpRawAccessChainNV: return "OpRawAccessChainNV"; + case SpvOpRayQueryGetIntersectionSpherePositionNV: return "OpRayQueryGetIntersectionSpherePositionNV"; + case SpvOpRayQueryGetIntersectionSphereRadiusNV: return "OpRayQueryGetIntersectionSphereRadiusNV"; + case SpvOpRayQueryGetIntersectionLSSPositionsNV: return "OpRayQueryGetIntersectionLSSPositionsNV"; + case SpvOpRayQueryGetIntersectionLSSRadiiNV: return "OpRayQueryGetIntersectionLSSRadiiNV"; + case SpvOpRayQueryGetIntersectionLSSHitValueNV: return "OpRayQueryGetIntersectionLSSHitValueNV"; + case SpvOpHitObjectGetSpherePositionNV: return "OpHitObjectGetSpherePositionNV"; + case SpvOpHitObjectGetSphereRadiusNV: return "OpHitObjectGetSphereRadiusNV"; + case SpvOpHitObjectGetLSSPositionsNV: return "OpHitObjectGetLSSPositionsNV"; + case SpvOpHitObjectGetLSSRadiiNV: return "OpHitObjectGetLSSRadiiNV"; + case SpvOpHitObjectIsSphereHitNV: return "OpHitObjectIsSphereHitNV"; + case SpvOpHitObjectIsLSSHitNV: return "OpHitObjectIsLSSHitNV"; + case SpvOpRayQueryIsSphereHitNV: return "OpRayQueryIsSphereHitNV"; + case SpvOpRayQueryIsLSSHitNV: return "OpRayQueryIsLSSHitNV"; + case SpvOpSubgroupShuffleINTEL: return "OpSubgroupShuffleINTEL"; + case SpvOpSubgroupShuffleDownINTEL: return "OpSubgroupShuffleDownINTEL"; + case SpvOpSubgroupShuffleUpINTEL: return "OpSubgroupShuffleUpINTEL"; + case SpvOpSubgroupShuffleXorINTEL: return "OpSubgroupShuffleXorINTEL"; + case SpvOpSubgroupBlockReadINTEL: return "OpSubgroupBlockReadINTEL"; + case SpvOpSubgroupBlockWriteINTEL: return "OpSubgroupBlockWriteINTEL"; + case SpvOpSubgroupImageBlockReadINTEL: return "OpSubgroupImageBlockReadINTEL"; + case SpvOpSubgroupImageBlockWriteINTEL: return "OpSubgroupImageBlockWriteINTEL"; + case SpvOpSubgroupImageMediaBlockReadINTEL: return "OpSubgroupImageMediaBlockReadINTEL"; + case SpvOpSubgroupImageMediaBlockWriteINTEL: return "OpSubgroupImageMediaBlockWriteINTEL"; + case SpvOpUCountLeadingZerosINTEL: return "OpUCountLeadingZerosINTEL"; + case SpvOpUCountTrailingZerosINTEL: return "OpUCountTrailingZerosINTEL"; + case SpvOpAbsISubINTEL: return "OpAbsISubINTEL"; + case SpvOpAbsUSubINTEL: return "OpAbsUSubINTEL"; + case SpvOpIAddSatINTEL: return "OpIAddSatINTEL"; + case SpvOpUAddSatINTEL: return "OpUAddSatINTEL"; + case SpvOpIAverageINTEL: return "OpIAverageINTEL"; + case SpvOpUAverageINTEL: return "OpUAverageINTEL"; + case SpvOpIAverageRoundedINTEL: return "OpIAverageRoundedINTEL"; + case SpvOpUAverageRoundedINTEL: return "OpUAverageRoundedINTEL"; + case SpvOpISubSatINTEL: return "OpISubSatINTEL"; + case SpvOpUSubSatINTEL: return "OpUSubSatINTEL"; + case SpvOpIMul32x16INTEL: return "OpIMul32x16INTEL"; + case SpvOpUMul32x16INTEL: return "OpUMul32x16INTEL"; + case SpvOpConstantFunctionPointerINTEL: return "OpConstantFunctionPointerINTEL"; + case SpvOpFunctionPointerCallINTEL: return "OpFunctionPointerCallINTEL"; + case SpvOpAsmTargetINTEL: return "OpAsmTargetINTEL"; + case SpvOpAsmINTEL: return "OpAsmINTEL"; + case SpvOpAsmCallINTEL: return "OpAsmCallINTEL"; + case SpvOpAtomicFMinEXT: return "OpAtomicFMinEXT"; + case SpvOpAtomicFMaxEXT: return "OpAtomicFMaxEXT"; + case SpvOpAssumeTrueKHR: return "OpAssumeTrueKHR"; + case SpvOpExpectKHR: return "OpExpectKHR"; + case SpvOpDecorateString: return "OpDecorateString"; + case SpvOpMemberDecorateString: return "OpMemberDecorateString"; + case SpvOpVmeImageINTEL: return "OpVmeImageINTEL"; + case SpvOpTypeVmeImageINTEL: return "OpTypeVmeImageINTEL"; + case SpvOpTypeAvcImePayloadINTEL: return "OpTypeAvcImePayloadINTEL"; + case SpvOpTypeAvcRefPayloadINTEL: return "OpTypeAvcRefPayloadINTEL"; + case SpvOpTypeAvcSicPayloadINTEL: return "OpTypeAvcSicPayloadINTEL"; + case SpvOpTypeAvcMcePayloadINTEL: return "OpTypeAvcMcePayloadINTEL"; + case SpvOpTypeAvcMceResultINTEL: return "OpTypeAvcMceResultINTEL"; + case SpvOpTypeAvcImeResultINTEL: return "OpTypeAvcImeResultINTEL"; + case SpvOpTypeAvcImeResultSingleReferenceStreamoutINTEL: return "OpTypeAvcImeResultSingleReferenceStreamoutINTEL"; + case SpvOpTypeAvcImeResultDualReferenceStreamoutINTEL: return "OpTypeAvcImeResultDualReferenceStreamoutINTEL"; + case SpvOpTypeAvcImeSingleReferenceStreaminINTEL: return "OpTypeAvcImeSingleReferenceStreaminINTEL"; + case SpvOpTypeAvcImeDualReferenceStreaminINTEL: return "OpTypeAvcImeDualReferenceStreaminINTEL"; + case SpvOpTypeAvcRefResultINTEL: return "OpTypeAvcRefResultINTEL"; + case SpvOpTypeAvcSicResultINTEL: return "OpTypeAvcSicResultINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL"; + case SpvOpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL"; + case SpvOpSubgroupAvcMceSetInterShapePenaltyINTEL: return "OpSubgroupAvcMceSetInterShapePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL"; + case SpvOpSubgroupAvcMceSetInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: return "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL"; + case SpvOpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL"; + case SpvOpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL"; + case SpvOpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL"; + case SpvOpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: return "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL"; + case SpvOpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL"; + case SpvOpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL"; + case SpvOpSubgroupAvcMceSetAcOnlyHaarINTEL: return "OpSubgroupAvcMceSetAcOnlyHaarINTEL"; + case SpvOpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL"; + case SpvOpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL"; + case SpvOpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL"; + case SpvOpSubgroupAvcMceConvertToImePayloadINTEL: return "OpSubgroupAvcMceConvertToImePayloadINTEL"; + case SpvOpSubgroupAvcMceConvertToImeResultINTEL: return "OpSubgroupAvcMceConvertToImeResultINTEL"; + case SpvOpSubgroupAvcMceConvertToRefPayloadINTEL: return "OpSubgroupAvcMceConvertToRefPayloadINTEL"; + case SpvOpSubgroupAvcMceConvertToRefResultINTEL: return "OpSubgroupAvcMceConvertToRefResultINTEL"; + case SpvOpSubgroupAvcMceConvertToSicPayloadINTEL: return "OpSubgroupAvcMceConvertToSicPayloadINTEL"; + case SpvOpSubgroupAvcMceConvertToSicResultINTEL: return "OpSubgroupAvcMceConvertToSicResultINTEL"; + case SpvOpSubgroupAvcMceGetMotionVectorsINTEL: return "OpSubgroupAvcMceGetMotionVectorsINTEL"; + case SpvOpSubgroupAvcMceGetInterDistortionsINTEL: return "OpSubgroupAvcMceGetInterDistortionsINTEL"; + case SpvOpSubgroupAvcMceGetBestInterDistortionsINTEL: return "OpSubgroupAvcMceGetBestInterDistortionsINTEL"; + case SpvOpSubgroupAvcMceGetInterMajorShapeINTEL: return "OpSubgroupAvcMceGetInterMajorShapeINTEL"; + case SpvOpSubgroupAvcMceGetInterMinorShapeINTEL: return "OpSubgroupAvcMceGetInterMinorShapeINTEL"; + case SpvOpSubgroupAvcMceGetInterDirectionsINTEL: return "OpSubgroupAvcMceGetInterDirectionsINTEL"; + case SpvOpSubgroupAvcMceGetInterMotionVectorCountINTEL: return "OpSubgroupAvcMceGetInterMotionVectorCountINTEL"; + case SpvOpSubgroupAvcMceGetInterReferenceIdsINTEL: return "OpSubgroupAvcMceGetInterReferenceIdsINTEL"; + case SpvOpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL"; + case SpvOpSubgroupAvcImeInitializeINTEL: return "OpSubgroupAvcImeInitializeINTEL"; + case SpvOpSubgroupAvcImeSetSingleReferenceINTEL: return "OpSubgroupAvcImeSetSingleReferenceINTEL"; + case SpvOpSubgroupAvcImeSetDualReferenceINTEL: return "OpSubgroupAvcImeSetDualReferenceINTEL"; + case SpvOpSubgroupAvcImeRefWindowSizeINTEL: return "OpSubgroupAvcImeRefWindowSizeINTEL"; + case SpvOpSubgroupAvcImeAdjustRefOffsetINTEL: return "OpSubgroupAvcImeAdjustRefOffsetINTEL"; + case SpvOpSubgroupAvcImeConvertToMcePayloadINTEL: return "OpSubgroupAvcImeConvertToMcePayloadINTEL"; + case SpvOpSubgroupAvcImeSetMaxMotionVectorCountINTEL: return "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL"; + case SpvOpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: return "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL"; + case SpvOpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: return "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL"; + case SpvOpSubgroupAvcImeSetWeightedSadINTEL: return "OpSubgroupAvcImeSetWeightedSadINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL"; + case SpvOpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL"; + case SpvOpSubgroupAvcImeConvertToMceResultINTEL: return "OpSubgroupAvcImeConvertToMceResultINTEL"; + case SpvOpSubgroupAvcImeGetSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeGetDualReferenceStreaminINTEL: return "OpSubgroupAvcImeGetDualReferenceStreaminINTEL"; + case SpvOpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeStripDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL"; + case SpvOpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL"; + case SpvOpSubgroupAvcImeGetBorderReachedINTEL: return "OpSubgroupAvcImeGetBorderReachedINTEL"; + case SpvOpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: return "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL"; + case SpvOpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: return "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL"; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL"; + case SpvOpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL"; + case SpvOpSubgroupAvcFmeInitializeINTEL: return "OpSubgroupAvcFmeInitializeINTEL"; + case SpvOpSubgroupAvcBmeInitializeINTEL: return "OpSubgroupAvcBmeInitializeINTEL"; + case SpvOpSubgroupAvcRefConvertToMcePayloadINTEL: return "OpSubgroupAvcRefConvertToMcePayloadINTEL"; + case SpvOpSubgroupAvcRefSetBidirectionalMixDisableINTEL: return "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL"; + case SpvOpSubgroupAvcRefSetBilinearFilterEnableINTEL: return "OpSubgroupAvcRefSetBilinearFilterEnableINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL"; + case SpvOpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL"; + case SpvOpSubgroupAvcRefConvertToMceResultINTEL: return "OpSubgroupAvcRefConvertToMceResultINTEL"; + case SpvOpSubgroupAvcSicInitializeINTEL: return "OpSubgroupAvcSicInitializeINTEL"; + case SpvOpSubgroupAvcSicConfigureSkcINTEL: return "OpSubgroupAvcSicConfigureSkcINTEL"; + case SpvOpSubgroupAvcSicConfigureIpeLumaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaINTEL"; + case SpvOpSubgroupAvcSicConfigureIpeLumaChromaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL"; + case SpvOpSubgroupAvcSicGetMotionVectorMaskINTEL: return "OpSubgroupAvcSicGetMotionVectorMaskINTEL"; + case SpvOpSubgroupAvcSicConvertToMcePayloadINTEL: return "OpSubgroupAvcSicConvertToMcePayloadINTEL"; + case SpvOpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL"; + case SpvOpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL"; + case SpvOpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL"; + case SpvOpSubgroupAvcSicSetBilinearFilterEnableINTEL: return "OpSubgroupAvcSicSetBilinearFilterEnableINTEL"; + case SpvOpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: return "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL"; + case SpvOpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: return "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL"; + case SpvOpSubgroupAvcSicEvaluateIpeINTEL: return "OpSubgroupAvcSicEvaluateIpeINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL"; + case SpvOpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL"; + case SpvOpSubgroupAvcSicConvertToMceResultINTEL: return "OpSubgroupAvcSicConvertToMceResultINTEL"; + case SpvOpSubgroupAvcSicGetIpeLumaShapeINTEL: return "OpSubgroupAvcSicGetIpeLumaShapeINTEL"; + case SpvOpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL"; + case SpvOpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL"; + case SpvOpSubgroupAvcSicGetPackedIpeLumaModesINTEL: return "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL"; + case SpvOpSubgroupAvcSicGetIpeChromaModeINTEL: return "OpSubgroupAvcSicGetIpeChromaModeINTEL"; + case SpvOpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL"; + case SpvOpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL"; + case SpvOpSubgroupAvcSicGetInterRawSadsINTEL: return "OpSubgroupAvcSicGetInterRawSadsINTEL"; + case SpvOpVariableLengthArrayINTEL: return "OpVariableLengthArrayINTEL"; + case SpvOpSaveMemoryINTEL: return "OpSaveMemoryINTEL"; + case SpvOpRestoreMemoryINTEL: return "OpRestoreMemoryINTEL"; + case SpvOpArbitraryFloatSinCosPiALTERA: return "OpArbitraryFloatSinCosPiALTERA"; + case SpvOpArbitraryFloatCastALTERA: return "OpArbitraryFloatCastALTERA"; + case SpvOpArbitraryFloatCastFromIntALTERA: return "OpArbitraryFloatCastFromIntALTERA"; + case SpvOpArbitraryFloatCastToIntALTERA: return "OpArbitraryFloatCastToIntALTERA"; + case SpvOpArbitraryFloatAddALTERA: return "OpArbitraryFloatAddALTERA"; + case SpvOpArbitraryFloatSubALTERA: return "OpArbitraryFloatSubALTERA"; + case SpvOpArbitraryFloatMulALTERA: return "OpArbitraryFloatMulALTERA"; + case SpvOpArbitraryFloatDivALTERA: return "OpArbitraryFloatDivALTERA"; + case SpvOpArbitraryFloatGTALTERA: return "OpArbitraryFloatGTALTERA"; + case SpvOpArbitraryFloatGEALTERA: return "OpArbitraryFloatGEALTERA"; + case SpvOpArbitraryFloatLTALTERA: return "OpArbitraryFloatLTALTERA"; + case SpvOpArbitraryFloatLEALTERA: return "OpArbitraryFloatLEALTERA"; + case SpvOpArbitraryFloatEQALTERA: return "OpArbitraryFloatEQALTERA"; + case SpvOpArbitraryFloatRecipALTERA: return "OpArbitraryFloatRecipALTERA"; + case SpvOpArbitraryFloatRSqrtALTERA: return "OpArbitraryFloatRSqrtALTERA"; + case SpvOpArbitraryFloatCbrtALTERA: return "OpArbitraryFloatCbrtALTERA"; + case SpvOpArbitraryFloatHypotALTERA: return "OpArbitraryFloatHypotALTERA"; + case SpvOpArbitraryFloatSqrtALTERA: return "OpArbitraryFloatSqrtALTERA"; + case SpvOpArbitraryFloatLogINTEL: return "OpArbitraryFloatLogINTEL"; + case SpvOpArbitraryFloatLog2INTEL: return "OpArbitraryFloatLog2INTEL"; + case SpvOpArbitraryFloatLog10INTEL: return "OpArbitraryFloatLog10INTEL"; + case SpvOpArbitraryFloatLog1pINTEL: return "OpArbitraryFloatLog1pINTEL"; + case SpvOpArbitraryFloatExpINTEL: return "OpArbitraryFloatExpINTEL"; + case SpvOpArbitraryFloatExp2INTEL: return "OpArbitraryFloatExp2INTEL"; + case SpvOpArbitraryFloatExp10INTEL: return "OpArbitraryFloatExp10INTEL"; + case SpvOpArbitraryFloatExpm1INTEL: return "OpArbitraryFloatExpm1INTEL"; + case SpvOpArbitraryFloatSinINTEL: return "OpArbitraryFloatSinINTEL"; + case SpvOpArbitraryFloatCosINTEL: return "OpArbitraryFloatCosINTEL"; + case SpvOpArbitraryFloatSinCosINTEL: return "OpArbitraryFloatSinCosINTEL"; + case SpvOpArbitraryFloatSinPiINTEL: return "OpArbitraryFloatSinPiINTEL"; + case SpvOpArbitraryFloatCosPiINTEL: return "OpArbitraryFloatCosPiINTEL"; + case SpvOpArbitraryFloatASinINTEL: return "OpArbitraryFloatASinINTEL"; + case SpvOpArbitraryFloatASinPiINTEL: return "OpArbitraryFloatASinPiINTEL"; + case SpvOpArbitraryFloatACosINTEL: return "OpArbitraryFloatACosINTEL"; + case SpvOpArbitraryFloatACosPiINTEL: return "OpArbitraryFloatACosPiINTEL"; + case SpvOpArbitraryFloatATanINTEL: return "OpArbitraryFloatATanINTEL"; + case SpvOpArbitraryFloatATanPiINTEL: return "OpArbitraryFloatATanPiINTEL"; + case SpvOpArbitraryFloatATan2INTEL: return "OpArbitraryFloatATan2INTEL"; + case SpvOpArbitraryFloatPowINTEL: return "OpArbitraryFloatPowINTEL"; + case SpvOpArbitraryFloatPowRINTEL: return "OpArbitraryFloatPowRINTEL"; + case SpvOpArbitraryFloatPowNINTEL: return "OpArbitraryFloatPowNINTEL"; + case SpvOpLoopControlINTEL: return "OpLoopControlINTEL"; + case SpvOpAliasDomainDeclINTEL: return "OpAliasDomainDeclINTEL"; + case SpvOpAliasScopeDeclINTEL: return "OpAliasScopeDeclINTEL"; + case SpvOpAliasScopeListDeclINTEL: return "OpAliasScopeListDeclINTEL"; + case SpvOpFixedSqrtALTERA: return "OpFixedSqrtALTERA"; + case SpvOpFixedRecipALTERA: return "OpFixedRecipALTERA"; + case SpvOpFixedRsqrtALTERA: return "OpFixedRsqrtALTERA"; + case SpvOpFixedSinALTERA: return "OpFixedSinALTERA"; + case SpvOpFixedCosALTERA: return "OpFixedCosALTERA"; + case SpvOpFixedSinCosALTERA: return "OpFixedSinCosALTERA"; + case SpvOpFixedSinPiALTERA: return "OpFixedSinPiALTERA"; + case SpvOpFixedCosPiALTERA: return "OpFixedCosPiALTERA"; + case SpvOpFixedSinCosPiALTERA: return "OpFixedSinCosPiALTERA"; + case SpvOpFixedLogALTERA: return "OpFixedLogALTERA"; + case SpvOpFixedExpALTERA: return "OpFixedExpALTERA"; + case SpvOpPtrCastToCrossWorkgroupALTERA: return "OpPtrCastToCrossWorkgroupALTERA"; + case SpvOpCrossWorkgroupCastToPtrALTERA: return "OpCrossWorkgroupCastToPtrALTERA"; + case SpvOpReadPipeBlockingALTERA: return "OpReadPipeBlockingALTERA"; + case SpvOpWritePipeBlockingALTERA: return "OpWritePipeBlockingALTERA"; + case SpvOpFPGARegALTERA: return "OpFPGARegALTERA"; + case SpvOpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case SpvOpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case SpvOpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case SpvOpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case SpvOpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case SpvOpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case SpvOpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case SpvOpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case SpvOpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case SpvOpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case SpvOpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case SpvOpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case SpvOpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case SpvOpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case SpvOpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case SpvOpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case SpvOpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + case SpvOpAtomicFAddEXT: return "OpAtomicFAddEXT"; + case SpvOpTypeBufferSurfaceINTEL: return "OpTypeBufferSurfaceINTEL"; + case SpvOpTypeStructContinuedINTEL: return "OpTypeStructContinuedINTEL"; + case SpvOpConstantCompositeContinuedINTEL: return "OpConstantCompositeContinuedINTEL"; + case SpvOpSpecConstantCompositeContinuedINTEL: return "OpSpecConstantCompositeContinuedINTEL"; + case SpvOpCompositeConstructContinuedINTEL: return "OpCompositeConstructContinuedINTEL"; + case SpvOpConvertFToBF16INTEL: return "OpConvertFToBF16INTEL"; + case SpvOpConvertBF16ToFINTEL: return "OpConvertBF16ToFINTEL"; + case SpvOpControlBarrierArriveINTEL: return "OpControlBarrierArriveINTEL"; + case SpvOpControlBarrierWaitINTEL: return "OpControlBarrierWaitINTEL"; + case SpvOpArithmeticFenceEXT: return "OpArithmeticFenceEXT"; + case SpvOpTaskSequenceCreateALTERA: return "OpTaskSequenceCreateALTERA"; + case SpvOpTaskSequenceAsyncALTERA: return "OpTaskSequenceAsyncALTERA"; + case SpvOpTaskSequenceGetALTERA: return "OpTaskSequenceGetALTERA"; + case SpvOpTaskSequenceReleaseALTERA: return "OpTaskSequenceReleaseALTERA"; + case SpvOpTypeTaskSequenceALTERA: return "OpTypeTaskSequenceALTERA"; + case SpvOpSubgroupBlockPrefetchINTEL: return "OpSubgroupBlockPrefetchINTEL"; + case SpvOpSubgroup2DBlockLoadINTEL: return "OpSubgroup2DBlockLoadINTEL"; + case SpvOpSubgroup2DBlockLoadTransformINTEL: return "OpSubgroup2DBlockLoadTransformINTEL"; + case SpvOpSubgroup2DBlockLoadTransposeINTEL: return "OpSubgroup2DBlockLoadTransposeINTEL"; + case SpvOpSubgroup2DBlockPrefetchINTEL: return "OpSubgroup2DBlockPrefetchINTEL"; + case SpvOpSubgroup2DBlockStoreINTEL: return "OpSubgroup2DBlockStoreINTEL"; + case SpvOpSubgroupMatrixMultiplyAccumulateINTEL: return "OpSubgroupMatrixMultiplyAccumulateINTEL"; + case SpvOpBitwiseFunctionINTEL: return "OpBitwiseFunctionINTEL"; + case SpvOpUntypedVariableLengthArrayINTEL: return "OpUntypedVariableLengthArrayINTEL"; + case SpvOpConditionalExtensionINTEL: return "OpConditionalExtensionINTEL"; + case SpvOpConditionalEntryPointINTEL: return "OpConditionalEntryPointINTEL"; + case SpvOpConditionalCapabilityINTEL: return "OpConditionalCapabilityINTEL"; + case SpvOpSpecConstantTargetINTEL: return "OpSpecConstantTargetINTEL"; + case SpvOpSpecConstantArchitectureINTEL: return "OpSpecConstantArchitectureINTEL"; + case SpvOpSpecConstantCapabilitiesINTEL: return "OpSpecConstantCapabilitiesINTEL"; + case SpvOpConditionalCopyObjectINTEL: return "OpConditionalCopyObjectINTEL"; + case SpvOpGroupIMulKHR: return "OpGroupIMulKHR"; + case SpvOpGroupFMulKHR: return "OpGroupFMulKHR"; + case SpvOpGroupBitwiseAndKHR: return "OpGroupBitwiseAndKHR"; + case SpvOpGroupBitwiseOrKHR: return "OpGroupBitwiseOrKHR"; + case SpvOpGroupBitwiseXorKHR: return "OpGroupBitwiseXorKHR"; + case SpvOpGroupLogicalAndKHR: return "OpGroupLogicalAndKHR"; + case SpvOpGroupLogicalOrKHR: return "OpGroupLogicalOrKHR"; + case SpvOpGroupLogicalXorKHR: return "OpGroupLogicalXorKHR"; + case SpvOpRoundFToTF32INTEL: return "OpRoundFToTF32INTEL"; + case SpvOpMaskedGatherINTEL: return "OpMaskedGatherINTEL"; + case SpvOpMaskedScatterINTEL: return "OpMaskedScatterINTEL"; + case SpvOpConvertHandleToImageINTEL: return "OpConvertHandleToImageINTEL"; + case SpvOpConvertHandleToSamplerINTEL: return "OpConvertHandleToSamplerINTEL"; + case SpvOpConvertHandleToSampledImageINTEL: return "OpConvertHandleToSampledImageINTEL"; + case SpvOpFDot2MixAcc32VALVE: return "OpFDot2MixAcc32VALVE"; + case SpvOpFDot2MixAcc16VALVE: return "OpFDot2MixAcc16VALVE"; + case SpvOpFDot4MixAcc32VALVE: return "OpFDot4MixAcc32VALVE"; + default: return "Unknown"; + } +} + +#endif /* SPV_ENABLE_UTILITY_CODE */ + +#endif + diff --git a/thirdparty/SPIRV-Cross/spirv.hpp b/thirdparty/SPIRV-Cross/spirv.hpp new file mode 100644 index 000000000..462c3f6bf --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv.hpp @@ -0,0 +1,5667 @@ +// Copyright: 2014-2024 The Khronos Group Inc. +// License: MIT +// +// MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS +// KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS +// SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT +// https://www.khronos.org/registry/ + +// This header is automatically generated by the same tool that creates +// the Binary Section of the SPIR-V specification. + +// Enumeration tokens for SPIR-V, in various styles: +// C, C++, C++11, JSON, Lua, Python, C#, D, Beef +// +// - C will have tokens with a "Spv" prefix, e.g.: SpvSourceLanguageGLSL +// - C++ will have tokens in the "spv" name space, e.g.: spv::SourceLanguageGLSL +// - C++11 will use enum classes in the spv namespace, e.g.: spv::SourceLanguage::GLSL +// - Lua will use tables, e.g.: spv.SourceLanguage.GLSL +// - Python will use dictionaries, e.g.: spv['SourceLanguage']['GLSL'] +// - C# will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// - D will have tokens under the "spv" module, e.g: spv.SourceLanguage.GLSL +// - Beef will use enum classes in the Specification class located in the "Spv" namespace, +// e.g.: Spv.Specification.SourceLanguage.GLSL +// +// Some tokens act like mask values, which can be OR'd together, +// while others are mutually exclusive. The mask-like ones have +// "Mask" in their name, and a parallel enum that has the shift +// amount (1 << x) for each corresponding enumerant. + +#ifndef spirv_HPP +#define spirv_HPP + +namespace spv { + +typedef unsigned int Id; + +#define SPV_VERSION 0x10600 +#define SPV_REVISION 1 + +static const unsigned int MagicNumber = 0x07230203; +static const unsigned int Version = 0x00010600; +static const unsigned int Revision = 1; +static const unsigned int OpCodeMask = 0xffff; +static const unsigned int WordCountShift = 16; + +enum SourceLanguage { + SourceLanguageUnknown = 0, + SourceLanguageESSL = 1, + SourceLanguageGLSL = 2, + SourceLanguageOpenCL_C = 3, + SourceLanguageOpenCL_CPP = 4, + SourceLanguageHLSL = 5, + SourceLanguageCPP_for_OpenCL = 6, + SourceLanguageSYCL = 7, + SourceLanguageHERO_C = 8, + SourceLanguageNZSL = 9, + SourceLanguageWGSL = 10, + SourceLanguageSlang = 11, + SourceLanguageZig = 12, + SourceLanguageRust = 13, + SourceLanguageMax = 0x7fffffff, +}; + +enum ExecutionModel { + ExecutionModelVertex = 0, + ExecutionModelTessellationControl = 1, + ExecutionModelTessellationEvaluation = 2, + ExecutionModelGeometry = 3, + ExecutionModelFragment = 4, + ExecutionModelGLCompute = 5, + ExecutionModelKernel = 6, + ExecutionModelTaskNV = 5267, + ExecutionModelMeshNV = 5268, + ExecutionModelRayGenerationKHR = 5313, + ExecutionModelRayGenerationNV = 5313, + ExecutionModelIntersectionKHR = 5314, + ExecutionModelIntersectionNV = 5314, + ExecutionModelAnyHitKHR = 5315, + ExecutionModelAnyHitNV = 5315, + ExecutionModelClosestHitKHR = 5316, + ExecutionModelClosestHitNV = 5316, + ExecutionModelMissKHR = 5317, + ExecutionModelMissNV = 5317, + ExecutionModelCallableKHR = 5318, + ExecutionModelCallableNV = 5318, + ExecutionModelTaskEXT = 5364, + ExecutionModelMeshEXT = 5365, + ExecutionModelMax = 0x7fffffff, +}; + +enum AddressingModel { + AddressingModelLogical = 0, + AddressingModelPhysical32 = 1, + AddressingModelPhysical64 = 2, + AddressingModelPhysicalStorageBuffer64 = 5348, + AddressingModelPhysicalStorageBuffer64EXT = 5348, + AddressingModelMax = 0x7fffffff, +}; + +enum MemoryModel { + MemoryModelSimple = 0, + MemoryModelGLSL450 = 1, + MemoryModelOpenCL = 2, + MemoryModelVulkan = 3, + MemoryModelVulkanKHR = 3, + MemoryModelMax = 0x7fffffff, +}; + +enum ExecutionMode { + ExecutionModeInvocations = 0, + ExecutionModeSpacingEqual = 1, + ExecutionModeSpacingFractionalEven = 2, + ExecutionModeSpacingFractionalOdd = 3, + ExecutionModeVertexOrderCw = 4, + ExecutionModeVertexOrderCcw = 5, + ExecutionModePixelCenterInteger = 6, + ExecutionModeOriginUpperLeft = 7, + ExecutionModeOriginLowerLeft = 8, + ExecutionModeEarlyFragmentTests = 9, + ExecutionModePointMode = 10, + ExecutionModeXfb = 11, + ExecutionModeDepthReplacing = 12, + ExecutionModeDepthGreater = 14, + ExecutionModeDepthLess = 15, + ExecutionModeDepthUnchanged = 16, + ExecutionModeLocalSize = 17, + ExecutionModeLocalSizeHint = 18, + ExecutionModeInputPoints = 19, + ExecutionModeInputLines = 20, + ExecutionModeInputLinesAdjacency = 21, + ExecutionModeTriangles = 22, + ExecutionModeInputTrianglesAdjacency = 23, + ExecutionModeQuads = 24, + ExecutionModeIsolines = 25, + ExecutionModeOutputVertices = 26, + ExecutionModeOutputPoints = 27, + ExecutionModeOutputLineStrip = 28, + ExecutionModeOutputTriangleStrip = 29, + ExecutionModeVecTypeHint = 30, + ExecutionModeContractionOff = 31, + ExecutionModeInitializer = 33, + ExecutionModeFinalizer = 34, + ExecutionModeSubgroupSize = 35, + ExecutionModeSubgroupsPerWorkgroup = 36, + ExecutionModeSubgroupsPerWorkgroupId = 37, + ExecutionModeLocalSizeId = 38, + ExecutionModeLocalSizeHintId = 39, + ExecutionModeNonCoherentColorAttachmentReadEXT = 4169, + ExecutionModeNonCoherentDepthAttachmentReadEXT = 4170, + ExecutionModeNonCoherentStencilAttachmentReadEXT = 4171, + ExecutionModeSubgroupUniformControlFlowKHR = 4421, + ExecutionModePostDepthCoverage = 4446, + ExecutionModeDenormPreserve = 4459, + ExecutionModeDenormFlushToZero = 4460, + ExecutionModeSignedZeroInfNanPreserve = 4461, + ExecutionModeRoundingModeRTE = 4462, + ExecutionModeRoundingModeRTZ = 4463, + ExecutionModeNonCoherentTileAttachmentReadQCOM = 4489, + ExecutionModeTileShadingRateQCOM = 4490, + ExecutionModeEarlyAndLateFragmentTestsAMD = 5017, + ExecutionModeStencilRefReplacingEXT = 5027, + ExecutionModeCoalescingAMDX = 5069, + ExecutionModeIsApiEntryAMDX = 5070, + ExecutionModeMaxNodeRecursionAMDX = 5071, + ExecutionModeStaticNumWorkgroupsAMDX = 5072, + ExecutionModeShaderIndexAMDX = 5073, + ExecutionModeMaxNumWorkgroupsAMDX = 5077, + ExecutionModeStencilRefUnchangedFrontAMD = 5079, + ExecutionModeStencilRefGreaterFrontAMD = 5080, + ExecutionModeStencilRefLessFrontAMD = 5081, + ExecutionModeStencilRefUnchangedBackAMD = 5082, + ExecutionModeStencilRefGreaterBackAMD = 5083, + ExecutionModeStencilRefLessBackAMD = 5084, + ExecutionModeQuadDerivativesKHR = 5088, + ExecutionModeRequireFullQuadsKHR = 5089, + ExecutionModeSharesInputWithAMDX = 5102, + ExecutionModeArithmeticPoisonKHR = 5157, + ExecutionModeOutputLinesEXT = 5269, + ExecutionModeOutputLinesNV = 5269, + ExecutionModeOutputPrimitivesEXT = 5270, + ExecutionModeOutputPrimitivesNV = 5270, + ExecutionModeDerivativeGroupQuadsKHR = 5289, + ExecutionModeDerivativeGroupQuadsNV = 5289, + ExecutionModeDerivativeGroupLinearKHR = 5290, + ExecutionModeDerivativeGroupLinearNV = 5290, + ExecutionModeOutputTrianglesEXT = 5298, + ExecutionModeOutputTrianglesNV = 5298, + ExecutionModePixelInterlockOrderedEXT = 5366, + ExecutionModePixelInterlockUnorderedEXT = 5367, + ExecutionModeSampleInterlockOrderedEXT = 5368, + ExecutionModeSampleInterlockUnorderedEXT = 5369, + ExecutionModeShadingRateInterlockOrderedEXT = 5370, + ExecutionModeShadingRateInterlockUnorderedEXT = 5371, + ExecutionModeShader64BitIndexingEXT = 5427, + ExecutionModeSharedLocalMemorySizeINTEL = 5618, + ExecutionModeRoundingModeRTPINTEL = 5620, + ExecutionModeRoundingModeRTNINTEL = 5621, + ExecutionModeFloatingPointModeALTINTEL = 5622, + ExecutionModeFloatingPointModeIEEEINTEL = 5623, + ExecutionModeMaxWorkgroupSizeINTEL = 5893, + ExecutionModeMaxWorkDimINTEL = 5894, + ExecutionModeNoGlobalOffsetINTEL = 5895, + ExecutionModeNumSIMDWorkitemsINTEL = 5896, + ExecutionModeSchedulerTargetFmaxMhzINTEL = 5903, + ExecutionModeMaximallyReconvergesKHR = 6023, + ExecutionModeFPFastMathDefault = 6028, + ExecutionModeStreamingInterfaceINTEL = 6154, + ExecutionModeRegisterMapInterfaceINTEL = 6160, + ExecutionModeNamedBarrierCountINTEL = 6417, + ExecutionModeMaximumRegistersINTEL = 6461, + ExecutionModeMaximumRegistersIdINTEL = 6462, + ExecutionModeNamedMaximumRegistersINTEL = 6463, + ExecutionModeMax = 0x7fffffff, +}; + +enum StorageClass { + StorageClassUniformConstant = 0, + StorageClassInput = 1, + StorageClassUniform = 2, + StorageClassOutput = 3, + StorageClassWorkgroup = 4, + StorageClassCrossWorkgroup = 5, + StorageClassPrivate = 6, + StorageClassFunction = 7, + StorageClassGeneric = 8, + StorageClassPushConstant = 9, + StorageClassAtomicCounter = 10, + StorageClassImage = 11, + StorageClassStorageBuffer = 12, + StorageClassTileImageEXT = 4172, + StorageClassTileAttachmentQCOM = 4491, + StorageClassNodePayloadAMDX = 5068, + StorageClassCallableDataKHR = 5328, + StorageClassCallableDataNV = 5328, + StorageClassIncomingCallableDataKHR = 5329, + StorageClassIncomingCallableDataNV = 5329, + StorageClassRayPayloadKHR = 5338, + StorageClassRayPayloadNV = 5338, + StorageClassHitAttributeKHR = 5339, + StorageClassHitAttributeNV = 5339, + StorageClassIncomingRayPayloadKHR = 5342, + StorageClassIncomingRayPayloadNV = 5342, + StorageClassShaderRecordBufferKHR = 5343, + StorageClassShaderRecordBufferNV = 5343, + StorageClassPhysicalStorageBuffer = 5349, + StorageClassPhysicalStorageBufferEXT = 5349, + StorageClassHitObjectAttributeNV = 5385, + StorageClassTaskPayloadWorkgroupEXT = 5402, + StorageClassHitObjectAttributeEXT = 5411, + StorageClassCodeSectionINTEL = 5605, + StorageClassDeviceOnlyALTERA = 5936, + StorageClassDeviceOnlyINTEL = 5936, + StorageClassHostOnlyALTERA = 5937, + StorageClassHostOnlyINTEL = 5937, + StorageClassMax = 0x7fffffff, +}; + +enum Dim { + Dim1D = 0, + Dim2D = 1, + Dim3D = 2, + DimCube = 3, + DimRect = 4, + DimBuffer = 5, + DimSubpassData = 6, + DimTileImageDataEXT = 4173, + DimMax = 0x7fffffff, +}; + +enum SamplerAddressingMode { + SamplerAddressingModeNone = 0, + SamplerAddressingModeClampToEdge = 1, + SamplerAddressingModeClamp = 2, + SamplerAddressingModeRepeat = 3, + SamplerAddressingModeRepeatMirrored = 4, + SamplerAddressingModeMax = 0x7fffffff, +}; + +enum SamplerFilterMode { + SamplerFilterModeNearest = 0, + SamplerFilterModeLinear = 1, + SamplerFilterModeMax = 0x7fffffff, +}; + +enum ImageFormat { + ImageFormatUnknown = 0, + ImageFormatRgba32f = 1, + ImageFormatRgba16f = 2, + ImageFormatR32f = 3, + ImageFormatRgba8 = 4, + ImageFormatRgba8Snorm = 5, + ImageFormatRg32f = 6, + ImageFormatRg16f = 7, + ImageFormatR11fG11fB10f = 8, + ImageFormatR16f = 9, + ImageFormatRgba16 = 10, + ImageFormatRgb10A2 = 11, + ImageFormatRg16 = 12, + ImageFormatRg8 = 13, + ImageFormatR16 = 14, + ImageFormatR8 = 15, + ImageFormatRgba16Snorm = 16, + ImageFormatRg16Snorm = 17, + ImageFormatRg8Snorm = 18, + ImageFormatR16Snorm = 19, + ImageFormatR8Snorm = 20, + ImageFormatRgba32i = 21, + ImageFormatRgba16i = 22, + ImageFormatRgba8i = 23, + ImageFormatR32i = 24, + ImageFormatRg32i = 25, + ImageFormatRg16i = 26, + ImageFormatRg8i = 27, + ImageFormatR16i = 28, + ImageFormatR8i = 29, + ImageFormatRgba32ui = 30, + ImageFormatRgba16ui = 31, + ImageFormatRgba8ui = 32, + ImageFormatR32ui = 33, + ImageFormatRgb10a2ui = 34, + ImageFormatRg32ui = 35, + ImageFormatRg16ui = 36, + ImageFormatRg8ui = 37, + ImageFormatR16ui = 38, + ImageFormatR8ui = 39, + ImageFormatR64ui = 40, + ImageFormatR64i = 41, + ImageFormatMax = 0x7fffffff, +}; + +enum ImageChannelOrder { + ImageChannelOrderR = 0, + ImageChannelOrderA = 1, + ImageChannelOrderRG = 2, + ImageChannelOrderRA = 3, + ImageChannelOrderRGB = 4, + ImageChannelOrderRGBA = 5, + ImageChannelOrderBGRA = 6, + ImageChannelOrderARGB = 7, + ImageChannelOrderIntensity = 8, + ImageChannelOrderLuminance = 9, + ImageChannelOrderRx = 10, + ImageChannelOrderRGx = 11, + ImageChannelOrderRGBx = 12, + ImageChannelOrderDepth = 13, + ImageChannelOrderDepthStencil = 14, + ImageChannelOrdersRGB = 15, + ImageChannelOrdersRGBx = 16, + ImageChannelOrdersRGBA = 17, + ImageChannelOrdersBGRA = 18, + ImageChannelOrderABGR = 19, + ImageChannelOrderMax = 0x7fffffff, +}; + +enum ImageChannelDataType { + ImageChannelDataTypeSnormInt8 = 0, + ImageChannelDataTypeSnormInt16 = 1, + ImageChannelDataTypeUnormInt8 = 2, + ImageChannelDataTypeUnormInt16 = 3, + ImageChannelDataTypeUnormShort565 = 4, + ImageChannelDataTypeUnormShort555 = 5, + ImageChannelDataTypeUnormInt101010 = 6, + ImageChannelDataTypeSignedInt8 = 7, + ImageChannelDataTypeSignedInt16 = 8, + ImageChannelDataTypeSignedInt32 = 9, + ImageChannelDataTypeUnsignedInt8 = 10, + ImageChannelDataTypeUnsignedInt16 = 11, + ImageChannelDataTypeUnsignedInt32 = 12, + ImageChannelDataTypeHalfFloat = 13, + ImageChannelDataTypeFloat = 14, + ImageChannelDataTypeUnormInt24 = 15, + ImageChannelDataTypeUnormInt101010_2 = 16, + ImageChannelDataTypeUnormInt10X6EXT = 17, + ImageChannelDataTypeUnsignedIntRaw10EXT = 19, + ImageChannelDataTypeUnsignedIntRaw12EXT = 20, + ImageChannelDataTypeUnormInt2_101010EXT = 21, + ImageChannelDataTypeUnsignedInt10X6EXT = 22, + ImageChannelDataTypeUnsignedInt12X4EXT = 23, + ImageChannelDataTypeUnsignedInt14X2EXT = 24, + ImageChannelDataTypeUnormInt12X4EXT = 25, + ImageChannelDataTypeUnormInt14X2EXT = 26, + ImageChannelDataTypeMax = 0x7fffffff, +}; + +enum ImageOperandsShift { + ImageOperandsBiasShift = 0, + ImageOperandsLodShift = 1, + ImageOperandsGradShift = 2, + ImageOperandsConstOffsetShift = 3, + ImageOperandsOffsetShift = 4, + ImageOperandsConstOffsetsShift = 5, + ImageOperandsSampleShift = 6, + ImageOperandsMinLodShift = 7, + ImageOperandsMakeTexelAvailableShift = 8, + ImageOperandsMakeTexelAvailableKHRShift = 8, + ImageOperandsMakeTexelVisibleShift = 9, + ImageOperandsMakeTexelVisibleKHRShift = 9, + ImageOperandsNonPrivateTexelShift = 10, + ImageOperandsNonPrivateTexelKHRShift = 10, + ImageOperandsVolatileTexelShift = 11, + ImageOperandsVolatileTexelKHRShift = 11, + ImageOperandsSignExtendShift = 12, + ImageOperandsZeroExtendShift = 13, + ImageOperandsNontemporalShift = 14, + ImageOperandsOffsetsShift = 16, + ImageOperandsMax = 0x7fffffff, +}; + +enum ImageOperandsMask { + ImageOperandsMaskNone = 0, + ImageOperandsBiasMask = 0x00000001, + ImageOperandsLodMask = 0x00000002, + ImageOperandsGradMask = 0x00000004, + ImageOperandsConstOffsetMask = 0x00000008, + ImageOperandsOffsetMask = 0x00000010, + ImageOperandsConstOffsetsMask = 0x00000020, + ImageOperandsSampleMask = 0x00000040, + ImageOperandsMinLodMask = 0x00000080, + ImageOperandsMakeTexelAvailableMask = 0x00000100, + ImageOperandsMakeTexelAvailableKHRMask = 0x00000100, + ImageOperandsMakeTexelVisibleMask = 0x00000200, + ImageOperandsMakeTexelVisibleKHRMask = 0x00000200, + ImageOperandsNonPrivateTexelMask = 0x00000400, + ImageOperandsNonPrivateTexelKHRMask = 0x00000400, + ImageOperandsVolatileTexelMask = 0x00000800, + ImageOperandsVolatileTexelKHRMask = 0x00000800, + ImageOperandsSignExtendMask = 0x00001000, + ImageOperandsZeroExtendMask = 0x00002000, + ImageOperandsNontemporalMask = 0x00004000, + ImageOperandsOffsetsMask = 0x00010000, +}; + +enum FPFastMathModeShift { + FPFastMathModeNotNaNShift = 0, + FPFastMathModeNotInfShift = 1, + FPFastMathModeNSZShift = 2, + FPFastMathModeAllowRecipShift = 3, + FPFastMathModeFastShift = 4, + FPFastMathModeAllowContractShift = 16, + FPFastMathModeAllowContractFastINTELShift = 16, + FPFastMathModeAllowReassocShift = 17, + FPFastMathModeAllowReassocINTELShift = 17, + FPFastMathModeAllowTransformShift = 18, + FPFastMathModeMax = 0x7fffffff, +}; + +enum FPFastMathModeMask { + FPFastMathModeMaskNone = 0, + FPFastMathModeNotNaNMask = 0x00000001, + FPFastMathModeNotInfMask = 0x00000002, + FPFastMathModeNSZMask = 0x00000004, + FPFastMathModeAllowRecipMask = 0x00000008, + FPFastMathModeFastMask = 0x00000010, + FPFastMathModeAllowContractMask = 0x00010000, + FPFastMathModeAllowContractFastINTELMask = 0x00010000, + FPFastMathModeAllowReassocMask = 0x00020000, + FPFastMathModeAllowReassocINTELMask = 0x00020000, + FPFastMathModeAllowTransformMask = 0x00040000, +}; + +enum FPRoundingMode { + FPRoundingModeRTE = 0, + FPRoundingModeRTZ = 1, + FPRoundingModeRTP = 2, + FPRoundingModeRTN = 3, + FPRoundingModeMax = 0x7fffffff, +}; + +enum LinkageType { + LinkageTypeExport = 0, + LinkageTypeImport = 1, + LinkageTypeLinkOnceODR = 2, + LinkageTypeMax = 0x7fffffff, +}; + +enum AccessQualifier { + AccessQualifierReadOnly = 0, + AccessQualifierWriteOnly = 1, + AccessQualifierReadWrite = 2, + AccessQualifierMax = 0x7fffffff, +}; + +enum FunctionParameterAttribute { + FunctionParameterAttributeZext = 0, + FunctionParameterAttributeSext = 1, + FunctionParameterAttributeByVal = 2, + FunctionParameterAttributeSret = 3, + FunctionParameterAttributeNoAlias = 4, + FunctionParameterAttributeNoCapture = 5, + FunctionParameterAttributeNoWrite = 6, + FunctionParameterAttributeNoReadWrite = 7, + FunctionParameterAttributeRuntimeAlignedALTERA = 5940, + FunctionParameterAttributeRuntimeAlignedINTEL = 5940, + FunctionParameterAttributeMax = 0x7fffffff, +}; + +enum Decoration { + DecorationRelaxedPrecision = 0, + DecorationSpecId = 1, + DecorationBlock = 2, + DecorationBufferBlock = 3, + DecorationRowMajor = 4, + DecorationColMajor = 5, + DecorationArrayStride = 6, + DecorationMatrixStride = 7, + DecorationGLSLShared = 8, + DecorationGLSLPacked = 9, + DecorationCPacked = 10, + DecorationBuiltIn = 11, + DecorationNoPerspective = 13, + DecorationFlat = 14, + DecorationPatch = 15, + DecorationCentroid = 16, + DecorationSample = 17, + DecorationInvariant = 18, + DecorationRestrict = 19, + DecorationAliased = 20, + DecorationVolatile = 21, + DecorationConstant = 22, + DecorationCoherent = 23, + DecorationNonWritable = 24, + DecorationNonReadable = 25, + DecorationUniform = 26, + DecorationUniformId = 27, + DecorationSaturatedConversion = 28, + DecorationStream = 29, + DecorationLocation = 30, + DecorationComponent = 31, + DecorationIndex = 32, + DecorationBinding = 33, + DecorationDescriptorSet = 34, + DecorationOffset = 35, + DecorationXfbBuffer = 36, + DecorationXfbStride = 37, + DecorationFuncParamAttr = 38, + DecorationFPRoundingMode = 39, + DecorationFPFastMathMode = 40, + DecorationLinkageAttributes = 41, + DecorationNoContraction = 42, + DecorationInputAttachmentIndex = 43, + DecorationAlignment = 44, + DecorationMaxByteOffset = 45, + DecorationAlignmentId = 46, + DecorationMaxByteOffsetId = 47, + DecorationSaturatedToLargestFloat8NormalConversionEXT = 4216, + DecorationNoSignedWrap = 4469, + DecorationNoUnsignedWrap = 4470, + DecorationWeightTextureQCOM = 4487, + DecorationBlockMatchTextureQCOM = 4488, + DecorationBlockMatchSamplerQCOM = 4499, + DecorationExplicitInterpAMD = 4999, + DecorationNodeSharesPayloadLimitsWithAMDX = 5019, + DecorationNodeMaxPayloadsAMDX = 5020, + DecorationTrackFinishWritingAMDX = 5078, + DecorationPayloadNodeNameAMDX = 5091, + DecorationPayloadNodeBaseIndexAMDX = 5098, + DecorationPayloadNodeSparseArrayAMDX = 5099, + DecorationPayloadNodeArraySizeAMDX = 5100, + DecorationPayloadDispatchIndirectAMDX = 5105, + DecorationArrayStrideIdEXT = 5124, + DecorationOffsetIdEXT = 5125, + DecorationUTFEncodedKHR = 5145, + DecorationOverrideCoverageNV = 5248, + DecorationPassthroughNV = 5250, + DecorationViewportRelativeNV = 5252, + DecorationSecondaryViewportRelativeNV = 5256, + DecorationPerPrimitiveEXT = 5271, + DecorationPerPrimitiveNV = 5271, + DecorationPerViewNV = 5272, + DecorationPerTaskNV = 5273, + DecorationPerVertexKHR = 5285, + DecorationPerVertexNV = 5285, + DecorationNonUniform = 5300, + DecorationNonUniformEXT = 5300, + DecorationRestrictPointer = 5355, + DecorationRestrictPointerEXT = 5355, + DecorationAliasedPointer = 5356, + DecorationAliasedPointerEXT = 5356, + DecorationMemberOffsetNV = 5358, + DecorationHitObjectShaderRecordBufferNV = 5386, + DecorationHitObjectShaderRecordBufferEXT = 5389, + DecorationBankNV = 5397, + DecorationBindlessSamplerNV = 5398, + DecorationBindlessImageNV = 5399, + DecorationBoundSamplerNV = 5400, + DecorationBoundImageNV = 5401, + DecorationSIMTCallINTEL = 5599, + DecorationReferencedIndirectlyINTEL = 5602, + DecorationClobberINTEL = 5607, + DecorationSideEffectsINTEL = 5608, + DecorationVectorComputeVariableINTEL = 5624, + DecorationFuncParamIOKindINTEL = 5625, + DecorationVectorComputeFunctionINTEL = 5626, + DecorationStackCallINTEL = 5627, + DecorationGlobalVariableOffsetINTEL = 5628, + DecorationCounterBuffer = 5634, + DecorationHlslCounterBufferGOOGLE = 5634, + DecorationHlslSemanticGOOGLE = 5635, + DecorationUserSemantic = 5635, + DecorationUserTypeGOOGLE = 5636, + DecorationFunctionRoundingModeINTEL = 5822, + DecorationFunctionDenormModeINTEL = 5823, + DecorationRegisterALTERA = 5825, + DecorationRegisterINTEL = 5825, + DecorationMemoryALTERA = 5826, + DecorationMemoryINTEL = 5826, + DecorationNumbanksALTERA = 5827, + DecorationNumbanksINTEL = 5827, + DecorationBankwidthALTERA = 5828, + DecorationBankwidthINTEL = 5828, + DecorationMaxPrivateCopiesALTERA = 5829, + DecorationMaxPrivateCopiesINTEL = 5829, + DecorationSinglepumpALTERA = 5830, + DecorationSinglepumpINTEL = 5830, + DecorationDoublepumpALTERA = 5831, + DecorationDoublepumpINTEL = 5831, + DecorationMaxReplicatesALTERA = 5832, + DecorationMaxReplicatesINTEL = 5832, + DecorationSimpleDualPortALTERA = 5833, + DecorationSimpleDualPortINTEL = 5833, + DecorationMergeALTERA = 5834, + DecorationMergeINTEL = 5834, + DecorationBankBitsALTERA = 5835, + DecorationBankBitsINTEL = 5835, + DecorationForcePow2DepthALTERA = 5836, + DecorationForcePow2DepthINTEL = 5836, + DecorationStridesizeALTERA = 5883, + DecorationStridesizeINTEL = 5883, + DecorationWordsizeALTERA = 5884, + DecorationWordsizeINTEL = 5884, + DecorationTrueDualPortALTERA = 5885, + DecorationTrueDualPortINTEL = 5885, + DecorationBurstCoalesceALTERA = 5899, + DecorationBurstCoalesceINTEL = 5899, + DecorationCacheSizeALTERA = 5900, + DecorationCacheSizeINTEL = 5900, + DecorationDontStaticallyCoalesceALTERA = 5901, + DecorationDontStaticallyCoalesceINTEL = 5901, + DecorationPrefetchALTERA = 5902, + DecorationPrefetchINTEL = 5902, + DecorationStallEnableALTERA = 5905, + DecorationStallEnableINTEL = 5905, + DecorationFuseLoopsInFunctionALTERA = 5907, + DecorationFuseLoopsInFunctionINTEL = 5907, + DecorationMathOpDSPModeALTERA = 5909, + DecorationMathOpDSPModeINTEL = 5909, + DecorationAliasScopeINTEL = 5914, + DecorationNoAliasINTEL = 5915, + DecorationInitiationIntervalALTERA = 5917, + DecorationInitiationIntervalINTEL = 5917, + DecorationMaxConcurrencyALTERA = 5918, + DecorationMaxConcurrencyINTEL = 5918, + DecorationPipelineEnableALTERA = 5919, + DecorationPipelineEnableINTEL = 5919, + DecorationBufferLocationALTERA = 5921, + DecorationBufferLocationINTEL = 5921, + DecorationIOPipeStorageALTERA = 5944, + DecorationIOPipeStorageINTEL = 5944, + DecorationFunctionFloatingPointModeINTEL = 6080, + DecorationSingleElementVectorINTEL = 6085, + DecorationVectorComputeCallableFunctionINTEL = 6087, + DecorationMediaBlockIOINTEL = 6140, + DecorationStallFreeALTERA = 6151, + DecorationStallFreeINTEL = 6151, + DecorationFPMaxErrorDecorationINTEL = 6170, + DecorationLatencyControlLabelALTERA = 6172, + DecorationLatencyControlLabelINTEL = 6172, + DecorationLatencyControlConstraintALTERA = 6173, + DecorationLatencyControlConstraintINTEL = 6173, + DecorationConduitKernelArgumentALTERA = 6175, + DecorationConduitKernelArgumentINTEL = 6175, + DecorationRegisterMapKernelArgumentALTERA = 6176, + DecorationRegisterMapKernelArgumentINTEL = 6176, + DecorationMMHostInterfaceAddressWidthALTERA = 6177, + DecorationMMHostInterfaceAddressWidthINTEL = 6177, + DecorationMMHostInterfaceDataWidthALTERA = 6178, + DecorationMMHostInterfaceDataWidthINTEL = 6178, + DecorationMMHostInterfaceLatencyALTERA = 6179, + DecorationMMHostInterfaceLatencyINTEL = 6179, + DecorationMMHostInterfaceReadWriteModeALTERA = 6180, + DecorationMMHostInterfaceReadWriteModeINTEL = 6180, + DecorationMMHostInterfaceMaxBurstALTERA = 6181, + DecorationMMHostInterfaceMaxBurstINTEL = 6181, + DecorationMMHostInterfaceWaitRequestALTERA = 6182, + DecorationMMHostInterfaceWaitRequestINTEL = 6182, + DecorationStableKernelArgumentALTERA = 6183, + DecorationStableKernelArgumentINTEL = 6183, + DecorationHostAccessINTEL = 6188, + DecorationInitModeALTERA = 6190, + DecorationInitModeINTEL = 6190, + DecorationImplementInRegisterMapALTERA = 6191, + DecorationImplementInRegisterMapINTEL = 6191, + DecorationConditionalINTEL = 6247, + DecorationCacheControlLoadINTEL = 6442, + DecorationCacheControlStoreINTEL = 6443, + DecorationMax = 0x7fffffff, +}; + +enum BuiltIn { + BuiltInPosition = 0, + BuiltInPointSize = 1, + BuiltInClipDistance = 3, + BuiltInCullDistance = 4, + BuiltInVertexId = 5, + BuiltInInstanceId = 6, + BuiltInPrimitiveId = 7, + BuiltInInvocationId = 8, + BuiltInLayer = 9, + BuiltInViewportIndex = 10, + BuiltInTessLevelOuter = 11, + BuiltInTessLevelInner = 12, + BuiltInTessCoord = 13, + BuiltInPatchVertices = 14, + BuiltInFragCoord = 15, + BuiltInPointCoord = 16, + BuiltInFrontFacing = 17, + BuiltInSampleId = 18, + BuiltInSamplePosition = 19, + BuiltInSampleMask = 20, + BuiltInFragDepth = 22, + BuiltInHelperInvocation = 23, + BuiltInNumWorkgroups = 24, + BuiltInWorkgroupSize = 25, + BuiltInWorkgroupId = 26, + BuiltInLocalInvocationId = 27, + BuiltInGlobalInvocationId = 28, + BuiltInLocalInvocationIndex = 29, + BuiltInWorkDim = 30, + BuiltInGlobalSize = 31, + BuiltInEnqueuedWorkgroupSize = 32, + BuiltInGlobalOffset = 33, + BuiltInGlobalLinearId = 34, + BuiltInSubgroupSize = 36, + BuiltInSubgroupMaxSize = 37, + BuiltInNumSubgroups = 38, + BuiltInNumEnqueuedSubgroups = 39, + BuiltInSubgroupId = 40, + BuiltInSubgroupLocalInvocationId = 41, + BuiltInVertexIndex = 42, + BuiltInInstanceIndex = 43, + BuiltInCoreIDARM = 4160, + BuiltInCoreCountARM = 4161, + BuiltInCoreMaxIDARM = 4162, + BuiltInWarpIDARM = 4163, + BuiltInWarpMaxIDARM = 4164, + BuiltInSubgroupEqMask = 4416, + BuiltInSubgroupEqMaskKHR = 4416, + BuiltInSubgroupGeMask = 4417, + BuiltInSubgroupGeMaskKHR = 4417, + BuiltInSubgroupGtMask = 4418, + BuiltInSubgroupGtMaskKHR = 4418, + BuiltInSubgroupLeMask = 4419, + BuiltInSubgroupLeMaskKHR = 4419, + BuiltInSubgroupLtMask = 4420, + BuiltInSubgroupLtMaskKHR = 4420, + BuiltInBaseVertex = 4424, + BuiltInBaseInstance = 4425, + BuiltInDrawIndex = 4426, + BuiltInPrimitiveShadingRateKHR = 4432, + BuiltInDeviceIndex = 4438, + BuiltInViewIndex = 4440, + BuiltInShadingRateKHR = 4444, + BuiltInTileOffsetQCOM = 4492, + BuiltInTileDimensionQCOM = 4493, + BuiltInTileApronSizeQCOM = 4494, + BuiltInBaryCoordNoPerspAMD = 4992, + BuiltInBaryCoordNoPerspCentroidAMD = 4993, + BuiltInBaryCoordNoPerspSampleAMD = 4994, + BuiltInBaryCoordSmoothAMD = 4995, + BuiltInBaryCoordSmoothCentroidAMD = 4996, + BuiltInBaryCoordSmoothSampleAMD = 4997, + BuiltInBaryCoordPullModelAMD = 4998, + BuiltInFragStencilRefEXT = 5014, + BuiltInRemainingRecursionLevelsAMDX = 5021, + BuiltInShaderIndexAMDX = 5073, + BuiltInSamplerHeapEXT = 5122, + BuiltInResourceHeapEXT = 5123, + BuiltInViewportMaskNV = 5253, + BuiltInSecondaryPositionNV = 5257, + BuiltInSecondaryViewportMaskNV = 5258, + BuiltInPositionPerViewNV = 5261, + BuiltInViewportMaskPerViewNV = 5262, + BuiltInFullyCoveredEXT = 5264, + BuiltInTaskCountNV = 5274, + BuiltInPrimitiveCountNV = 5275, + BuiltInPrimitiveIndicesNV = 5276, + BuiltInClipDistancePerViewNV = 5277, + BuiltInCullDistancePerViewNV = 5278, + BuiltInLayerPerViewNV = 5279, + BuiltInMeshViewCountNV = 5280, + BuiltInMeshViewIndicesNV = 5281, + BuiltInBaryCoordKHR = 5286, + BuiltInBaryCoordNV = 5286, + BuiltInBaryCoordNoPerspKHR = 5287, + BuiltInBaryCoordNoPerspNV = 5287, + BuiltInFragSizeEXT = 5292, + BuiltInFragmentSizeNV = 5292, + BuiltInFragInvocationCountEXT = 5293, + BuiltInInvocationsPerPixelNV = 5293, + BuiltInPrimitivePointIndicesEXT = 5294, + BuiltInPrimitiveLineIndicesEXT = 5295, + BuiltInPrimitiveTriangleIndicesEXT = 5296, + BuiltInCullPrimitiveEXT = 5299, + BuiltInLaunchIdKHR = 5319, + BuiltInLaunchIdNV = 5319, + BuiltInLaunchSizeKHR = 5320, + BuiltInLaunchSizeNV = 5320, + BuiltInWorldRayOriginKHR = 5321, + BuiltInWorldRayOriginNV = 5321, + BuiltInWorldRayDirectionKHR = 5322, + BuiltInWorldRayDirectionNV = 5322, + BuiltInObjectRayOriginKHR = 5323, + BuiltInObjectRayOriginNV = 5323, + BuiltInObjectRayDirectionKHR = 5324, + BuiltInObjectRayDirectionNV = 5324, + BuiltInRayTminKHR = 5325, + BuiltInRayTminNV = 5325, + BuiltInRayTmaxKHR = 5326, + BuiltInRayTmaxNV = 5326, + BuiltInInstanceCustomIndexKHR = 5327, + BuiltInInstanceCustomIndexNV = 5327, + BuiltInObjectToWorldKHR = 5330, + BuiltInObjectToWorldNV = 5330, + BuiltInWorldToObjectKHR = 5331, + BuiltInWorldToObjectNV = 5331, + BuiltInHitTNV = 5332, + BuiltInHitKindKHR = 5333, + BuiltInHitKindNV = 5333, + BuiltInCurrentRayTimeNV = 5334, + BuiltInHitTriangleVertexPositionsKHR = 5335, + BuiltInHitMicroTriangleVertexPositionsNV = 5337, + BuiltInHitMicroTriangleVertexBarycentricsNV = 5344, + BuiltInIncomingRayFlagsKHR = 5351, + BuiltInIncomingRayFlagsNV = 5351, + BuiltInRayGeometryIndexKHR = 5352, + BuiltInHitIsSphereNV = 5359, + BuiltInHitIsLSSNV = 5360, + BuiltInHitSpherePositionNV = 5361, + BuiltInWarpsPerSMNV = 5374, + BuiltInSMCountNV = 5375, + BuiltInWarpIDNV = 5376, + BuiltInSMIDNV = 5377, + BuiltInHitLSSPositionsNV = 5396, + BuiltInHitKindFrontFacingMicroTriangleNV = 5405, + BuiltInHitKindBackFacingMicroTriangleNV = 5406, + BuiltInHitSphereRadiusNV = 5420, + BuiltInHitLSSRadiiNV = 5421, + BuiltInClusterIDNV = 5436, + BuiltInCullMaskKHR = 6021, + BuiltInMax = 0x7fffffff, +}; + +enum SelectionControlShift { + SelectionControlFlattenShift = 0, + SelectionControlDontFlattenShift = 1, + SelectionControlMax = 0x7fffffff, +}; + +enum SelectionControlMask { + SelectionControlMaskNone = 0, + SelectionControlFlattenMask = 0x00000001, + SelectionControlDontFlattenMask = 0x00000002, +}; + +enum LoopControlShift { + LoopControlUnrollShift = 0, + LoopControlDontUnrollShift = 1, + LoopControlDependencyInfiniteShift = 2, + LoopControlDependencyLengthShift = 3, + LoopControlMinIterationsShift = 4, + LoopControlMaxIterationsShift = 5, + LoopControlIterationMultipleShift = 6, + LoopControlPeelCountShift = 7, + LoopControlPartialCountShift = 8, + LoopControlInitiationIntervalALTERAShift = 16, + LoopControlInitiationIntervalINTELShift = 16, + LoopControlMaxConcurrencyALTERAShift = 17, + LoopControlMaxConcurrencyINTELShift = 17, + LoopControlDependencyArrayALTERAShift = 18, + LoopControlDependencyArrayINTELShift = 18, + LoopControlPipelineEnableALTERAShift = 19, + LoopControlPipelineEnableINTELShift = 19, + LoopControlLoopCoalesceALTERAShift = 20, + LoopControlLoopCoalesceINTELShift = 20, + LoopControlMaxInterleavingALTERAShift = 21, + LoopControlMaxInterleavingINTELShift = 21, + LoopControlSpeculatedIterationsALTERAShift = 22, + LoopControlSpeculatedIterationsINTELShift = 22, + LoopControlNoFusionALTERAShift = 23, + LoopControlNoFusionINTELShift = 23, + LoopControlLoopCountALTERAShift = 24, + LoopControlLoopCountINTELShift = 24, + LoopControlMaxReinvocationDelayALTERAShift = 25, + LoopControlMaxReinvocationDelayINTELShift = 25, + LoopControlMax = 0x7fffffff, +}; + +enum LoopControlMask { + LoopControlMaskNone = 0, + LoopControlUnrollMask = 0x00000001, + LoopControlDontUnrollMask = 0x00000002, + LoopControlDependencyInfiniteMask = 0x00000004, + LoopControlDependencyLengthMask = 0x00000008, + LoopControlMinIterationsMask = 0x00000010, + LoopControlMaxIterationsMask = 0x00000020, + LoopControlIterationMultipleMask = 0x00000040, + LoopControlPeelCountMask = 0x00000080, + LoopControlPartialCountMask = 0x00000100, + LoopControlInitiationIntervalALTERAMask = 0x00010000, + LoopControlInitiationIntervalINTELMask = 0x00010000, + LoopControlMaxConcurrencyALTERAMask = 0x00020000, + LoopControlMaxConcurrencyINTELMask = 0x00020000, + LoopControlDependencyArrayALTERAMask = 0x00040000, + LoopControlDependencyArrayINTELMask = 0x00040000, + LoopControlPipelineEnableALTERAMask = 0x00080000, + LoopControlPipelineEnableINTELMask = 0x00080000, + LoopControlLoopCoalesceALTERAMask = 0x00100000, + LoopControlLoopCoalesceINTELMask = 0x00100000, + LoopControlMaxInterleavingALTERAMask = 0x00200000, + LoopControlMaxInterleavingINTELMask = 0x00200000, + LoopControlSpeculatedIterationsALTERAMask = 0x00400000, + LoopControlSpeculatedIterationsINTELMask = 0x00400000, + LoopControlNoFusionALTERAMask = 0x00800000, + LoopControlNoFusionINTELMask = 0x00800000, + LoopControlLoopCountALTERAMask = 0x01000000, + LoopControlLoopCountINTELMask = 0x01000000, + LoopControlMaxReinvocationDelayALTERAMask = 0x02000000, + LoopControlMaxReinvocationDelayINTELMask = 0x02000000, +}; + +enum FunctionControlShift { + FunctionControlInlineShift = 0, + FunctionControlDontInlineShift = 1, + FunctionControlPureShift = 2, + FunctionControlConstShift = 3, + FunctionControlOptNoneEXTShift = 16, + FunctionControlOptNoneINTELShift = 16, + FunctionControlMax = 0x7fffffff, +}; + +enum FunctionControlMask { + FunctionControlMaskNone = 0, + FunctionControlInlineMask = 0x00000001, + FunctionControlDontInlineMask = 0x00000002, + FunctionControlPureMask = 0x00000004, + FunctionControlConstMask = 0x00000008, + FunctionControlOptNoneEXTMask = 0x00010000, + FunctionControlOptNoneINTELMask = 0x00010000, +}; + +enum MemorySemanticsShift { + MemorySemanticsAcquireShift = 1, + MemorySemanticsReleaseShift = 2, + MemorySemanticsAcquireReleaseShift = 3, + MemorySemanticsSequentiallyConsistentShift = 4, + MemorySemanticsUniformMemoryShift = 6, + MemorySemanticsSubgroupMemoryShift = 7, + MemorySemanticsWorkgroupMemoryShift = 8, + MemorySemanticsCrossWorkgroupMemoryShift = 9, + MemorySemanticsAtomicCounterMemoryShift = 10, + MemorySemanticsImageMemoryShift = 11, + MemorySemanticsOutputMemoryShift = 12, + MemorySemanticsOutputMemoryKHRShift = 12, + MemorySemanticsMakeAvailableShift = 13, + MemorySemanticsMakeAvailableKHRShift = 13, + MemorySemanticsMakeVisibleShift = 14, + MemorySemanticsMakeVisibleKHRShift = 14, + MemorySemanticsVolatileShift = 15, + MemorySemanticsMax = 0x7fffffff, +}; + +enum MemorySemanticsMask { + MemorySemanticsMaskNone = 0, + MemorySemanticsAcquireMask = 0x00000002, + MemorySemanticsReleaseMask = 0x00000004, + MemorySemanticsAcquireReleaseMask = 0x00000008, + MemorySemanticsSequentiallyConsistentMask = 0x00000010, + MemorySemanticsUniformMemoryMask = 0x00000040, + MemorySemanticsSubgroupMemoryMask = 0x00000080, + MemorySemanticsWorkgroupMemoryMask = 0x00000100, + MemorySemanticsCrossWorkgroupMemoryMask = 0x00000200, + MemorySemanticsAtomicCounterMemoryMask = 0x00000400, + MemorySemanticsImageMemoryMask = 0x00000800, + MemorySemanticsOutputMemoryMask = 0x00001000, + MemorySemanticsOutputMemoryKHRMask = 0x00001000, + MemorySemanticsMakeAvailableMask = 0x00002000, + MemorySemanticsMakeAvailableKHRMask = 0x00002000, + MemorySemanticsMakeVisibleMask = 0x00004000, + MemorySemanticsMakeVisibleKHRMask = 0x00004000, + MemorySemanticsVolatileMask = 0x00008000, +}; + +enum MemoryAccessShift { + MemoryAccessVolatileShift = 0, + MemoryAccessAlignedShift = 1, + MemoryAccessNontemporalShift = 2, + MemoryAccessMakePointerAvailableShift = 3, + MemoryAccessMakePointerAvailableKHRShift = 3, + MemoryAccessMakePointerVisibleShift = 4, + MemoryAccessMakePointerVisibleKHRShift = 4, + MemoryAccessNonPrivatePointerShift = 5, + MemoryAccessNonPrivatePointerKHRShift = 5, + MemoryAccessAliasScopeINTELMaskShift = 16, + MemoryAccessNoAliasINTELMaskShift = 17, + MemoryAccessMax = 0x7fffffff, +}; + +enum MemoryAccessMask { + MemoryAccessMaskNone = 0, + MemoryAccessVolatileMask = 0x00000001, + MemoryAccessAlignedMask = 0x00000002, + MemoryAccessNontemporalMask = 0x00000004, + MemoryAccessMakePointerAvailableMask = 0x00000008, + MemoryAccessMakePointerAvailableKHRMask = 0x00000008, + MemoryAccessMakePointerVisibleMask = 0x00000010, + MemoryAccessMakePointerVisibleKHRMask = 0x00000010, + MemoryAccessNonPrivatePointerMask = 0x00000020, + MemoryAccessNonPrivatePointerKHRMask = 0x00000020, + MemoryAccessAliasScopeINTELMaskMask = 0x00010000, + MemoryAccessNoAliasINTELMaskMask = 0x00020000, +}; + +enum Scope { + ScopeCrossDevice = 0, + ScopeDevice = 1, + ScopeWorkgroup = 2, + ScopeSubgroup = 3, + ScopeInvocation = 4, + ScopeQueueFamily = 5, + ScopeQueueFamilyKHR = 5, + ScopeShaderCallKHR = 6, + ScopeMax = 0x7fffffff, +}; + +enum GroupOperation { + GroupOperationReduce = 0, + GroupOperationInclusiveScan = 1, + GroupOperationExclusiveScan = 2, + GroupOperationClusteredReduce = 3, + GroupOperationPartitionedReduceEXT = 6, + GroupOperationPartitionedReduceNV = 6, + GroupOperationPartitionedInclusiveScanEXT = 7, + GroupOperationPartitionedInclusiveScanNV = 7, + GroupOperationPartitionedExclusiveScanEXT = 8, + GroupOperationPartitionedExclusiveScanNV = 8, + GroupOperationMax = 0x7fffffff, +}; + +enum KernelEnqueueFlags { + KernelEnqueueFlagsNoWait = 0, + KernelEnqueueFlagsWaitKernel = 1, + KernelEnqueueFlagsWaitWorkGroup = 2, + KernelEnqueueFlagsMax = 0x7fffffff, +}; + +enum KernelProfilingInfoShift { + KernelProfilingInfoCmdExecTimeShift = 0, + KernelProfilingInfoMax = 0x7fffffff, +}; + +enum KernelProfilingInfoMask { + KernelProfilingInfoMaskNone = 0, + KernelProfilingInfoCmdExecTimeMask = 0x00000001, +}; + +enum Capability { + CapabilityMatrix = 0, + CapabilityShader = 1, + CapabilityGeometry = 2, + CapabilityTessellation = 3, + CapabilityAddresses = 4, + CapabilityLinkage = 5, + CapabilityKernel = 6, + CapabilityVector16 = 7, + CapabilityFloat16Buffer = 8, + CapabilityFloat16 = 9, + CapabilityFloat64 = 10, + CapabilityInt64 = 11, + CapabilityInt64Atomics = 12, + CapabilityImageBasic = 13, + CapabilityImageReadWrite = 14, + CapabilityImageMipmap = 15, + CapabilityPipes = 17, + CapabilityGroups = 18, + CapabilityDeviceEnqueue = 19, + CapabilityLiteralSampler = 20, + CapabilityAtomicStorage = 21, + CapabilityInt16 = 22, + CapabilityTessellationPointSize = 23, + CapabilityGeometryPointSize = 24, + CapabilityImageGatherExtended = 25, + CapabilityStorageImageMultisample = 27, + CapabilityUniformBufferArrayDynamicIndexing = 28, + CapabilitySampledImageArrayDynamicIndexing = 29, + CapabilityStorageBufferArrayDynamicIndexing = 30, + CapabilityStorageImageArrayDynamicIndexing = 31, + CapabilityClipDistance = 32, + CapabilityCullDistance = 33, + CapabilityImageCubeArray = 34, + CapabilitySampleRateShading = 35, + CapabilityImageRect = 36, + CapabilitySampledRect = 37, + CapabilityGenericPointer = 38, + CapabilityInt8 = 39, + CapabilityInputAttachment = 40, + CapabilitySparseResidency = 41, + CapabilityMinLod = 42, + CapabilitySampled1D = 43, + CapabilityImage1D = 44, + CapabilitySampledCubeArray = 45, + CapabilitySampledBuffer = 46, + CapabilityImageBuffer = 47, + CapabilityImageMSArray = 48, + CapabilityStorageImageExtendedFormats = 49, + CapabilityImageQuery = 50, + CapabilityDerivativeControl = 51, + CapabilityInterpolationFunction = 52, + CapabilityTransformFeedback = 53, + CapabilityGeometryStreams = 54, + CapabilityStorageImageReadWithoutFormat = 55, + CapabilityStorageImageWriteWithoutFormat = 56, + CapabilityMultiViewport = 57, + CapabilitySubgroupDispatch = 58, + CapabilityNamedBarrier = 59, + CapabilityPipeStorage = 60, + CapabilityGroupNonUniform = 61, + CapabilityGroupNonUniformVote = 62, + CapabilityGroupNonUniformArithmetic = 63, + CapabilityGroupNonUniformBallot = 64, + CapabilityGroupNonUniformShuffle = 65, + CapabilityGroupNonUniformShuffleRelative = 66, + CapabilityGroupNonUniformClustered = 67, + CapabilityGroupNonUniformQuad = 68, + CapabilityShaderLayer = 69, + CapabilityShaderViewportIndex = 70, + CapabilityUniformDecoration = 71, + CapabilityCoreBuiltinsARM = 4165, + CapabilityTileImageColorReadAccessEXT = 4166, + CapabilityTileImageDepthReadAccessEXT = 4167, + CapabilityTileImageStencilReadAccessEXT = 4168, + CapabilityTensorsARM = 4174, + CapabilityStorageTensorArrayDynamicIndexingARM = 4175, + CapabilityStorageTensorArrayNonUniformIndexingARM = 4176, + CapabilityGraphARM = 4191, + CapabilityCooperativeMatrixLayoutsARM = 4201, + CapabilityFloat8EXT = 4212, + CapabilityFloat8CooperativeMatrixEXT = 4213, + CapabilityFragmentShadingRateKHR = 4422, + CapabilitySubgroupBallotKHR = 4423, + CapabilityDrawParameters = 4427, + CapabilityWorkgroupMemoryExplicitLayoutKHR = 4428, + CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR = 4429, + CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR = 4430, + CapabilitySubgroupVoteKHR = 4431, + CapabilityStorageBuffer16BitAccess = 4433, + CapabilityStorageUniformBufferBlock16 = 4433, + CapabilityStorageUniform16 = 4434, + CapabilityUniformAndStorageBuffer16BitAccess = 4434, + CapabilityStoragePushConstant16 = 4435, + CapabilityStorageInputOutput16 = 4436, + CapabilityDeviceGroup = 4437, + CapabilityMultiView = 4439, + CapabilityVariablePointersStorageBuffer = 4441, + CapabilityVariablePointers = 4442, + CapabilityAtomicStorageOps = 4445, + CapabilitySampleMaskPostDepthCoverage = 4447, + CapabilityStorageBuffer8BitAccess = 4448, + CapabilityUniformAndStorageBuffer8BitAccess = 4449, + CapabilityStoragePushConstant8 = 4450, + CapabilityDenormPreserve = 4464, + CapabilityDenormFlushToZero = 4465, + CapabilitySignedZeroInfNanPreserve = 4466, + CapabilityRoundingModeRTE = 4467, + CapabilityRoundingModeRTZ = 4468, + CapabilityRayQueryProvisionalKHR = 4471, + CapabilityRayQueryKHR = 4472, + CapabilityUntypedPointersKHR = 4473, + CapabilityRayTraversalPrimitiveCullingKHR = 4478, + CapabilityRayTracingKHR = 4479, + CapabilityTextureSampleWeightedQCOM = 4484, + CapabilityTextureBoxFilterQCOM = 4485, + CapabilityTextureBlockMatchQCOM = 4486, + CapabilityTileShadingQCOM = 4495, + CapabilityCooperativeMatrixConversionQCOM = 4496, + CapabilityTextureBlockMatch2QCOM = 4498, + CapabilityFloat16ImageAMD = 5008, + CapabilityImageGatherBiasLodAMD = 5009, + CapabilityFragmentMaskAMD = 5010, + CapabilityStencilExportEXT = 5013, + CapabilityImageReadWriteLodAMD = 5015, + CapabilityInt64ImageEXT = 5016, + CapabilityShaderClockKHR = 5055, + CapabilityShaderEnqueueAMDX = 5067, + CapabilityQuadControlKHR = 5087, + CapabilityInt4TypeINTEL = 5112, + CapabilityInt4CooperativeMatrixINTEL = 5114, + CapabilityBFloat16TypeKHR = 5116, + CapabilityBFloat16DotProductKHR = 5117, + CapabilityBFloat16CooperativeMatrixKHR = 5118, + CapabilityAbortKHR = 5120, + CapabilityDescriptorHeapEXT = 5128, + CapabilityConstantDataKHR = 5146, + CapabilityPoisonFreezeKHR = 5156, + CapabilitySampleMaskOverrideCoverageNV = 5249, + CapabilityGeometryShaderPassthroughNV = 5251, + CapabilityShaderViewportIndexLayerEXT = 5254, + CapabilityShaderViewportIndexLayerNV = 5254, + CapabilityShaderViewportMaskNV = 5255, + CapabilityShaderStereoViewNV = 5259, + CapabilityPerViewAttributesNV = 5260, + CapabilityFragmentFullyCoveredEXT = 5265, + CapabilityMeshShadingNV = 5266, + CapabilityImageFootprintNV = 5282, + CapabilityMeshShadingEXT = 5283, + CapabilityFragmentBarycentricKHR = 5284, + CapabilityFragmentBarycentricNV = 5284, + CapabilityComputeDerivativeGroupQuadsKHR = 5288, + CapabilityComputeDerivativeGroupQuadsNV = 5288, + CapabilityFragmentDensityEXT = 5291, + CapabilityShadingRateNV = 5291, + CapabilityGroupNonUniformPartitionedEXT = 5297, + CapabilityGroupNonUniformPartitionedNV = 5297, + CapabilityShaderNonUniform = 5301, + CapabilityShaderNonUniformEXT = 5301, + CapabilityRuntimeDescriptorArray = 5302, + CapabilityRuntimeDescriptorArrayEXT = 5302, + CapabilityInputAttachmentArrayDynamicIndexing = 5303, + CapabilityInputAttachmentArrayDynamicIndexingEXT = 5303, + CapabilityUniformTexelBufferArrayDynamicIndexing = 5304, + CapabilityUniformTexelBufferArrayDynamicIndexingEXT = 5304, + CapabilityStorageTexelBufferArrayDynamicIndexing = 5305, + CapabilityStorageTexelBufferArrayDynamicIndexingEXT = 5305, + CapabilityUniformBufferArrayNonUniformIndexing = 5306, + CapabilityUniformBufferArrayNonUniformIndexingEXT = 5306, + CapabilitySampledImageArrayNonUniformIndexing = 5307, + CapabilitySampledImageArrayNonUniformIndexingEXT = 5307, + CapabilityStorageBufferArrayNonUniformIndexing = 5308, + CapabilityStorageBufferArrayNonUniformIndexingEXT = 5308, + CapabilityStorageImageArrayNonUniformIndexing = 5309, + CapabilityStorageImageArrayNonUniformIndexingEXT = 5309, + CapabilityInputAttachmentArrayNonUniformIndexing = 5310, + CapabilityInputAttachmentArrayNonUniformIndexingEXT = 5310, + CapabilityUniformTexelBufferArrayNonUniformIndexing = 5311, + CapabilityUniformTexelBufferArrayNonUniformIndexingEXT = 5311, + CapabilityStorageTexelBufferArrayNonUniformIndexing = 5312, + CapabilityStorageTexelBufferArrayNonUniformIndexingEXT = 5312, + CapabilityRayTracingPositionFetchKHR = 5336, + CapabilityRayTracingNV = 5340, + CapabilityRayTracingMotionBlurNV = 5341, + CapabilityVulkanMemoryModel = 5345, + CapabilityVulkanMemoryModelKHR = 5345, + CapabilityVulkanMemoryModelDeviceScope = 5346, + CapabilityVulkanMemoryModelDeviceScopeKHR = 5346, + CapabilityPhysicalStorageBufferAddresses = 5347, + CapabilityPhysicalStorageBufferAddressesEXT = 5347, + CapabilityComputeDerivativeGroupLinearKHR = 5350, + CapabilityComputeDerivativeGroupLinearNV = 5350, + CapabilityRayTracingProvisionalKHR = 5353, + CapabilityCooperativeMatrixNV = 5357, + CapabilityFragmentShaderSampleInterlockEXT = 5363, + CapabilityFragmentShaderShadingRateInterlockEXT = 5372, + CapabilityShaderSMBuiltinsNV = 5373, + CapabilityFragmentShaderPixelInterlockEXT = 5378, + CapabilityDemoteToHelperInvocation = 5379, + CapabilityDemoteToHelperInvocationEXT = 5379, + CapabilityDisplacementMicromapNV = 5380, + CapabilityRayTracingOpacityMicromapEXT = 5381, + CapabilityShaderInvocationReorderNV = 5383, + CapabilityShaderInvocationReorderEXT = 5388, + CapabilityBindlessTextureNV = 5390, + CapabilityRayQueryPositionFetchKHR = 5391, + CapabilityCooperativeVectorNV = 5394, + CapabilityAtomicFloat16VectorNV = 5404, + CapabilityRayTracingDisplacementMicromapNV = 5409, + CapabilityRawAccessChainsNV = 5414, + CapabilityRayTracingSpheresGeometryNV = 5418, + CapabilityRayTracingLinearSweptSpheresGeometryNV = 5419, + CapabilityPushConstantBanksNV = 5423, + CapabilityLongVectorEXT = 5425, + CapabilityShader64BitIndexingEXT = 5426, + CapabilityCooperativeMatrixReductionsNV = 5430, + CapabilityCooperativeMatrixConversionsNV = 5431, + CapabilityCooperativeMatrixPerElementOperationsNV = 5432, + CapabilityCooperativeMatrixTensorAddressingNV = 5433, + CapabilityCooperativeMatrixBlockLoadsNV = 5434, + CapabilityCooperativeVectorTrainingNV = 5435, + CapabilityRayTracingClusterAccelerationStructureNV = 5437, + CapabilityTensorAddressingNV = 5439, + CapabilitySubgroupShuffleINTEL = 5568, + CapabilitySubgroupBufferBlockIOINTEL = 5569, + CapabilitySubgroupImageBlockIOINTEL = 5570, + CapabilitySubgroupImageMediaBlockIOINTEL = 5579, + CapabilityRoundToInfinityINTEL = 5582, + CapabilityFloatingPointModeINTEL = 5583, + CapabilityIntegerFunctions2INTEL = 5584, + CapabilityFunctionPointersINTEL = 5603, + CapabilityIndirectReferencesINTEL = 5604, + CapabilityAsmINTEL = 5606, + CapabilityAtomicFloat32MinMaxEXT = 5612, + CapabilityAtomicFloat64MinMaxEXT = 5613, + CapabilityAtomicFloat16MinMaxEXT = 5616, + CapabilityVectorComputeINTEL = 5617, + CapabilityVectorAnyINTEL = 5619, + CapabilityExpectAssumeKHR = 5629, + CapabilitySubgroupAvcMotionEstimationINTEL = 5696, + CapabilitySubgroupAvcMotionEstimationIntraINTEL = 5697, + CapabilitySubgroupAvcMotionEstimationChromaINTEL = 5698, + CapabilityVariableLengthArrayINTEL = 5817, + CapabilityFunctionFloatControlINTEL = 5821, + CapabilityFPGAMemoryAttributesALTERA = 5824, + CapabilityFPGAMemoryAttributesINTEL = 5824, + CapabilityFPFastMathModeINTEL = 5837, + CapabilityArbitraryPrecisionIntegersALTERA = 5844, + CapabilityArbitraryPrecisionIntegersINTEL = 5844, + CapabilityArbitraryPrecisionFloatingPointALTERA = 5845, + CapabilityArbitraryPrecisionFloatingPointINTEL = 5845, + CapabilityUnstructuredLoopControlsINTEL = 5886, + CapabilityFPGALoopControlsALTERA = 5888, + CapabilityFPGALoopControlsINTEL = 5888, + CapabilityKernelAttributesINTEL = 5892, + CapabilityFPGAKernelAttributesINTEL = 5897, + CapabilityFPGAMemoryAccessesALTERA = 5898, + CapabilityFPGAMemoryAccessesINTEL = 5898, + CapabilityFPGAClusterAttributesALTERA = 5904, + CapabilityFPGAClusterAttributesINTEL = 5904, + CapabilityLoopFuseALTERA = 5906, + CapabilityLoopFuseINTEL = 5906, + CapabilityFPGADSPControlALTERA = 5908, + CapabilityFPGADSPControlINTEL = 5908, + CapabilityMemoryAccessAliasingINTEL = 5910, + CapabilityFPGAInvocationPipeliningAttributesALTERA = 5916, + CapabilityFPGAInvocationPipeliningAttributesINTEL = 5916, + CapabilityFPGABufferLocationALTERA = 5920, + CapabilityFPGABufferLocationINTEL = 5920, + CapabilityArbitraryPrecisionFixedPointALTERA = 5922, + CapabilityArbitraryPrecisionFixedPointINTEL = 5922, + CapabilityUSMStorageClassesALTERA = 5935, + CapabilityUSMStorageClassesINTEL = 5935, + CapabilityRuntimeAlignedAttributeALTERA = 5939, + CapabilityRuntimeAlignedAttributeINTEL = 5939, + CapabilityIOPipesALTERA = 5943, + CapabilityIOPipesINTEL = 5943, + CapabilityBlockingPipesALTERA = 5945, + CapabilityBlockingPipesINTEL = 5945, + CapabilityFPGARegALTERA = 5948, + CapabilityFPGARegINTEL = 5948, + CapabilityDotProductInputAll = 6016, + CapabilityDotProductInputAllKHR = 6016, + CapabilityDotProductInput4x8Bit = 6017, + CapabilityDotProductInput4x8BitKHR = 6017, + CapabilityDotProductInput4x8BitPacked = 6018, + CapabilityDotProductInput4x8BitPackedKHR = 6018, + CapabilityDotProduct = 6019, + CapabilityDotProductKHR = 6019, + CapabilityRayCullMaskKHR = 6020, + CapabilityCooperativeMatrixKHR = 6022, + CapabilityReplicatedCompositesEXT = 6024, + CapabilityBitInstructions = 6025, + CapabilityGroupNonUniformRotateKHR = 6026, + CapabilityFloatControls2 = 6029, + CapabilityFMAKHR = 6030, + CapabilityAtomicFloat32AddEXT = 6033, + CapabilityAtomicFloat64AddEXT = 6034, + CapabilityLongCompositesINTEL = 6089, + CapabilityOptNoneEXT = 6094, + CapabilityOptNoneINTEL = 6094, + CapabilityAtomicFloat16AddEXT = 6095, + CapabilityDebugInfoModuleINTEL = 6114, + CapabilityBFloat16ConversionINTEL = 6115, + CapabilitySplitBarrierINTEL = 6141, + CapabilityArithmeticFenceEXT = 6144, + CapabilityFPGAClusterAttributesV2ALTERA = 6150, + CapabilityFPGAClusterAttributesV2INTEL = 6150, + CapabilityFPGAKernelAttributesv2INTEL = 6161, + CapabilityTaskSequenceALTERA = 6162, + CapabilityTaskSequenceINTEL = 6162, + CapabilityFPMaxErrorINTEL = 6169, + CapabilityFPGALatencyControlALTERA = 6171, + CapabilityFPGALatencyControlINTEL = 6171, + CapabilityFPGAArgumentInterfacesALTERA = 6174, + CapabilityFPGAArgumentInterfacesINTEL = 6174, + CapabilityGlobalVariableHostAccessINTEL = 6187, + CapabilityGlobalVariableFPGADecorationsALTERA = 6189, + CapabilityGlobalVariableFPGADecorationsINTEL = 6189, + CapabilitySubgroupBufferPrefetchINTEL = 6220, + CapabilitySubgroup2DBlockIOINTEL = 6228, + CapabilitySubgroup2DBlockTransformINTEL = 6229, + CapabilitySubgroup2DBlockTransposeINTEL = 6230, + CapabilitySubgroupMatrixMultiplyAccumulateINTEL = 6236, + CapabilityTernaryBitwiseFunctionINTEL = 6241, + CapabilityUntypedVariableLengthArrayINTEL = 6243, + CapabilitySpecConditionalINTEL = 6245, + CapabilityFunctionVariantsINTEL = 6246, + CapabilityGroupUniformArithmeticKHR = 6400, + CapabilityTensorFloat32RoundingINTEL = 6425, + CapabilityMaskedGatherScatterINTEL = 6427, + CapabilityCacheControlsINTEL = 6441, + CapabilityRegisterLimitsINTEL = 6460, + CapabilityBindlessImagesINTEL = 6528, + CapabilityDotProductFloat16AccFloat32VALVE = 6912, + CapabilityDotProductFloat16AccFloat16VALVE = 6913, + CapabilityDotProductBFloat16AccVALVE = 6914, + CapabilityDotProductFloat8AccFloat32VALVE = 6915, + CapabilityMax = 0x7fffffff, +}; + +enum RayFlagsShift { + RayFlagsOpaqueKHRShift = 0, + RayFlagsNoOpaqueKHRShift = 1, + RayFlagsTerminateOnFirstHitKHRShift = 2, + RayFlagsSkipClosestHitShaderKHRShift = 3, + RayFlagsCullBackFacingTrianglesKHRShift = 4, + RayFlagsCullFrontFacingTrianglesKHRShift = 5, + RayFlagsCullOpaqueKHRShift = 6, + RayFlagsCullNoOpaqueKHRShift = 7, + RayFlagsSkipBuiltinPrimitivesNVShift = 8, + RayFlagsSkipTrianglesKHRShift = 8, + RayFlagsSkipAABBsKHRShift = 9, + RayFlagsForceOpacityMicromap2StateEXTShift = 10, + RayFlagsMax = 0x7fffffff, +}; + +enum RayFlagsMask { + RayFlagsMaskNone = 0, + RayFlagsOpaqueKHRMask = 0x00000001, + RayFlagsNoOpaqueKHRMask = 0x00000002, + RayFlagsTerminateOnFirstHitKHRMask = 0x00000004, + RayFlagsSkipClosestHitShaderKHRMask = 0x00000008, + RayFlagsCullBackFacingTrianglesKHRMask = 0x00000010, + RayFlagsCullFrontFacingTrianglesKHRMask = 0x00000020, + RayFlagsCullOpaqueKHRMask = 0x00000040, + RayFlagsCullNoOpaqueKHRMask = 0x00000080, + RayFlagsSkipBuiltinPrimitivesNVMask = 0x00000100, + RayFlagsSkipTrianglesKHRMask = 0x00000100, + RayFlagsSkipAABBsKHRMask = 0x00000200, + RayFlagsForceOpacityMicromap2StateEXTMask = 0x00000400, +}; + +enum RayQueryIntersection { + RayQueryIntersectionRayQueryCandidateIntersectionKHR = 0, + RayQueryIntersectionRayQueryCommittedIntersectionKHR = 1, + RayQueryIntersectionMax = 0x7fffffff, +}; + +enum RayQueryCommittedIntersectionType { + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR = 0, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR = 1, + RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR = 2, + RayQueryCommittedIntersectionTypeMax = 0x7fffffff, +}; + +enum RayQueryCandidateIntersectionType { + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR = 0, + RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR = 1, + RayQueryCandidateIntersectionTypeMax = 0x7fffffff, +}; + +enum FragmentShadingRateShift { + FragmentShadingRateVertical2PixelsShift = 0, + FragmentShadingRateVertical4PixelsShift = 1, + FragmentShadingRateHorizontal2PixelsShift = 2, + FragmentShadingRateHorizontal4PixelsShift = 3, + FragmentShadingRateMax = 0x7fffffff, +}; + +enum FragmentShadingRateMask { + FragmentShadingRateMaskNone = 0, + FragmentShadingRateVertical2PixelsMask = 0x00000001, + FragmentShadingRateVertical4PixelsMask = 0x00000002, + FragmentShadingRateHorizontal2PixelsMask = 0x00000004, + FragmentShadingRateHorizontal4PixelsMask = 0x00000008, +}; + +enum FPDenormMode { + FPDenormModePreserve = 0, + FPDenormModeFlushToZero = 1, + FPDenormModeMax = 0x7fffffff, +}; + +enum FPOperationMode { + FPOperationModeIEEE = 0, + FPOperationModeALT = 1, + FPOperationModeMax = 0x7fffffff, +}; + +enum QuantizationModes { + QuantizationModesTRN = 0, + QuantizationModesTRN_ZERO = 1, + QuantizationModesRND = 2, + QuantizationModesRND_ZERO = 3, + QuantizationModesRND_INF = 4, + QuantizationModesRND_MIN_INF = 5, + QuantizationModesRND_CONV = 6, + QuantizationModesRND_CONV_ODD = 7, + QuantizationModesMax = 0x7fffffff, +}; + +enum OverflowModes { + OverflowModesWRAP = 0, + OverflowModesSAT = 1, + OverflowModesSAT_ZERO = 2, + OverflowModesSAT_SYM = 3, + OverflowModesMax = 0x7fffffff, +}; + +enum PackedVectorFormat { + PackedVectorFormatPackedVectorFormat4x8Bit = 0, + PackedVectorFormatPackedVectorFormat4x8BitKHR = 0, + PackedVectorFormatMax = 0x7fffffff, +}; + +enum CooperativeMatrixOperandsShift { + CooperativeMatrixOperandsMatrixASignedComponentsKHRShift = 0, + CooperativeMatrixOperandsMatrixBSignedComponentsKHRShift = 1, + CooperativeMatrixOperandsMatrixCSignedComponentsKHRShift = 2, + CooperativeMatrixOperandsMatrixResultSignedComponentsKHRShift = 3, + CooperativeMatrixOperandsSaturatingAccumulationKHRShift = 4, + CooperativeMatrixOperandsMax = 0x7fffffff, +}; + +enum CooperativeMatrixOperandsMask { + CooperativeMatrixOperandsMaskNone = 0, + CooperativeMatrixOperandsMatrixASignedComponentsKHRMask = 0x00000001, + CooperativeMatrixOperandsMatrixBSignedComponentsKHRMask = 0x00000002, + CooperativeMatrixOperandsMatrixCSignedComponentsKHRMask = 0x00000004, + CooperativeMatrixOperandsMatrixResultSignedComponentsKHRMask = 0x00000008, + CooperativeMatrixOperandsSaturatingAccumulationKHRMask = 0x00000010, +}; + +enum CooperativeMatrixLayout { + CooperativeMatrixLayoutRowMajorKHR = 0, + CooperativeMatrixLayoutColumnMajorKHR = 1, + CooperativeMatrixLayoutRowBlockedInterleavedARM = 4202, + CooperativeMatrixLayoutColumnBlockedInterleavedARM = 4203, + CooperativeMatrixLayoutMax = 0x7fffffff, +}; + +enum CooperativeMatrixUse { + CooperativeMatrixUseMatrixAKHR = 0, + CooperativeMatrixUseMatrixBKHR = 1, + CooperativeMatrixUseMatrixAccumulatorKHR = 2, + CooperativeMatrixUseMax = 0x7fffffff, +}; + +enum CooperativeMatrixReduceShift { + CooperativeMatrixReduceRowShift = 0, + CooperativeMatrixReduceColumnShift = 1, + CooperativeMatrixReduce2x2Shift = 2, + CooperativeMatrixReduceMax = 0x7fffffff, +}; + +enum CooperativeMatrixReduceMask { + CooperativeMatrixReduceMaskNone = 0, + CooperativeMatrixReduceRowMask = 0x00000001, + CooperativeMatrixReduceColumnMask = 0x00000002, + CooperativeMatrixReduce2x2Mask = 0x00000004, +}; + +enum TensorClampMode { + TensorClampModeUndefined = 0, + TensorClampModeConstant = 1, + TensorClampModeClampToEdge = 2, + TensorClampModeRepeat = 3, + TensorClampModeRepeatMirrored = 4, + TensorClampModeMax = 0x7fffffff, +}; + +enum TensorAddressingOperandsShift { + TensorAddressingOperandsTensorViewShift = 0, + TensorAddressingOperandsDecodeFuncShift = 1, + TensorAddressingOperandsMax = 0x7fffffff, +}; + +enum TensorAddressingOperandsMask { + TensorAddressingOperandsMaskNone = 0, + TensorAddressingOperandsTensorViewMask = 0x00000001, + TensorAddressingOperandsDecodeFuncMask = 0x00000002, +}; + +enum TensorOperandsShift { + TensorOperandsNontemporalARMShift = 0, + TensorOperandsOutOfBoundsValueARMShift = 1, + TensorOperandsMakeElementAvailableARMShift = 2, + TensorOperandsMakeElementVisibleARMShift = 3, + TensorOperandsNonPrivateElementARMShift = 4, + TensorOperandsMax = 0x7fffffff, +}; + +enum TensorOperandsMask { + TensorOperandsMaskNone = 0, + TensorOperandsNontemporalARMMask = 0x00000001, + TensorOperandsOutOfBoundsValueARMMask = 0x00000002, + TensorOperandsMakeElementAvailableARMMask = 0x00000004, + TensorOperandsMakeElementVisibleARMMask = 0x00000008, + TensorOperandsNonPrivateElementARMMask = 0x00000010, +}; + +enum InitializationModeQualifier { + InitializationModeQualifierInitOnDeviceReprogramALTERA = 0, + InitializationModeQualifierInitOnDeviceReprogramINTEL = 0, + InitializationModeQualifierInitOnDeviceResetALTERA = 1, + InitializationModeQualifierInitOnDeviceResetINTEL = 1, + InitializationModeQualifierMax = 0x7fffffff, +}; + +enum HostAccessQualifier { + HostAccessQualifierNoneINTEL = 0, + HostAccessQualifierReadINTEL = 1, + HostAccessQualifierWriteINTEL = 2, + HostAccessQualifierReadWriteINTEL = 3, + HostAccessQualifierMax = 0x7fffffff, +}; + +enum LoadCacheControl { + LoadCacheControlUncachedINTEL = 0, + LoadCacheControlCachedINTEL = 1, + LoadCacheControlStreamingINTEL = 2, + LoadCacheControlInvalidateAfterReadINTEL = 3, + LoadCacheControlConstCachedINTEL = 4, + LoadCacheControlMax = 0x7fffffff, +}; + +enum StoreCacheControl { + StoreCacheControlUncachedINTEL = 0, + StoreCacheControlWriteThroughINTEL = 1, + StoreCacheControlWriteBackINTEL = 2, + StoreCacheControlStreamingINTEL = 3, + StoreCacheControlMax = 0x7fffffff, +}; + +enum NamedMaximumNumberOfRegisters { + NamedMaximumNumberOfRegistersAutoINTEL = 0, + NamedMaximumNumberOfRegistersMax = 0x7fffffff, +}; + +enum MatrixMultiplyAccumulateOperandsShift { + MatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELShift = 0, + MatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELShift = 1, + MatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELShift = 2, + MatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELShift = 3, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELShift = 4, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELShift = 5, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELShift = 6, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELShift = 7, + MatrixMultiplyAccumulateOperandsMatrixATF32INTELShift = 8, + MatrixMultiplyAccumulateOperandsMatrixBTF32INTELShift = 9, + MatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELShift = 10, + MatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELShift = 11, + MatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELShift = 12, + MatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELShift = 13, + MatrixMultiplyAccumulateOperandsMax = 0x7fffffff, +}; + +enum MatrixMultiplyAccumulateOperandsMask { + MatrixMultiplyAccumulateOperandsMaskNone = 0, + MatrixMultiplyAccumulateOperandsMatrixASignedComponentsINTELMask = 0x00000001, + MatrixMultiplyAccumulateOperandsMatrixBSignedComponentsINTELMask = 0x00000002, + MatrixMultiplyAccumulateOperandsMatrixCBFloat16INTELMask = 0x00000004, + MatrixMultiplyAccumulateOperandsMatrixResultBFloat16INTELMask = 0x00000008, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt8INTELMask = 0x00000010, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt8INTELMask = 0x00000020, + MatrixMultiplyAccumulateOperandsMatrixAPackedInt4INTELMask = 0x00000040, + MatrixMultiplyAccumulateOperandsMatrixBPackedInt4INTELMask = 0x00000080, + MatrixMultiplyAccumulateOperandsMatrixATF32INTELMask = 0x00000100, + MatrixMultiplyAccumulateOperandsMatrixBTF32INTELMask = 0x00000200, + MatrixMultiplyAccumulateOperandsMatrixAPackedFloat16INTELMask = 0x00000400, + MatrixMultiplyAccumulateOperandsMatrixBPackedFloat16INTELMask = 0x00000800, + MatrixMultiplyAccumulateOperandsMatrixAPackedBFloat16INTELMask = 0x00001000, + MatrixMultiplyAccumulateOperandsMatrixBPackedBFloat16INTELMask = 0x00002000, +}; + +enum RawAccessChainOperandsShift { + RawAccessChainOperandsRobustnessPerComponentNVShift = 0, + RawAccessChainOperandsRobustnessPerElementNVShift = 1, + RawAccessChainOperandsMax = 0x7fffffff, +}; + +enum RawAccessChainOperandsMask { + RawAccessChainOperandsMaskNone = 0, + RawAccessChainOperandsRobustnessPerComponentNVMask = 0x00000001, + RawAccessChainOperandsRobustnessPerElementNVMask = 0x00000002, +}; + +enum FPEncoding { + FPEncodingBFloat16KHR = 0, + FPEncodingFloat8E4M3EXT = 4214, + FPEncodingFloat8E5M2EXT = 4215, + FPEncodingMax = 0x7fffffff, +}; + +enum CooperativeVectorMatrixLayout { + CooperativeVectorMatrixLayoutRowMajorNV = 0, + CooperativeVectorMatrixLayoutColumnMajorNV = 1, + CooperativeVectorMatrixLayoutInferencingOptimalNV = 2, + CooperativeVectorMatrixLayoutTrainingOptimalNV = 3, + CooperativeVectorMatrixLayoutMax = 0x7fffffff, +}; + +enum ComponentType { + ComponentTypeFloat16NV = 0, + ComponentTypeFloat32NV = 1, + ComponentTypeFloat64NV = 2, + ComponentTypeSignedInt8NV = 3, + ComponentTypeSignedInt16NV = 4, + ComponentTypeSignedInt32NV = 5, + ComponentTypeSignedInt64NV = 6, + ComponentTypeUnsignedInt8NV = 7, + ComponentTypeUnsignedInt16NV = 8, + ComponentTypeUnsignedInt32NV = 9, + ComponentTypeUnsignedInt64NV = 10, + ComponentTypeSignedInt8PackedNV = 1000491000, + ComponentTypeUnsignedInt8PackedNV = 1000491001, + ComponentTypeFloatE4M3NV = 1000491002, + ComponentTypeFloatE5M2NV = 1000491003, + ComponentTypeMax = 0x7fffffff, +}; + +enum Op { + OpNop = 0, + OpUndef = 1, + OpSourceContinued = 2, + OpSource = 3, + OpSourceExtension = 4, + OpName = 5, + OpMemberName = 6, + OpString = 7, + OpLine = 8, + OpExtension = 10, + OpExtInstImport = 11, + OpExtInst = 12, + OpMemoryModel = 14, + OpEntryPoint = 15, + OpExecutionMode = 16, + OpCapability = 17, + OpTypeVoid = 19, + OpTypeBool = 20, + OpTypeInt = 21, + OpTypeFloat = 22, + OpTypeVector = 23, + OpTypeMatrix = 24, + OpTypeImage = 25, + OpTypeSampler = 26, + OpTypeSampledImage = 27, + OpTypeArray = 28, + OpTypeRuntimeArray = 29, + OpTypeStruct = 30, + OpTypeOpaque = 31, + OpTypePointer = 32, + OpTypeFunction = 33, + OpTypeEvent = 34, + OpTypeDeviceEvent = 35, + OpTypeReserveId = 36, + OpTypeQueue = 37, + OpTypePipe = 38, + OpTypeForwardPointer = 39, + OpConstantTrue = 41, + OpConstantFalse = 42, + OpConstant = 43, + OpConstantComposite = 44, + OpConstantSampler = 45, + OpConstantNull = 46, + OpSpecConstantTrue = 48, + OpSpecConstantFalse = 49, + OpSpecConstant = 50, + OpSpecConstantComposite = 51, + OpSpecConstantOp = 52, + OpFunction = 54, + OpFunctionParameter = 55, + OpFunctionEnd = 56, + OpFunctionCall = 57, + OpVariable = 59, + OpImageTexelPointer = 60, + OpLoad = 61, + OpStore = 62, + OpCopyMemory = 63, + OpCopyMemorySized = 64, + OpAccessChain = 65, + OpInBoundsAccessChain = 66, + OpPtrAccessChain = 67, + OpArrayLength = 68, + OpGenericPtrMemSemantics = 69, + OpInBoundsPtrAccessChain = 70, + OpDecorate = 71, + OpMemberDecorate = 72, + OpDecorationGroup = 73, + OpGroupDecorate = 74, + OpGroupMemberDecorate = 75, + OpVectorExtractDynamic = 77, + OpVectorInsertDynamic = 78, + OpVectorShuffle = 79, + OpCompositeConstruct = 80, + OpCompositeExtract = 81, + OpCompositeInsert = 82, + OpCopyObject = 83, + OpTranspose = 84, + OpSampledImage = 86, + OpImageSampleImplicitLod = 87, + OpImageSampleExplicitLod = 88, + OpImageSampleDrefImplicitLod = 89, + OpImageSampleDrefExplicitLod = 90, + OpImageSampleProjImplicitLod = 91, + OpImageSampleProjExplicitLod = 92, + OpImageSampleProjDrefImplicitLod = 93, + OpImageSampleProjDrefExplicitLod = 94, + OpImageFetch = 95, + OpImageGather = 96, + OpImageDrefGather = 97, + OpImageRead = 98, + OpImageWrite = 99, + OpImage = 100, + OpImageQueryFormat = 101, + OpImageQueryOrder = 102, + OpImageQuerySizeLod = 103, + OpImageQuerySize = 104, + OpImageQueryLod = 105, + OpImageQueryLevels = 106, + OpImageQuerySamples = 107, + OpConvertFToU = 109, + OpConvertFToS = 110, + OpConvertSToF = 111, + OpConvertUToF = 112, + OpUConvert = 113, + OpSConvert = 114, + OpFConvert = 115, + OpQuantizeToF16 = 116, + OpConvertPtrToU = 117, + OpSatConvertSToU = 118, + OpSatConvertUToS = 119, + OpConvertUToPtr = 120, + OpPtrCastToGeneric = 121, + OpGenericCastToPtr = 122, + OpGenericCastToPtrExplicit = 123, + OpBitcast = 124, + OpSNegate = 126, + OpFNegate = 127, + OpIAdd = 128, + OpFAdd = 129, + OpISub = 130, + OpFSub = 131, + OpIMul = 132, + OpFMul = 133, + OpUDiv = 134, + OpSDiv = 135, + OpFDiv = 136, + OpUMod = 137, + OpSRem = 138, + OpSMod = 139, + OpFRem = 140, + OpFMod = 141, + OpVectorTimesScalar = 142, + OpMatrixTimesScalar = 143, + OpVectorTimesMatrix = 144, + OpMatrixTimesVector = 145, + OpMatrixTimesMatrix = 146, + OpOuterProduct = 147, + OpDot = 148, + OpIAddCarry = 149, + OpISubBorrow = 150, + OpUMulExtended = 151, + OpSMulExtended = 152, + OpAny = 154, + OpAll = 155, + OpIsNan = 156, + OpIsInf = 157, + OpIsFinite = 158, + OpIsNormal = 159, + OpSignBitSet = 160, + OpLessOrGreater = 161, + OpOrdered = 162, + OpUnordered = 163, + OpLogicalEqual = 164, + OpLogicalNotEqual = 165, + OpLogicalOr = 166, + OpLogicalAnd = 167, + OpLogicalNot = 168, + OpSelect = 169, + OpIEqual = 170, + OpINotEqual = 171, + OpUGreaterThan = 172, + OpSGreaterThan = 173, + OpUGreaterThanEqual = 174, + OpSGreaterThanEqual = 175, + OpULessThan = 176, + OpSLessThan = 177, + OpULessThanEqual = 178, + OpSLessThanEqual = 179, + OpFOrdEqual = 180, + OpFUnordEqual = 181, + OpFOrdNotEqual = 182, + OpFUnordNotEqual = 183, + OpFOrdLessThan = 184, + OpFUnordLessThan = 185, + OpFOrdGreaterThan = 186, + OpFUnordGreaterThan = 187, + OpFOrdLessThanEqual = 188, + OpFUnordLessThanEqual = 189, + OpFOrdGreaterThanEqual = 190, + OpFUnordGreaterThanEqual = 191, + OpShiftRightLogical = 194, + OpShiftRightArithmetic = 195, + OpShiftLeftLogical = 196, + OpBitwiseOr = 197, + OpBitwiseXor = 198, + OpBitwiseAnd = 199, + OpNot = 200, + OpBitFieldInsert = 201, + OpBitFieldSExtract = 202, + OpBitFieldUExtract = 203, + OpBitReverse = 204, + OpBitCount = 205, + OpDPdx = 207, + OpDPdy = 208, + OpFwidth = 209, + OpDPdxFine = 210, + OpDPdyFine = 211, + OpFwidthFine = 212, + OpDPdxCoarse = 213, + OpDPdyCoarse = 214, + OpFwidthCoarse = 215, + OpEmitVertex = 218, + OpEndPrimitive = 219, + OpEmitStreamVertex = 220, + OpEndStreamPrimitive = 221, + OpControlBarrier = 224, + OpMemoryBarrier = 225, + OpAtomicLoad = 227, + OpAtomicStore = 228, + OpAtomicExchange = 229, + OpAtomicCompareExchange = 230, + OpAtomicCompareExchangeWeak = 231, + OpAtomicIIncrement = 232, + OpAtomicIDecrement = 233, + OpAtomicIAdd = 234, + OpAtomicISub = 235, + OpAtomicSMin = 236, + OpAtomicUMin = 237, + OpAtomicSMax = 238, + OpAtomicUMax = 239, + OpAtomicAnd = 240, + OpAtomicOr = 241, + OpAtomicXor = 242, + OpPhi = 245, + OpLoopMerge = 246, + OpSelectionMerge = 247, + OpLabel = 248, + OpBranch = 249, + OpBranchConditional = 250, + OpSwitch = 251, + OpKill = 252, + OpReturn = 253, + OpReturnValue = 254, + OpUnreachable = 255, + OpLifetimeStart = 256, + OpLifetimeStop = 257, + OpGroupAsyncCopy = 259, + OpGroupWaitEvents = 260, + OpGroupAll = 261, + OpGroupAny = 262, + OpGroupBroadcast = 263, + OpGroupIAdd = 264, + OpGroupFAdd = 265, + OpGroupFMin = 266, + OpGroupUMin = 267, + OpGroupSMin = 268, + OpGroupFMax = 269, + OpGroupUMax = 270, + OpGroupSMax = 271, + OpReadPipe = 274, + OpWritePipe = 275, + OpReservedReadPipe = 276, + OpReservedWritePipe = 277, + OpReserveReadPipePackets = 278, + OpReserveWritePipePackets = 279, + OpCommitReadPipe = 280, + OpCommitWritePipe = 281, + OpIsValidReserveId = 282, + OpGetNumPipePackets = 283, + OpGetMaxPipePackets = 284, + OpGroupReserveReadPipePackets = 285, + OpGroupReserveWritePipePackets = 286, + OpGroupCommitReadPipe = 287, + OpGroupCommitWritePipe = 288, + OpEnqueueMarker = 291, + OpEnqueueKernel = 292, + OpGetKernelNDrangeSubGroupCount = 293, + OpGetKernelNDrangeMaxSubGroupSize = 294, + OpGetKernelWorkGroupSize = 295, + OpGetKernelPreferredWorkGroupSizeMultiple = 296, + OpRetainEvent = 297, + OpReleaseEvent = 298, + OpCreateUserEvent = 299, + OpIsValidEvent = 300, + OpSetUserEventStatus = 301, + OpCaptureEventProfilingInfo = 302, + OpGetDefaultQueue = 303, + OpBuildNDRange = 304, + OpImageSparseSampleImplicitLod = 305, + OpImageSparseSampleExplicitLod = 306, + OpImageSparseSampleDrefImplicitLod = 307, + OpImageSparseSampleDrefExplicitLod = 308, + OpImageSparseSampleProjImplicitLod = 309, + OpImageSparseSampleProjExplicitLod = 310, + OpImageSparseSampleProjDrefImplicitLod = 311, + OpImageSparseSampleProjDrefExplicitLod = 312, + OpImageSparseFetch = 313, + OpImageSparseGather = 314, + OpImageSparseDrefGather = 315, + OpImageSparseTexelsResident = 316, + OpNoLine = 317, + OpAtomicFlagTestAndSet = 318, + OpAtomicFlagClear = 319, + OpImageSparseRead = 320, + OpSizeOf = 321, + OpTypePipeStorage = 322, + OpConstantPipeStorage = 323, + OpCreatePipeFromPipeStorage = 324, + OpGetKernelLocalSizeForSubgroupCount = 325, + OpGetKernelMaxNumSubgroups = 326, + OpTypeNamedBarrier = 327, + OpNamedBarrierInitialize = 328, + OpMemoryNamedBarrier = 329, + OpModuleProcessed = 330, + OpExecutionModeId = 331, + OpDecorateId = 332, + OpGroupNonUniformElect = 333, + OpGroupNonUniformAll = 334, + OpGroupNonUniformAny = 335, + OpGroupNonUniformAllEqual = 336, + OpGroupNonUniformBroadcast = 337, + OpGroupNonUniformBroadcastFirst = 338, + OpGroupNonUniformBallot = 339, + OpGroupNonUniformInverseBallot = 340, + OpGroupNonUniformBallotBitExtract = 341, + OpGroupNonUniformBallotBitCount = 342, + OpGroupNonUniformBallotFindLSB = 343, + OpGroupNonUniformBallotFindMSB = 344, + OpGroupNonUniformShuffle = 345, + OpGroupNonUniformShuffleXor = 346, + OpGroupNonUniformShuffleUp = 347, + OpGroupNonUniformShuffleDown = 348, + OpGroupNonUniformIAdd = 349, + OpGroupNonUniformFAdd = 350, + OpGroupNonUniformIMul = 351, + OpGroupNonUniformFMul = 352, + OpGroupNonUniformSMin = 353, + OpGroupNonUniformUMin = 354, + OpGroupNonUniformFMin = 355, + OpGroupNonUniformSMax = 356, + OpGroupNonUniformUMax = 357, + OpGroupNonUniformFMax = 358, + OpGroupNonUniformBitwiseAnd = 359, + OpGroupNonUniformBitwiseOr = 360, + OpGroupNonUniformBitwiseXor = 361, + OpGroupNonUniformLogicalAnd = 362, + OpGroupNonUniformLogicalOr = 363, + OpGroupNonUniformLogicalXor = 364, + OpGroupNonUniformQuadBroadcast = 365, + OpGroupNonUniformQuadSwap = 366, + OpCopyLogical = 400, + OpPtrEqual = 401, + OpPtrNotEqual = 402, + OpPtrDiff = 403, + OpColorAttachmentReadEXT = 4160, + OpDepthAttachmentReadEXT = 4161, + OpStencilAttachmentReadEXT = 4162, + OpTypeTensorARM = 4163, + OpTensorReadARM = 4164, + OpTensorWriteARM = 4165, + OpTensorQuerySizeARM = 4166, + OpGraphConstantARM = 4181, + OpGraphEntryPointARM = 4182, + OpGraphARM = 4183, + OpGraphInputARM = 4184, + OpGraphSetOutputARM = 4185, + OpGraphEndARM = 4186, + OpTypeGraphARM = 4190, + OpTerminateInvocation = 4416, + OpTypeUntypedPointerKHR = 4417, + OpUntypedVariableKHR = 4418, + OpUntypedAccessChainKHR = 4419, + OpUntypedInBoundsAccessChainKHR = 4420, + OpSubgroupBallotKHR = 4421, + OpSubgroupFirstInvocationKHR = 4422, + OpUntypedPtrAccessChainKHR = 4423, + OpUntypedInBoundsPtrAccessChainKHR = 4424, + OpUntypedArrayLengthKHR = 4425, + OpUntypedPrefetchKHR = 4426, + OpFmaKHR = 4427, + OpSubgroupAllKHR = 4428, + OpSubgroupAnyKHR = 4429, + OpSubgroupAllEqualKHR = 4430, + OpGroupNonUniformRotateKHR = 4431, + OpSubgroupReadInvocationKHR = 4432, + OpExtInstWithForwardRefsKHR = 4433, + OpUntypedGroupAsyncCopyKHR = 4434, + OpTraceRayKHR = 4445, + OpExecuteCallableKHR = 4446, + OpConvertUToAccelerationStructureKHR = 4447, + OpIgnoreIntersectionKHR = 4448, + OpTerminateRayKHR = 4449, + OpSDot = 4450, + OpSDotKHR = 4450, + OpUDot = 4451, + OpUDotKHR = 4451, + OpSUDot = 4452, + OpSUDotKHR = 4452, + OpSDotAccSat = 4453, + OpSDotAccSatKHR = 4453, + OpUDotAccSat = 4454, + OpUDotAccSatKHR = 4454, + OpSUDotAccSat = 4455, + OpSUDotAccSatKHR = 4455, + OpTypeCooperativeMatrixKHR = 4456, + OpCooperativeMatrixLoadKHR = 4457, + OpCooperativeMatrixStoreKHR = 4458, + OpCooperativeMatrixMulAddKHR = 4459, + OpCooperativeMatrixLengthKHR = 4460, + OpConstantCompositeReplicateEXT = 4461, + OpSpecConstantCompositeReplicateEXT = 4462, + OpCompositeConstructReplicateEXT = 4463, + OpTypeRayQueryKHR = 4472, + OpRayQueryInitializeKHR = 4473, + OpRayQueryTerminateKHR = 4474, + OpRayQueryGenerateIntersectionKHR = 4475, + OpRayQueryConfirmIntersectionKHR = 4476, + OpRayQueryProceedKHR = 4477, + OpRayQueryGetIntersectionTypeKHR = 4479, + OpImageSampleWeightedQCOM = 4480, + OpImageBoxFilterQCOM = 4481, + OpImageBlockMatchSSDQCOM = 4482, + OpImageBlockMatchSADQCOM = 4483, + OpBitCastArrayQCOM = 4497, + OpImageBlockMatchWindowSSDQCOM = 4500, + OpImageBlockMatchWindowSADQCOM = 4501, + OpImageBlockMatchGatherSSDQCOM = 4502, + OpImageBlockMatchGatherSADQCOM = 4503, + OpCompositeConstructCoopMatQCOM = 4540, + OpCompositeExtractCoopMatQCOM = 4541, + OpExtractSubArrayQCOM = 4542, + OpGroupIAddNonUniformAMD = 5000, + OpGroupFAddNonUniformAMD = 5001, + OpGroupFMinNonUniformAMD = 5002, + OpGroupUMinNonUniformAMD = 5003, + OpGroupSMinNonUniformAMD = 5004, + OpGroupFMaxNonUniformAMD = 5005, + OpGroupUMaxNonUniformAMD = 5006, + OpGroupSMaxNonUniformAMD = 5007, + OpFragmentMaskFetchAMD = 5011, + OpFragmentFetchAMD = 5012, + OpReadClockKHR = 5056, + OpAllocateNodePayloadsAMDX = 5074, + OpEnqueueNodePayloadsAMDX = 5075, + OpTypeNodePayloadArrayAMDX = 5076, + OpFinishWritingNodePayloadAMDX = 5078, + OpNodePayloadArrayLengthAMDX = 5090, + OpIsNodePayloadValidAMDX = 5101, + OpConstantStringAMDX = 5103, + OpSpecConstantStringAMDX = 5104, + OpGroupNonUniformQuadAllKHR = 5110, + OpGroupNonUniformQuadAnyKHR = 5111, + OpTypeBufferEXT = 5115, + OpBufferPointerEXT = 5119, + OpAbortKHR = 5121, + OpUntypedImageTexelPointerEXT = 5126, + OpMemberDecorateIdEXT = 5127, + OpConstantSizeOfEXT = 5129, + OpConstantDataKHR = 5147, + OpSpecConstantDataKHR = 5148, + OpPoisonKHR = 5158, + OpFreezeKHR = 5159, + OpHitObjectRecordHitMotionNV = 5249, + OpHitObjectRecordHitWithIndexMotionNV = 5250, + OpHitObjectRecordMissMotionNV = 5251, + OpHitObjectGetWorldToObjectNV = 5252, + OpHitObjectGetObjectToWorldNV = 5253, + OpHitObjectGetObjectRayDirectionNV = 5254, + OpHitObjectGetObjectRayOriginNV = 5255, + OpHitObjectTraceRayMotionNV = 5256, + OpHitObjectGetShaderRecordBufferHandleNV = 5257, + OpHitObjectGetShaderBindingTableRecordIndexNV = 5258, + OpHitObjectRecordEmptyNV = 5259, + OpHitObjectTraceRayNV = 5260, + OpHitObjectRecordHitNV = 5261, + OpHitObjectRecordHitWithIndexNV = 5262, + OpHitObjectRecordMissNV = 5263, + OpHitObjectExecuteShaderNV = 5264, + OpHitObjectGetCurrentTimeNV = 5265, + OpHitObjectGetAttributesNV = 5266, + OpHitObjectGetHitKindNV = 5267, + OpHitObjectGetPrimitiveIndexNV = 5268, + OpHitObjectGetGeometryIndexNV = 5269, + OpHitObjectGetInstanceIdNV = 5270, + OpHitObjectGetInstanceCustomIndexNV = 5271, + OpHitObjectGetWorldRayDirectionNV = 5272, + OpHitObjectGetWorldRayOriginNV = 5273, + OpHitObjectGetRayTMaxNV = 5274, + OpHitObjectGetRayTMinNV = 5275, + OpHitObjectIsEmptyNV = 5276, + OpHitObjectIsHitNV = 5277, + OpHitObjectIsMissNV = 5278, + OpReorderThreadWithHitObjectNV = 5279, + OpReorderThreadWithHintNV = 5280, + OpTypeHitObjectNV = 5281, + OpImageSampleFootprintNV = 5283, + OpTypeCooperativeVectorNV = 5288, + OpTypeVectorIdEXT = 5288, + OpCooperativeVectorMatrixMulNV = 5289, + OpCooperativeVectorOuterProductAccumulateNV = 5290, + OpCooperativeVectorReduceSumAccumulateNV = 5291, + OpCooperativeVectorMatrixMulAddNV = 5292, + OpCooperativeMatrixConvertNV = 5293, + OpEmitMeshTasksEXT = 5294, + OpSetMeshOutputsEXT = 5295, + OpGroupNonUniformPartitionEXT = 5296, + OpGroupNonUniformPartitionNV = 5296, + OpWritePackedPrimitiveIndices4x8NV = 5299, + OpFetchMicroTriangleVertexPositionNV = 5300, + OpFetchMicroTriangleVertexBarycentricNV = 5301, + OpCooperativeVectorLoadNV = 5302, + OpCooperativeVectorStoreNV = 5303, + OpHitObjectRecordFromQueryEXT = 5304, + OpHitObjectRecordMissEXT = 5305, + OpHitObjectRecordMissMotionEXT = 5306, + OpHitObjectGetIntersectionTriangleVertexPositionsEXT = 5307, + OpHitObjectGetRayFlagsEXT = 5308, + OpHitObjectSetShaderBindingTableRecordIndexEXT = 5309, + OpHitObjectReorderExecuteShaderEXT = 5310, + OpHitObjectTraceReorderExecuteEXT = 5311, + OpHitObjectTraceMotionReorderExecuteEXT = 5312, + OpTypeHitObjectEXT = 5313, + OpReorderThreadWithHintEXT = 5314, + OpReorderThreadWithHitObjectEXT = 5315, + OpHitObjectTraceRayEXT = 5316, + OpHitObjectTraceRayMotionEXT = 5317, + OpHitObjectRecordEmptyEXT = 5318, + OpHitObjectExecuteShaderEXT = 5319, + OpHitObjectGetCurrentTimeEXT = 5320, + OpHitObjectGetAttributesEXT = 5321, + OpHitObjectGetHitKindEXT = 5322, + OpHitObjectGetPrimitiveIndexEXT = 5323, + OpHitObjectGetGeometryIndexEXT = 5324, + OpHitObjectGetInstanceIdEXT = 5325, + OpHitObjectGetInstanceCustomIndexEXT = 5326, + OpHitObjectGetObjectRayOriginEXT = 5327, + OpHitObjectGetObjectRayDirectionEXT = 5328, + OpHitObjectGetWorldRayDirectionEXT = 5329, + OpHitObjectGetWorldRayOriginEXT = 5330, + OpHitObjectGetObjectToWorldEXT = 5331, + OpHitObjectGetWorldToObjectEXT = 5332, + OpHitObjectGetRayTMaxEXT = 5333, + OpReportIntersectionKHR = 5334, + OpReportIntersectionNV = 5334, + OpIgnoreIntersectionNV = 5335, + OpTerminateRayNV = 5336, + OpTraceNV = 5337, + OpTraceMotionNV = 5338, + OpTraceRayMotionNV = 5339, + OpRayQueryGetIntersectionTriangleVertexPositionsKHR = 5340, + OpTypeAccelerationStructureKHR = 5341, + OpTypeAccelerationStructureNV = 5341, + OpExecuteCallableNV = 5344, + OpRayQueryGetClusterIdNV = 5345, + OpRayQueryGetIntersectionClusterIdNV = 5345, + OpHitObjectGetClusterIdNV = 5346, + OpHitObjectGetRayTMinEXT = 5347, + OpHitObjectGetShaderBindingTableRecordIndexEXT = 5348, + OpHitObjectGetShaderRecordBufferHandleEXT = 5349, + OpHitObjectIsEmptyEXT = 5350, + OpHitObjectIsHitEXT = 5351, + OpHitObjectIsMissEXT = 5352, + OpTypeCooperativeMatrixNV = 5358, + OpCooperativeMatrixLoadNV = 5359, + OpCooperativeMatrixStoreNV = 5360, + OpCooperativeMatrixMulAddNV = 5361, + OpCooperativeMatrixLengthNV = 5362, + OpBeginInvocationInterlockEXT = 5364, + OpEndInvocationInterlockEXT = 5365, + OpCooperativeMatrixReduceNV = 5366, + OpCooperativeMatrixLoadTensorNV = 5367, + OpCooperativeMatrixStoreTensorNV = 5368, + OpCooperativeMatrixPerElementOpNV = 5369, + OpTypeTensorLayoutNV = 5370, + OpTypeTensorViewNV = 5371, + OpCreateTensorLayoutNV = 5372, + OpTensorLayoutSetDimensionNV = 5373, + OpTensorLayoutSetStrideNV = 5374, + OpTensorLayoutSliceNV = 5375, + OpTensorLayoutSetClampValueNV = 5376, + OpCreateTensorViewNV = 5377, + OpTensorViewSetDimensionNV = 5378, + OpTensorViewSetStrideNV = 5379, + OpDemoteToHelperInvocation = 5380, + OpDemoteToHelperInvocationEXT = 5380, + OpIsHelperInvocationEXT = 5381, + OpTensorViewSetClipNV = 5382, + OpTensorLayoutSetBlockSizeNV = 5384, + OpCooperativeMatrixTransposeNV = 5390, + OpConvertUToImageNV = 5391, + OpConvertUToSamplerNV = 5392, + OpConvertImageToUNV = 5393, + OpConvertSamplerToUNV = 5394, + OpConvertUToSampledImageNV = 5395, + OpConvertSampledImageToUNV = 5396, + OpSamplerImageAddressingModeNV = 5397, + OpRawAccessChainNV = 5398, + OpRayQueryGetIntersectionSpherePositionNV = 5427, + OpRayQueryGetIntersectionSphereRadiusNV = 5428, + OpRayQueryGetIntersectionLSSPositionsNV = 5429, + OpRayQueryGetIntersectionLSSRadiiNV = 5430, + OpRayQueryGetIntersectionLSSHitValueNV = 5431, + OpHitObjectGetSpherePositionNV = 5432, + OpHitObjectGetSphereRadiusNV = 5433, + OpHitObjectGetLSSPositionsNV = 5434, + OpHitObjectGetLSSRadiiNV = 5435, + OpHitObjectIsSphereHitNV = 5436, + OpHitObjectIsLSSHitNV = 5437, + OpRayQueryIsSphereHitNV = 5438, + OpRayQueryIsLSSHitNV = 5439, + OpSubgroupShuffleINTEL = 5571, + OpSubgroupShuffleDownINTEL = 5572, + OpSubgroupShuffleUpINTEL = 5573, + OpSubgroupShuffleXorINTEL = 5574, + OpSubgroupBlockReadINTEL = 5575, + OpSubgroupBlockWriteINTEL = 5576, + OpSubgroupImageBlockReadINTEL = 5577, + OpSubgroupImageBlockWriteINTEL = 5578, + OpSubgroupImageMediaBlockReadINTEL = 5580, + OpSubgroupImageMediaBlockWriteINTEL = 5581, + OpUCountLeadingZerosINTEL = 5585, + OpUCountTrailingZerosINTEL = 5586, + OpAbsISubINTEL = 5587, + OpAbsUSubINTEL = 5588, + OpIAddSatINTEL = 5589, + OpUAddSatINTEL = 5590, + OpIAverageINTEL = 5591, + OpUAverageINTEL = 5592, + OpIAverageRoundedINTEL = 5593, + OpUAverageRoundedINTEL = 5594, + OpISubSatINTEL = 5595, + OpUSubSatINTEL = 5596, + OpIMul32x16INTEL = 5597, + OpUMul32x16INTEL = 5598, + OpConstantFunctionPointerINTEL = 5600, + OpFunctionPointerCallINTEL = 5601, + OpAsmTargetINTEL = 5609, + OpAsmINTEL = 5610, + OpAsmCallINTEL = 5611, + OpAtomicFMinEXT = 5614, + OpAtomicFMaxEXT = 5615, + OpAssumeTrueKHR = 5630, + OpExpectKHR = 5631, + OpDecorateString = 5632, + OpDecorateStringGOOGLE = 5632, + OpMemberDecorateString = 5633, + OpMemberDecorateStringGOOGLE = 5633, + OpVmeImageINTEL = 5699, + OpTypeVmeImageINTEL = 5700, + OpTypeAvcImePayloadINTEL = 5701, + OpTypeAvcRefPayloadINTEL = 5702, + OpTypeAvcSicPayloadINTEL = 5703, + OpTypeAvcMcePayloadINTEL = 5704, + OpTypeAvcMceResultINTEL = 5705, + OpTypeAvcImeResultINTEL = 5706, + OpTypeAvcImeResultSingleReferenceStreamoutINTEL = 5707, + OpTypeAvcImeResultDualReferenceStreamoutINTEL = 5708, + OpTypeAvcImeSingleReferenceStreaminINTEL = 5709, + OpTypeAvcImeDualReferenceStreaminINTEL = 5710, + OpTypeAvcRefResultINTEL = 5711, + OpTypeAvcSicResultINTEL = 5712, + OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL = 5713, + OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL = 5714, + OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL = 5715, + OpSubgroupAvcMceSetInterShapePenaltyINTEL = 5716, + OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL = 5717, + OpSubgroupAvcMceSetInterDirectionPenaltyINTEL = 5718, + OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL = 5719, + OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL = 5720, + OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL = 5721, + OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL = 5722, + OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL = 5723, + OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL = 5724, + OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL = 5725, + OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL = 5726, + OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL = 5727, + OpSubgroupAvcMceSetAcOnlyHaarINTEL = 5728, + OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL = 5729, + OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL = 5730, + OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL = 5731, + OpSubgroupAvcMceConvertToImePayloadINTEL = 5732, + OpSubgroupAvcMceConvertToImeResultINTEL = 5733, + OpSubgroupAvcMceConvertToRefPayloadINTEL = 5734, + OpSubgroupAvcMceConvertToRefResultINTEL = 5735, + OpSubgroupAvcMceConvertToSicPayloadINTEL = 5736, + OpSubgroupAvcMceConvertToSicResultINTEL = 5737, + OpSubgroupAvcMceGetMotionVectorsINTEL = 5738, + OpSubgroupAvcMceGetInterDistortionsINTEL = 5739, + OpSubgroupAvcMceGetBestInterDistortionsINTEL = 5740, + OpSubgroupAvcMceGetInterMajorShapeINTEL = 5741, + OpSubgroupAvcMceGetInterMinorShapeINTEL = 5742, + OpSubgroupAvcMceGetInterDirectionsINTEL = 5743, + OpSubgroupAvcMceGetInterMotionVectorCountINTEL = 5744, + OpSubgroupAvcMceGetInterReferenceIdsINTEL = 5745, + OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL = 5746, + OpSubgroupAvcImeInitializeINTEL = 5747, + OpSubgroupAvcImeSetSingleReferenceINTEL = 5748, + OpSubgroupAvcImeSetDualReferenceINTEL = 5749, + OpSubgroupAvcImeRefWindowSizeINTEL = 5750, + OpSubgroupAvcImeAdjustRefOffsetINTEL = 5751, + OpSubgroupAvcImeConvertToMcePayloadINTEL = 5752, + OpSubgroupAvcImeSetMaxMotionVectorCountINTEL = 5753, + OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL = 5754, + OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL = 5755, + OpSubgroupAvcImeSetWeightedSadINTEL = 5756, + OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL = 5757, + OpSubgroupAvcImeEvaluateWithDualReferenceINTEL = 5758, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL = 5759, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL = 5760, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL = 5761, + OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL = 5762, + OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL = 5763, + OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL = 5764, + OpSubgroupAvcImeConvertToMceResultINTEL = 5765, + OpSubgroupAvcImeGetSingleReferenceStreaminINTEL = 5766, + OpSubgroupAvcImeGetDualReferenceStreaminINTEL = 5767, + OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL = 5768, + OpSubgroupAvcImeStripDualReferenceStreamoutINTEL = 5769, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL = 5770, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL = 5771, + OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL = 5772, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL = 5773, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL = 5774, + OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL = 5775, + OpSubgroupAvcImeGetBorderReachedINTEL = 5776, + OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL = 5777, + OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL = 5778, + OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL = 5779, + OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL = 5780, + OpSubgroupAvcFmeInitializeINTEL = 5781, + OpSubgroupAvcBmeInitializeINTEL = 5782, + OpSubgroupAvcRefConvertToMcePayloadINTEL = 5783, + OpSubgroupAvcRefSetBidirectionalMixDisableINTEL = 5784, + OpSubgroupAvcRefSetBilinearFilterEnableINTEL = 5785, + OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL = 5786, + OpSubgroupAvcRefEvaluateWithDualReferenceINTEL = 5787, + OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL = 5788, + OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL = 5789, + OpSubgroupAvcRefConvertToMceResultINTEL = 5790, + OpSubgroupAvcSicInitializeINTEL = 5791, + OpSubgroupAvcSicConfigureSkcINTEL = 5792, + OpSubgroupAvcSicConfigureIpeLumaINTEL = 5793, + OpSubgroupAvcSicConfigureIpeLumaChromaINTEL = 5794, + OpSubgroupAvcSicGetMotionVectorMaskINTEL = 5795, + OpSubgroupAvcSicConvertToMcePayloadINTEL = 5796, + OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL = 5797, + OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL = 5798, + OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL = 5799, + OpSubgroupAvcSicSetBilinearFilterEnableINTEL = 5800, + OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL = 5801, + OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL = 5802, + OpSubgroupAvcSicEvaluateIpeINTEL = 5803, + OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL = 5804, + OpSubgroupAvcSicEvaluateWithDualReferenceINTEL = 5805, + OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL = 5806, + OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL = 5807, + OpSubgroupAvcSicConvertToMceResultINTEL = 5808, + OpSubgroupAvcSicGetIpeLumaShapeINTEL = 5809, + OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL = 5810, + OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL = 5811, + OpSubgroupAvcSicGetPackedIpeLumaModesINTEL = 5812, + OpSubgroupAvcSicGetIpeChromaModeINTEL = 5813, + OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL = 5814, + OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL = 5815, + OpSubgroupAvcSicGetInterRawSadsINTEL = 5816, + OpVariableLengthArrayINTEL = 5818, + OpSaveMemoryINTEL = 5819, + OpRestoreMemoryINTEL = 5820, + OpArbitraryFloatSinCosPiALTERA = 5840, + OpArbitraryFloatSinCosPiINTEL = 5840, + OpArbitraryFloatCastALTERA = 5841, + OpArbitraryFloatCastINTEL = 5841, + OpArbitraryFloatCastFromIntALTERA = 5842, + OpArbitraryFloatCastFromIntINTEL = 5842, + OpArbitraryFloatCastToIntALTERA = 5843, + OpArbitraryFloatCastToIntINTEL = 5843, + OpArbitraryFloatAddALTERA = 5846, + OpArbitraryFloatAddINTEL = 5846, + OpArbitraryFloatSubALTERA = 5847, + OpArbitraryFloatSubINTEL = 5847, + OpArbitraryFloatMulALTERA = 5848, + OpArbitraryFloatMulINTEL = 5848, + OpArbitraryFloatDivALTERA = 5849, + OpArbitraryFloatDivINTEL = 5849, + OpArbitraryFloatGTALTERA = 5850, + OpArbitraryFloatGTINTEL = 5850, + OpArbitraryFloatGEALTERA = 5851, + OpArbitraryFloatGEINTEL = 5851, + OpArbitraryFloatLTALTERA = 5852, + OpArbitraryFloatLTINTEL = 5852, + OpArbitraryFloatLEALTERA = 5853, + OpArbitraryFloatLEINTEL = 5853, + OpArbitraryFloatEQALTERA = 5854, + OpArbitraryFloatEQINTEL = 5854, + OpArbitraryFloatRecipALTERA = 5855, + OpArbitraryFloatRecipINTEL = 5855, + OpArbitraryFloatRSqrtALTERA = 5856, + OpArbitraryFloatRSqrtINTEL = 5856, + OpArbitraryFloatCbrtALTERA = 5857, + OpArbitraryFloatCbrtINTEL = 5857, + OpArbitraryFloatHypotALTERA = 5858, + OpArbitraryFloatHypotINTEL = 5858, + OpArbitraryFloatSqrtALTERA = 5859, + OpArbitraryFloatSqrtINTEL = 5859, + OpArbitraryFloatLogINTEL = 5860, + OpArbitraryFloatLog2INTEL = 5861, + OpArbitraryFloatLog10INTEL = 5862, + OpArbitraryFloatLog1pINTEL = 5863, + OpArbitraryFloatExpINTEL = 5864, + OpArbitraryFloatExp2INTEL = 5865, + OpArbitraryFloatExp10INTEL = 5866, + OpArbitraryFloatExpm1INTEL = 5867, + OpArbitraryFloatSinINTEL = 5868, + OpArbitraryFloatCosINTEL = 5869, + OpArbitraryFloatSinCosINTEL = 5870, + OpArbitraryFloatSinPiINTEL = 5871, + OpArbitraryFloatCosPiINTEL = 5872, + OpArbitraryFloatASinINTEL = 5873, + OpArbitraryFloatASinPiINTEL = 5874, + OpArbitraryFloatACosINTEL = 5875, + OpArbitraryFloatACosPiINTEL = 5876, + OpArbitraryFloatATanINTEL = 5877, + OpArbitraryFloatATanPiINTEL = 5878, + OpArbitraryFloatATan2INTEL = 5879, + OpArbitraryFloatPowINTEL = 5880, + OpArbitraryFloatPowRINTEL = 5881, + OpArbitraryFloatPowNINTEL = 5882, + OpLoopControlINTEL = 5887, + OpAliasDomainDeclINTEL = 5911, + OpAliasScopeDeclINTEL = 5912, + OpAliasScopeListDeclINTEL = 5913, + OpFixedSqrtALTERA = 5923, + OpFixedSqrtINTEL = 5923, + OpFixedRecipALTERA = 5924, + OpFixedRecipINTEL = 5924, + OpFixedRsqrtALTERA = 5925, + OpFixedRsqrtINTEL = 5925, + OpFixedSinALTERA = 5926, + OpFixedSinINTEL = 5926, + OpFixedCosALTERA = 5927, + OpFixedCosINTEL = 5927, + OpFixedSinCosALTERA = 5928, + OpFixedSinCosINTEL = 5928, + OpFixedSinPiALTERA = 5929, + OpFixedSinPiINTEL = 5929, + OpFixedCosPiALTERA = 5930, + OpFixedCosPiINTEL = 5930, + OpFixedSinCosPiALTERA = 5931, + OpFixedSinCosPiINTEL = 5931, + OpFixedLogALTERA = 5932, + OpFixedLogINTEL = 5932, + OpFixedExpALTERA = 5933, + OpFixedExpINTEL = 5933, + OpPtrCastToCrossWorkgroupALTERA = 5934, + OpPtrCastToCrossWorkgroupINTEL = 5934, + OpCrossWorkgroupCastToPtrALTERA = 5938, + OpCrossWorkgroupCastToPtrINTEL = 5938, + OpReadPipeBlockingALTERA = 5946, + OpReadPipeBlockingINTEL = 5946, + OpWritePipeBlockingALTERA = 5947, + OpWritePipeBlockingINTEL = 5947, + OpFPGARegALTERA = 5949, + OpFPGARegINTEL = 5949, + OpRayQueryGetRayTMinKHR = 6016, + OpRayQueryGetRayFlagsKHR = 6017, + OpRayQueryGetIntersectionTKHR = 6018, + OpRayQueryGetIntersectionInstanceCustomIndexKHR = 6019, + OpRayQueryGetIntersectionInstanceIdKHR = 6020, + OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR = 6021, + OpRayQueryGetIntersectionGeometryIndexKHR = 6022, + OpRayQueryGetIntersectionPrimitiveIndexKHR = 6023, + OpRayQueryGetIntersectionBarycentricsKHR = 6024, + OpRayQueryGetIntersectionFrontFaceKHR = 6025, + OpRayQueryGetIntersectionCandidateAABBOpaqueKHR = 6026, + OpRayQueryGetIntersectionObjectRayDirectionKHR = 6027, + OpRayQueryGetIntersectionObjectRayOriginKHR = 6028, + OpRayQueryGetWorldRayDirectionKHR = 6029, + OpRayQueryGetWorldRayOriginKHR = 6030, + OpRayQueryGetIntersectionObjectToWorldKHR = 6031, + OpRayQueryGetIntersectionWorldToObjectKHR = 6032, + OpAtomicFAddEXT = 6035, + OpTypeBufferSurfaceINTEL = 6086, + OpTypeStructContinuedINTEL = 6090, + OpConstantCompositeContinuedINTEL = 6091, + OpSpecConstantCompositeContinuedINTEL = 6092, + OpCompositeConstructContinuedINTEL = 6096, + OpConvertFToBF16INTEL = 6116, + OpConvertBF16ToFINTEL = 6117, + OpControlBarrierArriveINTEL = 6142, + OpControlBarrierWaitINTEL = 6143, + OpArithmeticFenceEXT = 6145, + OpTaskSequenceCreateALTERA = 6163, + OpTaskSequenceCreateINTEL = 6163, + OpTaskSequenceAsyncALTERA = 6164, + OpTaskSequenceAsyncINTEL = 6164, + OpTaskSequenceGetALTERA = 6165, + OpTaskSequenceGetINTEL = 6165, + OpTaskSequenceReleaseALTERA = 6166, + OpTaskSequenceReleaseINTEL = 6166, + OpTypeTaskSequenceALTERA = 6199, + OpTypeTaskSequenceINTEL = 6199, + OpSubgroupBlockPrefetchINTEL = 6221, + OpSubgroup2DBlockLoadINTEL = 6231, + OpSubgroup2DBlockLoadTransformINTEL = 6232, + OpSubgroup2DBlockLoadTransposeINTEL = 6233, + OpSubgroup2DBlockPrefetchINTEL = 6234, + OpSubgroup2DBlockStoreINTEL = 6235, + OpSubgroupMatrixMultiplyAccumulateINTEL = 6237, + OpBitwiseFunctionINTEL = 6242, + OpUntypedVariableLengthArrayINTEL = 6244, + OpConditionalExtensionINTEL = 6248, + OpConditionalEntryPointINTEL = 6249, + OpConditionalCapabilityINTEL = 6250, + OpSpecConstantTargetINTEL = 6251, + OpSpecConstantArchitectureINTEL = 6252, + OpSpecConstantCapabilitiesINTEL = 6253, + OpConditionalCopyObjectINTEL = 6254, + OpGroupIMulKHR = 6401, + OpGroupFMulKHR = 6402, + OpGroupBitwiseAndKHR = 6403, + OpGroupBitwiseOrKHR = 6404, + OpGroupBitwiseXorKHR = 6405, + OpGroupLogicalAndKHR = 6406, + OpGroupLogicalOrKHR = 6407, + OpGroupLogicalXorKHR = 6408, + OpRoundFToTF32INTEL = 6426, + OpMaskedGatherINTEL = 6428, + OpMaskedScatterINTEL = 6429, + OpConvertHandleToImageINTEL = 6529, + OpConvertHandleToSamplerINTEL = 6530, + OpConvertHandleToSampledImageINTEL = 6531, + OpFDot2MixAcc32VALVE = 6916, + OpFDot2MixAcc16VALVE = 6917, + OpFDot4MixAcc32VALVE = 6918, + OpMax = 0x7fffffff, +}; + +#ifdef SPV_ENABLE_UTILITY_CODE +#ifndef __cplusplus +#include +#endif +inline void HasResultAndType(Op opcode, bool *hasResult, bool *hasResultType) { + *hasResult = *hasResultType = false; + switch (opcode) { + default: /* unknown opcode */ break; + case OpNop: *hasResult = false; *hasResultType = false; break; + case OpUndef: *hasResult = true; *hasResultType = true; break; + case OpSourceContinued: *hasResult = false; *hasResultType = false; break; + case OpSource: *hasResult = false; *hasResultType = false; break; + case OpSourceExtension: *hasResult = false; *hasResultType = false; break; + case OpName: *hasResult = false; *hasResultType = false; break; + case OpMemberName: *hasResult = false; *hasResultType = false; break; + case OpString: *hasResult = true; *hasResultType = false; break; + case OpLine: *hasResult = false; *hasResultType = false; break; + case OpExtension: *hasResult = false; *hasResultType = false; break; + case OpExtInstImport: *hasResult = true; *hasResultType = false; break; + case OpExtInst: *hasResult = true; *hasResultType = true; break; + case OpMemoryModel: *hasResult = false; *hasResultType = false; break; + case OpEntryPoint: *hasResult = false; *hasResultType = false; break; + case OpExecutionMode: *hasResult = false; *hasResultType = false; break; + case OpCapability: *hasResult = false; *hasResultType = false; break; + case OpTypeVoid: *hasResult = true; *hasResultType = false; break; + case OpTypeBool: *hasResult = true; *hasResultType = false; break; + case OpTypeInt: *hasResult = true; *hasResultType = false; break; + case OpTypeFloat: *hasResult = true; *hasResultType = false; break; + case OpTypeVector: *hasResult = true; *hasResultType = false; break; + case OpTypeMatrix: *hasResult = true; *hasResultType = false; break; + case OpTypeImage: *hasResult = true; *hasResultType = false; break; + case OpTypeSampler: *hasResult = true; *hasResultType = false; break; + case OpTypeSampledImage: *hasResult = true; *hasResultType = false; break; + case OpTypeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeRuntimeArray: *hasResult = true; *hasResultType = false; break; + case OpTypeStruct: *hasResult = true; *hasResultType = false; break; + case OpTypeOpaque: *hasResult = true; *hasResultType = false; break; + case OpTypePointer: *hasResult = true; *hasResultType = false; break; + case OpTypeFunction: *hasResult = true; *hasResultType = false; break; + case OpTypeEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeDeviceEvent: *hasResult = true; *hasResultType = false; break; + case OpTypeReserveId: *hasResult = true; *hasResultType = false; break; + case OpTypeQueue: *hasResult = true; *hasResultType = false; break; + case OpTypePipe: *hasResult = true; *hasResultType = false; break; + case OpTypeForwardPointer: *hasResult = false; *hasResultType = false; break; + case OpConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpConstant: *hasResult = true; *hasResultType = true; break; + case OpConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpConstantSampler: *hasResult = true; *hasResultType = true; break; + case OpConstantNull: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantTrue: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantFalse: *hasResult = true; *hasResultType = true; break; + case OpSpecConstant: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantComposite: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantOp: *hasResult = true; *hasResultType = true; break; + case OpFunction: *hasResult = true; *hasResultType = true; break; + case OpFunctionParameter: *hasResult = true; *hasResultType = true; break; + case OpFunctionEnd: *hasResult = false; *hasResultType = false; break; + case OpFunctionCall: *hasResult = true; *hasResultType = true; break; + case OpVariable: *hasResult = true; *hasResultType = true; break; + case OpImageTexelPointer: *hasResult = true; *hasResultType = true; break; + case OpLoad: *hasResult = true; *hasResultType = true; break; + case OpStore: *hasResult = false; *hasResultType = false; break; + case OpCopyMemory: *hasResult = false; *hasResultType = false; break; + case OpCopyMemorySized: *hasResult = false; *hasResultType = false; break; + case OpAccessChain: *hasResult = true; *hasResultType = true; break; + case OpInBoundsAccessChain: *hasResult = true; *hasResultType = true; break; + case OpPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpArrayLength: *hasResult = true; *hasResultType = true; break; + case OpGenericPtrMemSemantics: *hasResult = true; *hasResultType = true; break; + case OpInBoundsPtrAccessChain: *hasResult = true; *hasResultType = true; break; + case OpDecorate: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpDecorationGroup: *hasResult = true; *hasResultType = false; break; + case OpGroupDecorate: *hasResult = false; *hasResultType = false; break; + case OpGroupMemberDecorate: *hasResult = false; *hasResultType = false; break; + case OpVectorExtractDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorInsertDynamic: *hasResult = true; *hasResultType = true; break; + case OpVectorShuffle: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstruct: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtract: *hasResult = true; *hasResultType = true; break; + case OpCompositeInsert: *hasResult = true; *hasResultType = true; break; + case OpCopyObject: *hasResult = true; *hasResultType = true; break; + case OpTranspose: *hasResult = true; *hasResultType = true; break; + case OpSampledImage: *hasResult = true; *hasResultType = true; break; + case OpImageSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageFetch: *hasResult = true; *hasResultType = true; break; + case OpImageGather: *hasResult = true; *hasResultType = true; break; + case OpImageDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageRead: *hasResult = true; *hasResultType = true; break; + case OpImageWrite: *hasResult = false; *hasResultType = false; break; + case OpImage: *hasResult = true; *hasResultType = true; break; + case OpImageQueryFormat: *hasResult = true; *hasResultType = true; break; + case OpImageQueryOrder: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySizeLod: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySize: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLod: *hasResult = true; *hasResultType = true; break; + case OpImageQueryLevels: *hasResult = true; *hasResultType = true; break; + case OpImageQuerySamples: *hasResult = true; *hasResultType = true; break; + case OpConvertFToU: *hasResult = true; *hasResultType = true; break; + case OpConvertFToS: *hasResult = true; *hasResultType = true; break; + case OpConvertSToF: *hasResult = true; *hasResultType = true; break; + case OpConvertUToF: *hasResult = true; *hasResultType = true; break; + case OpUConvert: *hasResult = true; *hasResultType = true; break; + case OpSConvert: *hasResult = true; *hasResultType = true; break; + case OpFConvert: *hasResult = true; *hasResultType = true; break; + case OpQuantizeToF16: *hasResult = true; *hasResultType = true; break; + case OpConvertPtrToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertSToU: *hasResult = true; *hasResultType = true; break; + case OpSatConvertUToS: *hasResult = true; *hasResultType = true; break; + case OpConvertUToPtr: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToGeneric: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtr: *hasResult = true; *hasResultType = true; break; + case OpGenericCastToPtrExplicit: *hasResult = true; *hasResultType = true; break; + case OpBitcast: *hasResult = true; *hasResultType = true; break; + case OpSNegate: *hasResult = true; *hasResultType = true; break; + case OpFNegate: *hasResult = true; *hasResultType = true; break; + case OpIAdd: *hasResult = true; *hasResultType = true; break; + case OpFAdd: *hasResult = true; *hasResultType = true; break; + case OpISub: *hasResult = true; *hasResultType = true; break; + case OpFSub: *hasResult = true; *hasResultType = true; break; + case OpIMul: *hasResult = true; *hasResultType = true; break; + case OpFMul: *hasResult = true; *hasResultType = true; break; + case OpUDiv: *hasResult = true; *hasResultType = true; break; + case OpSDiv: *hasResult = true; *hasResultType = true; break; + case OpFDiv: *hasResult = true; *hasResultType = true; break; + case OpUMod: *hasResult = true; *hasResultType = true; break; + case OpSRem: *hasResult = true; *hasResultType = true; break; + case OpSMod: *hasResult = true; *hasResultType = true; break; + case OpFRem: *hasResult = true; *hasResultType = true; break; + case OpFMod: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesScalar: *hasResult = true; *hasResultType = true; break; + case OpVectorTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesVector: *hasResult = true; *hasResultType = true; break; + case OpMatrixTimesMatrix: *hasResult = true; *hasResultType = true; break; + case OpOuterProduct: *hasResult = true; *hasResultType = true; break; + case OpDot: *hasResult = true; *hasResultType = true; break; + case OpIAddCarry: *hasResult = true; *hasResultType = true; break; + case OpISubBorrow: *hasResult = true; *hasResultType = true; break; + case OpUMulExtended: *hasResult = true; *hasResultType = true; break; + case OpSMulExtended: *hasResult = true; *hasResultType = true; break; + case OpAny: *hasResult = true; *hasResultType = true; break; + case OpAll: *hasResult = true; *hasResultType = true; break; + case OpIsNan: *hasResult = true; *hasResultType = true; break; + case OpIsInf: *hasResult = true; *hasResultType = true; break; + case OpIsFinite: *hasResult = true; *hasResultType = true; break; + case OpIsNormal: *hasResult = true; *hasResultType = true; break; + case OpSignBitSet: *hasResult = true; *hasResultType = true; break; + case OpLessOrGreater: *hasResult = true; *hasResultType = true; break; + case OpOrdered: *hasResult = true; *hasResultType = true; break; + case OpUnordered: *hasResult = true; *hasResultType = true; break; + case OpLogicalEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalNotEqual: *hasResult = true; *hasResultType = true; break; + case OpLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpLogicalNot: *hasResult = true; *hasResultType = true; break; + case OpSelect: *hasResult = true; *hasResultType = true; break; + case OpIEqual: *hasResult = true; *hasResultType = true; break; + case OpINotEqual: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpUGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpULessThan: *hasResult = true; *hasResultType = true; break; + case OpSLessThan: *hasResult = true; *hasResultType = true; break; + case OpULessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpSLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordNotEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThan: *hasResult = true; *hasResultType = true; break; + case OpFOrdLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordLessThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFOrdGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpFUnordGreaterThanEqual: *hasResult = true; *hasResultType = true; break; + case OpShiftRightLogical: *hasResult = true; *hasResultType = true; break; + case OpShiftRightArithmetic: *hasResult = true; *hasResultType = true; break; + case OpShiftLeftLogical: *hasResult = true; *hasResultType = true; break; + case OpBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpNot: *hasResult = true; *hasResultType = true; break; + case OpBitFieldInsert: *hasResult = true; *hasResultType = true; break; + case OpBitFieldSExtract: *hasResult = true; *hasResultType = true; break; + case OpBitFieldUExtract: *hasResult = true; *hasResultType = true; break; + case OpBitReverse: *hasResult = true; *hasResultType = true; break; + case OpBitCount: *hasResult = true; *hasResultType = true; break; + case OpDPdx: *hasResult = true; *hasResultType = true; break; + case OpDPdy: *hasResult = true; *hasResultType = true; break; + case OpFwidth: *hasResult = true; *hasResultType = true; break; + case OpDPdxFine: *hasResult = true; *hasResultType = true; break; + case OpDPdyFine: *hasResult = true; *hasResultType = true; break; + case OpFwidthFine: *hasResult = true; *hasResultType = true; break; + case OpDPdxCoarse: *hasResult = true; *hasResultType = true; break; + case OpDPdyCoarse: *hasResult = true; *hasResultType = true; break; + case OpFwidthCoarse: *hasResult = true; *hasResultType = true; break; + case OpEmitVertex: *hasResult = false; *hasResultType = false; break; + case OpEndPrimitive: *hasResult = false; *hasResultType = false; break; + case OpEmitStreamVertex: *hasResult = false; *hasResultType = false; break; + case OpEndStreamPrimitive: *hasResult = false; *hasResultType = false; break; + case OpControlBarrier: *hasResult = false; *hasResultType = false; break; + case OpMemoryBarrier: *hasResult = false; *hasResultType = false; break; + case OpAtomicLoad: *hasResult = true; *hasResultType = true; break; + case OpAtomicStore: *hasResult = false; *hasResultType = false; break; + case OpAtomicExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchange: *hasResult = true; *hasResultType = true; break; + case OpAtomicCompareExchangeWeak: *hasResult = true; *hasResultType = true; break; + case OpAtomicIIncrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIDecrement: *hasResult = true; *hasResultType = true; break; + case OpAtomicIAdd: *hasResult = true; *hasResultType = true; break; + case OpAtomicISub: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMin: *hasResult = true; *hasResultType = true; break; + case OpAtomicSMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicUMax: *hasResult = true; *hasResultType = true; break; + case OpAtomicAnd: *hasResult = true; *hasResultType = true; break; + case OpAtomicOr: *hasResult = true; *hasResultType = true; break; + case OpAtomicXor: *hasResult = true; *hasResultType = true; break; + case OpPhi: *hasResult = true; *hasResultType = true; break; + case OpLoopMerge: *hasResult = false; *hasResultType = false; break; + case OpSelectionMerge: *hasResult = false; *hasResultType = false; break; + case OpLabel: *hasResult = true; *hasResultType = false; break; + case OpBranch: *hasResult = false; *hasResultType = false; break; + case OpBranchConditional: *hasResult = false; *hasResultType = false; break; + case OpSwitch: *hasResult = false; *hasResultType = false; break; + case OpKill: *hasResult = false; *hasResultType = false; break; + case OpReturn: *hasResult = false; *hasResultType = false; break; + case OpReturnValue: *hasResult = false; *hasResultType = false; break; + case OpUnreachable: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStart: *hasResult = false; *hasResultType = false; break; + case OpLifetimeStop: *hasResult = false; *hasResultType = false; break; + case OpGroupAsyncCopy: *hasResult = true; *hasResultType = true; break; + case OpGroupWaitEvents: *hasResult = false; *hasResultType = false; break; + case OpGroupAll: *hasResult = true; *hasResultType = true; break; + case OpGroupAny: *hasResult = true; *hasResultType = true; break; + case OpGroupBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupSMax: *hasResult = true; *hasResultType = true; break; + case OpReadPipe: *hasResult = true; *hasResultType = true; break; + case OpWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReservedReadPipe: *hasResult = true; *hasResultType = true; break; + case OpReservedWritePipe: *hasResult = true; *hasResultType = true; break; + case OpReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpIsValidReserveId: *hasResult = true; *hasResultType = true; break; + case OpGetNumPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGetMaxPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveReadPipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupReserveWritePipePackets: *hasResult = true; *hasResultType = true; break; + case OpGroupCommitReadPipe: *hasResult = false; *hasResultType = false; break; + case OpGroupCommitWritePipe: *hasResult = false; *hasResultType = false; break; + case OpEnqueueMarker: *hasResult = true; *hasResultType = true; break; + case OpEnqueueKernel: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeSubGroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelNDrangeMaxSubGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelWorkGroupSize: *hasResult = true; *hasResultType = true; break; + case OpGetKernelPreferredWorkGroupSizeMultiple: *hasResult = true; *hasResultType = true; break; + case OpRetainEvent: *hasResult = false; *hasResultType = false; break; + case OpReleaseEvent: *hasResult = false; *hasResultType = false; break; + case OpCreateUserEvent: *hasResult = true; *hasResultType = true; break; + case OpIsValidEvent: *hasResult = true; *hasResultType = true; break; + case OpSetUserEventStatus: *hasResult = false; *hasResultType = false; break; + case OpCaptureEventProfilingInfo: *hasResult = false; *hasResultType = false; break; + case OpGetDefaultQueue: *hasResult = true; *hasResultType = true; break; + case OpBuildNDRange: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefImplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseSampleProjDrefExplicitLod: *hasResult = true; *hasResultType = true; break; + case OpImageSparseFetch: *hasResult = true; *hasResultType = true; break; + case OpImageSparseGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseDrefGather: *hasResult = true; *hasResultType = true; break; + case OpImageSparseTexelsResident: *hasResult = true; *hasResultType = true; break; + case OpNoLine: *hasResult = false; *hasResultType = false; break; + case OpAtomicFlagTestAndSet: *hasResult = true; *hasResultType = true; break; + case OpAtomicFlagClear: *hasResult = false; *hasResultType = false; break; + case OpImageSparseRead: *hasResult = true; *hasResultType = true; break; + case OpSizeOf: *hasResult = true; *hasResultType = true; break; + case OpTypePipeStorage: *hasResult = true; *hasResultType = false; break; + case OpConstantPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpCreatePipeFromPipeStorage: *hasResult = true; *hasResultType = true; break; + case OpGetKernelLocalSizeForSubgroupCount: *hasResult = true; *hasResultType = true; break; + case OpGetKernelMaxNumSubgroups: *hasResult = true; *hasResultType = true; break; + case OpTypeNamedBarrier: *hasResult = true; *hasResultType = false; break; + case OpNamedBarrierInitialize: *hasResult = true; *hasResultType = true; break; + case OpMemoryNamedBarrier: *hasResult = false; *hasResultType = false; break; + case OpModuleProcessed: *hasResult = false; *hasResultType = false; break; + case OpExecutionModeId: *hasResult = false; *hasResultType = false; break; + case OpDecorateId: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformElect: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAll: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAny: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformAllEqual: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBroadcastFirst: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformInverseBallot: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitExtract: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotBitCount: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindLSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBallotFindMSB: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffle: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleUp: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformShuffleDown: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFAdd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformIMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMul: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMin: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformSMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformUMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformFMax: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformBitwiseXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalAnd: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalOr: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformLogicalXor: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadBroadcast: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadSwap: *hasResult = true; *hasResultType = true; break; + case OpCopyLogical: *hasResult = true; *hasResultType = true; break; + case OpPtrEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrNotEqual: *hasResult = true; *hasResultType = true; break; + case OpPtrDiff: *hasResult = true; *hasResultType = true; break; + case OpColorAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpDepthAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpStencilAttachmentReadEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeTensorARM: *hasResult = true; *hasResultType = false; break; + case OpTensorReadARM: *hasResult = true; *hasResultType = true; break; + case OpTensorWriteARM: *hasResult = false; *hasResultType = false; break; + case OpTensorQuerySizeARM: *hasResult = true; *hasResultType = true; break; + case OpGraphConstantARM: *hasResult = true; *hasResultType = true; break; + case OpGraphEntryPointARM: *hasResult = false; *hasResultType = false; break; + case OpGraphARM: *hasResult = true; *hasResultType = true; break; + case OpGraphInputARM: *hasResult = true; *hasResultType = true; break; + case OpGraphSetOutputARM: *hasResult = false; *hasResultType = false; break; + case OpGraphEndARM: *hasResult = false; *hasResultType = false; break; + case OpTypeGraphARM: *hasResult = true; *hasResultType = false; break; + case OpTerminateInvocation: *hasResult = false; *hasResultType = false; break; + case OpTypeUntypedPointerKHR: *hasResult = true; *hasResultType = false; break; + case OpUntypedVariableKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedInBoundsAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBallotKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupFirstInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedInBoundsPtrAccessChainKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedArrayLengthKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedPrefetchKHR: *hasResult = false; *hasResultType = false; break; + case OpFmaKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAllEqualKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformRotateKHR: *hasResult = true; *hasResultType = true; break; + case OpSubgroupReadInvocationKHR: *hasResult = true; *hasResultType = true; break; + case OpExtInstWithForwardRefsKHR: *hasResult = true; *hasResultType = true; break; + case OpUntypedGroupAsyncCopyKHR: *hasResult = true; *hasResultType = true; break; + case OpTraceRayKHR: *hasResult = false; *hasResultType = false; break; + case OpExecuteCallableKHR: *hasResult = false; *hasResultType = false; break; + case OpConvertUToAccelerationStructureKHR: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayKHR: *hasResult = false; *hasResultType = false; break; + case OpSDot: *hasResult = true; *hasResultType = true; break; + case OpUDot: *hasResult = true; *hasResultType = true; break; + case OpSUDot: *hasResult = true; *hasResultType = true; break; + case OpSDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpUDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpSUDotAccSat: *hasResult = true; *hasResultType = true; break; + case OpTypeCooperativeMatrixKHR: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadKHR: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreKHR: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddKHR: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthKHR: *hasResult = true; *hasResultType = true; break; + case OpConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantCompositeReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstructReplicateEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeRayQueryKHR: *hasResult = true; *hasResultType = false; break; + case OpRayQueryInitializeKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryTerminateKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGenerateIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryConfirmIntersectionKHR: *hasResult = false; *hasResultType = false; break; + case OpRayQueryProceedKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTypeKHR: *hasResult = true; *hasResultType = true; break; + case OpImageSampleWeightedQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBoxFilterQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpBitCastArrayQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchWindowSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchWindowSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchGatherSSDQCOM: *hasResult = true; *hasResultType = true; break; + case OpImageBlockMatchGatherSADQCOM: *hasResult = true; *hasResultType = true; break; + case OpCompositeConstructCoopMatQCOM: *hasResult = true; *hasResultType = true; break; + case OpCompositeExtractCoopMatQCOM: *hasResult = true; *hasResultType = true; break; + case OpExtractSubArrayQCOM: *hasResult = true; *hasResultType = true; break; + case OpGroupIAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFAddNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMinNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupFMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupUMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpGroupSMaxNonUniformAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentMaskFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpFragmentFetchAMD: *hasResult = true; *hasResultType = true; break; + case OpReadClockKHR: *hasResult = true; *hasResultType = true; break; + case OpAllocateNodePayloadsAMDX: *hasResult = true; *hasResultType = true; break; + case OpEnqueueNodePayloadsAMDX: *hasResult = false; *hasResultType = false; break; + case OpTypeNodePayloadArrayAMDX: *hasResult = true; *hasResultType = false; break; + case OpFinishWritingNodePayloadAMDX: *hasResult = true; *hasResultType = true; break; + case OpNodePayloadArrayLengthAMDX: *hasResult = true; *hasResultType = true; break; + case OpIsNodePayloadValidAMDX: *hasResult = true; *hasResultType = true; break; + case OpConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case OpSpecConstantStringAMDX: *hasResult = true; *hasResultType = false; break; + case OpGroupNonUniformQuadAllKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupNonUniformQuadAnyKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeBufferEXT: *hasResult = true; *hasResultType = false; break; + case OpBufferPointerEXT: *hasResult = true; *hasResultType = true; break; + case OpAbortKHR: *hasResult = false; *hasResultType = false; break; + case OpUntypedImageTexelPointerEXT: *hasResult = true; *hasResultType = true; break; + case OpMemberDecorateIdEXT: *hasResult = false; *hasResultType = false; break; + case OpConstantSizeOfEXT: *hasResult = true; *hasResultType = true; break; + case OpConstantDataKHR: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantDataKHR: *hasResult = true; *hasResultType = true; break; + case OpPoisonKHR: *hasResult = true; *hasResultType = true; break; + case OpFreezeKHR: *hasResult = true; *hasResultType = true; break; + case OpHitObjectRecordHitMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitWithIndexMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetWorldToObjectNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectToWorldNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayOriginNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetShaderRecordBufferHandleNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetShaderBindingTableRecordIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectRecordEmptyNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectTraceRayNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordHitWithIndexNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectExecuteShaderNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetCurrentTimeNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetAttributesNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetHitKindNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetPrimitiveIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetGeometryIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceIdNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceCustomIndexNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayDirectionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayOriginNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMaxNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMinNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsEmptyNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsHitNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsMissNV: *hasResult = true; *hasResultType = true; break; + case OpReorderThreadWithHitObjectNV: *hasResult = false; *hasResultType = false; break; + case OpReorderThreadWithHintNV: *hasResult = false; *hasResultType = false; break; + case OpTypeHitObjectNV: *hasResult = true; *hasResultType = false; break; + case OpImageSampleFootprintNV: *hasResult = true; *hasResultType = true; break; + case OpTypeVectorIdEXT: *hasResult = true; *hasResultType = false; break; + case OpCooperativeVectorMatrixMulNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorOuterProductAccumulateNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeVectorReduceSumAccumulateNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeVectorMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixConvertNV: *hasResult = true; *hasResultType = true; break; + case OpEmitMeshTasksEXT: *hasResult = false; *hasResultType = false; break; + case OpSetMeshOutputsEXT: *hasResult = false; *hasResultType = false; break; + case OpGroupNonUniformPartitionEXT: *hasResult = true; *hasResultType = true; break; + case OpWritePackedPrimitiveIndices4x8NV: *hasResult = false; *hasResultType = false; break; + case OpFetchMicroTriangleVertexPositionNV: *hasResult = true; *hasResultType = true; break; + case OpFetchMicroTriangleVertexBarycentricNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeVectorStoreNV: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordFromQueryEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordMissMotionEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetIntersectionTriangleVertexPositionsEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayFlagsEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectSetShaderBindingTableRecordIndexEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectReorderExecuteShaderEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectTraceReorderExecuteEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectTraceMotionReorderExecuteEXT: *hasResult = false; *hasResultType = false; break; + case OpTypeHitObjectEXT: *hasResult = true; *hasResultType = false; break; + case OpReorderThreadWithHintEXT: *hasResult = false; *hasResultType = false; break; + case OpReorderThreadWithHitObjectEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectTraceRayEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectTraceRayMotionEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectRecordEmptyEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectExecuteShaderEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetCurrentTimeEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetAttributesEXT: *hasResult = false; *hasResultType = false; break; + case OpHitObjectGetHitKindEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetPrimitiveIndexEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetGeometryIndexEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceIdEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetInstanceCustomIndexEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayOriginEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectRayDirectionEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayDirectionEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldRayOriginEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetObjectToWorldEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetWorldToObjectEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMaxEXT: *hasResult = true; *hasResultType = true; break; + case OpReportIntersectionKHR: *hasResult = true; *hasResultType = true; break; + case OpIgnoreIntersectionNV: *hasResult = false; *hasResultType = false; break; + case OpTerminateRayNV: *hasResult = false; *hasResultType = false; break; + case OpTraceNV: *hasResult = false; *hasResultType = false; break; + case OpTraceMotionNV: *hasResult = false; *hasResultType = false; break; + case OpTraceRayMotionNV: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: *hasResult = true; *hasResultType = true; break; + case OpTypeAccelerationStructureKHR: *hasResult = true; *hasResultType = false; break; + case OpExecuteCallableNV: *hasResult = false; *hasResultType = false; break; + case OpRayQueryGetIntersectionClusterIdNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetClusterIdNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetRayTMinEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetShaderBindingTableRecordIndexEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetShaderRecordBufferHandleEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsEmptyEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsHitEXT: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsMissEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeCooperativeMatrixNV: *hasResult = true; *hasResultType = false; break; + case OpCooperativeMatrixLoadNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixMulAddNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLengthNV: *hasResult = true; *hasResultType = true; break; + case OpBeginInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpEndInvocationInterlockEXT: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixReduceNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixLoadTensorNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixStoreTensorNV: *hasResult = false; *hasResultType = false; break; + case OpCooperativeMatrixPerElementOpNV: *hasResult = true; *hasResultType = true; break; + case OpTypeTensorLayoutNV: *hasResult = true; *hasResultType = false; break; + case OpTypeTensorViewNV: *hasResult = true; *hasResultType = false; break; + case OpCreateTensorLayoutNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetStrideNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSliceNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetClampValueNV: *hasResult = true; *hasResultType = true; break; + case OpCreateTensorViewNV: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetDimensionNV: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetStrideNV: *hasResult = true; *hasResultType = true; break; + case OpDemoteToHelperInvocation: *hasResult = false; *hasResultType = false; break; + case OpIsHelperInvocationEXT: *hasResult = true; *hasResultType = true; break; + case OpTensorViewSetClipNV: *hasResult = true; *hasResultType = true; break; + case OpTensorLayoutSetBlockSizeNV: *hasResult = true; *hasResultType = true; break; + case OpCooperativeMatrixTransposeNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSamplerNV: *hasResult = true; *hasResultType = true; break; + case OpConvertImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSamplerToUNV: *hasResult = true; *hasResultType = true; break; + case OpConvertUToSampledImageNV: *hasResult = true; *hasResultType = true; break; + case OpConvertSampledImageToUNV: *hasResult = true; *hasResultType = true; break; + case OpSamplerImageAddressingModeNV: *hasResult = false; *hasResultType = false; break; + case OpRawAccessChainNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionLSSHitValueNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetSpherePositionNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetSphereRadiusNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetLSSPositionsNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectGetLSSRadiiNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case OpHitObjectIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryIsSphereHitNV: *hasResult = true; *hasResultType = true; break; + case OpRayQueryIsLSSHitNV: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleDownINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleUpINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupShuffleXorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupImageMediaBlockReadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupImageMediaBlockWriteINTEL: *hasResult = false; *hasResultType = false; break; + case OpUCountLeadingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpUCountTrailingZerosINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsISubINTEL: *hasResult = true; *hasResultType = true; break; + case OpAbsUSubINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAddSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageINTEL: *hasResult = true; *hasResultType = true; break; + case OpIAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpUAverageRoundedINTEL: *hasResult = true; *hasResultType = true; break; + case OpISubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpUSubSatINTEL: *hasResult = true; *hasResultType = true; break; + case OpIMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpUMul32x16INTEL: *hasResult = true; *hasResultType = true; break; + case OpConstantFunctionPointerINTEL: *hasResult = true; *hasResultType = true; break; + case OpFunctionPointerCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpAsmTargetINTEL: *hasResult = true; *hasResultType = false; break; + case OpAsmINTEL: *hasResult = true; *hasResultType = true; break; + case OpAsmCallINTEL: *hasResult = true; *hasResultType = true; break; + case OpAtomicFMinEXT: *hasResult = true; *hasResultType = true; break; + case OpAtomicFMaxEXT: *hasResult = true; *hasResultType = true; break; + case OpAssumeTrueKHR: *hasResult = false; *hasResultType = false; break; + case OpExpectKHR: *hasResult = true; *hasResultType = true; break; + case OpDecorateString: *hasResult = false; *hasResultType = false; break; + case OpMemberDecorateString: *hasResult = false; *hasResultType = false; break; + case OpVmeImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpTypeVmeImageINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicPayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMcePayloadINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcMceResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcImeDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcRefResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeAvcSicResultINTEL: *hasResult = true; *hasResultType = false; break; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToImeResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToRefResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceConvertToSicResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterDirectionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeRefWindowSizeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeSetWeightedSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetBorderReachedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcFmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcBmeInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcRefConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicInitializeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureSkcINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateIpeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicConvertToMceResultINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: *hasResult = true; *hasResultType = true; break; + case OpSubgroupAvcSicGetInterRawSadsINTEL: *hasResult = true; *hasResultType = true; break; + case OpVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case OpSaveMemoryINTEL: *hasResult = true; *hasResultType = true; break; + case OpRestoreMemoryINTEL: *hasResult = false; *hasResultType = false; break; + case OpArbitraryFloatSinCosPiALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastFromIntALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCastToIntALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatAddALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSubALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatMulALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatDivALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatGTALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatGEALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLTALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLEALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatEQALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatRecipALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatRSqrtALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCbrtALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatHypotALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSqrtALTERA: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLogINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog10INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatLog1pINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExpINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExp2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExp10INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatExpm1INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinCosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatSinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatCosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatASinINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatASinPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatACosINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatACosPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATanINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATanPiINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatATan2INTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowRINTEL: *hasResult = true; *hasResultType = true; break; + case OpArbitraryFloatPowNINTEL: *hasResult = true; *hasResultType = true; break; + case OpLoopControlINTEL: *hasResult = false; *hasResultType = false; break; + case OpAliasDomainDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpAliasScopeDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpAliasScopeListDeclINTEL: *hasResult = true; *hasResultType = false; break; + case OpFixedSqrtALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedRecipALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedRsqrtALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedSinALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedCosALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedSinCosALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedSinPiALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedCosPiALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedSinCosPiALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedLogALTERA: *hasResult = true; *hasResultType = true; break; + case OpFixedExpALTERA: *hasResult = true; *hasResultType = true; break; + case OpPtrCastToCrossWorkgroupALTERA: *hasResult = true; *hasResultType = true; break; + case OpCrossWorkgroupCastToPtrALTERA: *hasResult = true; *hasResultType = true; break; + case OpReadPipeBlockingALTERA: *hasResult = true; *hasResultType = true; break; + case OpWritePipeBlockingALTERA: *hasResult = true; *hasResultType = true; break; + case OpFPGARegALTERA: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayTMinKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetRayFlagsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionTKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceIdKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionGeometryIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionBarycentricsKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionFrontFaceKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayDirectionKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetWorldRayOriginKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionObjectToWorldKHR: *hasResult = true; *hasResultType = true; break; + case OpRayQueryGetIntersectionWorldToObjectKHR: *hasResult = true; *hasResultType = true; break; + case OpAtomicFAddEXT: *hasResult = true; *hasResultType = true; break; + case OpTypeBufferSurfaceINTEL: *hasResult = true; *hasResultType = false; break; + case OpTypeStructContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpSpecConstantCompositeContinuedINTEL: *hasResult = false; *hasResultType = false; break; + case OpCompositeConstructContinuedINTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertFToBF16INTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertBF16ToFINTEL: *hasResult = true; *hasResultType = true; break; + case OpControlBarrierArriveINTEL: *hasResult = false; *hasResultType = false; break; + case OpControlBarrierWaitINTEL: *hasResult = false; *hasResultType = false; break; + case OpArithmeticFenceEXT: *hasResult = true; *hasResultType = true; break; + case OpTaskSequenceCreateALTERA: *hasResult = true; *hasResultType = true; break; + case OpTaskSequenceAsyncALTERA: *hasResult = false; *hasResultType = false; break; + case OpTaskSequenceGetALTERA: *hasResult = true; *hasResultType = true; break; + case OpTaskSequenceReleaseALTERA: *hasResult = false; *hasResultType = false; break; + case OpTypeTaskSequenceALTERA: *hasResult = true; *hasResultType = false; break; + case OpSubgroupBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockLoadINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockLoadTransformINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockLoadTransposeINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockPrefetchINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroup2DBlockStoreINTEL: *hasResult = false; *hasResultType = false; break; + case OpSubgroupMatrixMultiplyAccumulateINTEL: *hasResult = true; *hasResultType = true; break; + case OpBitwiseFunctionINTEL: *hasResult = true; *hasResultType = true; break; + case OpUntypedVariableLengthArrayINTEL: *hasResult = true; *hasResultType = true; break; + case OpConditionalExtensionINTEL: *hasResult = false; *hasResultType = false; break; + case OpConditionalEntryPointINTEL: *hasResult = false; *hasResultType = false; break; + case OpConditionalCapabilityINTEL: *hasResult = false; *hasResultType = false; break; + case OpSpecConstantTargetINTEL: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantArchitectureINTEL: *hasResult = true; *hasResultType = true; break; + case OpSpecConstantCapabilitiesINTEL: *hasResult = true; *hasResultType = true; break; + case OpConditionalCopyObjectINTEL: *hasResult = true; *hasResultType = true; break; + case OpGroupIMulKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupFMulKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseAndKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseOrKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupBitwiseXorKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalAndKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalOrKHR: *hasResult = true; *hasResultType = true; break; + case OpGroupLogicalXorKHR: *hasResult = true; *hasResultType = true; break; + case OpRoundFToTF32INTEL: *hasResult = true; *hasResultType = true; break; + case OpMaskedGatherINTEL: *hasResult = true; *hasResultType = true; break; + case OpMaskedScatterINTEL: *hasResult = false; *hasResultType = false; break; + case OpConvertHandleToImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertHandleToSamplerINTEL: *hasResult = true; *hasResultType = true; break; + case OpConvertHandleToSampledImageINTEL: *hasResult = true; *hasResultType = true; break; + case OpFDot2MixAcc32VALVE: *hasResult = true; *hasResultType = true; break; + case OpFDot2MixAcc16VALVE: *hasResult = true; *hasResultType = true; break; + case OpFDot4MixAcc32VALVE: *hasResult = true; *hasResultType = true; break; + } +} +inline const char* SourceLanguageToString(SourceLanguage value) { + switch (value) { + case SourceLanguageUnknown: return "Unknown"; + case SourceLanguageESSL: return "ESSL"; + case SourceLanguageGLSL: return "GLSL"; + case SourceLanguageOpenCL_C: return "OpenCL_C"; + case SourceLanguageOpenCL_CPP: return "OpenCL_CPP"; + case SourceLanguageHLSL: return "HLSL"; + case SourceLanguageCPP_for_OpenCL: return "CPP_for_OpenCL"; + case SourceLanguageSYCL: return "SYCL"; + case SourceLanguageHERO_C: return "HERO_C"; + case SourceLanguageNZSL: return "NZSL"; + case SourceLanguageWGSL: return "WGSL"; + case SourceLanguageSlang: return "Slang"; + case SourceLanguageZig: return "Zig"; + case SourceLanguageRust: return "Rust"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModelToString(ExecutionModel value) { + switch (value) { + case ExecutionModelVertex: return "Vertex"; + case ExecutionModelTessellationControl: return "TessellationControl"; + case ExecutionModelTessellationEvaluation: return "TessellationEvaluation"; + case ExecutionModelGeometry: return "Geometry"; + case ExecutionModelFragment: return "Fragment"; + case ExecutionModelGLCompute: return "GLCompute"; + case ExecutionModelKernel: return "Kernel"; + case ExecutionModelTaskNV: return "TaskNV"; + case ExecutionModelMeshNV: return "MeshNV"; + case ExecutionModelRayGenerationKHR: return "RayGenerationKHR"; + case ExecutionModelIntersectionKHR: return "IntersectionKHR"; + case ExecutionModelAnyHitKHR: return "AnyHitKHR"; + case ExecutionModelClosestHitKHR: return "ClosestHitKHR"; + case ExecutionModelMissKHR: return "MissKHR"; + case ExecutionModelCallableKHR: return "CallableKHR"; + case ExecutionModelTaskEXT: return "TaskEXT"; + case ExecutionModelMeshEXT: return "MeshEXT"; + default: return "Unknown"; + } +} + +inline const char* AddressingModelToString(AddressingModel value) { + switch (value) { + case AddressingModelLogical: return "Logical"; + case AddressingModelPhysical32: return "Physical32"; + case AddressingModelPhysical64: return "Physical64"; + case AddressingModelPhysicalStorageBuffer64: return "PhysicalStorageBuffer64"; + default: return "Unknown"; + } +} + +inline const char* MemoryModelToString(MemoryModel value) { + switch (value) { + case MemoryModelSimple: return "Simple"; + case MemoryModelGLSL450: return "GLSL450"; + case MemoryModelOpenCL: return "OpenCL"; + case MemoryModelVulkan: return "Vulkan"; + default: return "Unknown"; + } +} + +inline const char* ExecutionModeToString(ExecutionMode value) { + switch (value) { + case ExecutionModeInvocations: return "Invocations"; + case ExecutionModeSpacingEqual: return "SpacingEqual"; + case ExecutionModeSpacingFractionalEven: return "SpacingFractionalEven"; + case ExecutionModeSpacingFractionalOdd: return "SpacingFractionalOdd"; + case ExecutionModeVertexOrderCw: return "VertexOrderCw"; + case ExecutionModeVertexOrderCcw: return "VertexOrderCcw"; + case ExecutionModePixelCenterInteger: return "PixelCenterInteger"; + case ExecutionModeOriginUpperLeft: return "OriginUpperLeft"; + case ExecutionModeOriginLowerLeft: return "OriginLowerLeft"; + case ExecutionModeEarlyFragmentTests: return "EarlyFragmentTests"; + case ExecutionModePointMode: return "PointMode"; + case ExecutionModeXfb: return "Xfb"; + case ExecutionModeDepthReplacing: return "DepthReplacing"; + case ExecutionModeDepthGreater: return "DepthGreater"; + case ExecutionModeDepthLess: return "DepthLess"; + case ExecutionModeDepthUnchanged: return "DepthUnchanged"; + case ExecutionModeLocalSize: return "LocalSize"; + case ExecutionModeLocalSizeHint: return "LocalSizeHint"; + case ExecutionModeInputPoints: return "InputPoints"; + case ExecutionModeInputLines: return "InputLines"; + case ExecutionModeInputLinesAdjacency: return "InputLinesAdjacency"; + case ExecutionModeTriangles: return "Triangles"; + case ExecutionModeInputTrianglesAdjacency: return "InputTrianglesAdjacency"; + case ExecutionModeQuads: return "Quads"; + case ExecutionModeIsolines: return "Isolines"; + case ExecutionModeOutputVertices: return "OutputVertices"; + case ExecutionModeOutputPoints: return "OutputPoints"; + case ExecutionModeOutputLineStrip: return "OutputLineStrip"; + case ExecutionModeOutputTriangleStrip: return "OutputTriangleStrip"; + case ExecutionModeVecTypeHint: return "VecTypeHint"; + case ExecutionModeContractionOff: return "ContractionOff"; + case ExecutionModeInitializer: return "Initializer"; + case ExecutionModeFinalizer: return "Finalizer"; + case ExecutionModeSubgroupSize: return "SubgroupSize"; + case ExecutionModeSubgroupsPerWorkgroup: return "SubgroupsPerWorkgroup"; + case ExecutionModeSubgroupsPerWorkgroupId: return "SubgroupsPerWorkgroupId"; + case ExecutionModeLocalSizeId: return "LocalSizeId"; + case ExecutionModeLocalSizeHintId: return "LocalSizeHintId"; + case ExecutionModeNonCoherentColorAttachmentReadEXT: return "NonCoherentColorAttachmentReadEXT"; + case ExecutionModeNonCoherentDepthAttachmentReadEXT: return "NonCoherentDepthAttachmentReadEXT"; + case ExecutionModeNonCoherentStencilAttachmentReadEXT: return "NonCoherentStencilAttachmentReadEXT"; + case ExecutionModeSubgroupUniformControlFlowKHR: return "SubgroupUniformControlFlowKHR"; + case ExecutionModePostDepthCoverage: return "PostDepthCoverage"; + case ExecutionModeDenormPreserve: return "DenormPreserve"; + case ExecutionModeDenormFlushToZero: return "DenormFlushToZero"; + case ExecutionModeSignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case ExecutionModeRoundingModeRTE: return "RoundingModeRTE"; + case ExecutionModeRoundingModeRTZ: return "RoundingModeRTZ"; + case ExecutionModeNonCoherentTileAttachmentReadQCOM: return "NonCoherentTileAttachmentReadQCOM"; + case ExecutionModeTileShadingRateQCOM: return "TileShadingRateQCOM"; + case ExecutionModeEarlyAndLateFragmentTestsAMD: return "EarlyAndLateFragmentTestsAMD"; + case ExecutionModeStencilRefReplacingEXT: return "StencilRefReplacingEXT"; + case ExecutionModeCoalescingAMDX: return "CoalescingAMDX"; + case ExecutionModeIsApiEntryAMDX: return "IsApiEntryAMDX"; + case ExecutionModeMaxNodeRecursionAMDX: return "MaxNodeRecursionAMDX"; + case ExecutionModeStaticNumWorkgroupsAMDX: return "StaticNumWorkgroupsAMDX"; + case ExecutionModeShaderIndexAMDX: return "ShaderIndexAMDX"; + case ExecutionModeMaxNumWorkgroupsAMDX: return "MaxNumWorkgroupsAMDX"; + case ExecutionModeStencilRefUnchangedFrontAMD: return "StencilRefUnchangedFrontAMD"; + case ExecutionModeStencilRefGreaterFrontAMD: return "StencilRefGreaterFrontAMD"; + case ExecutionModeStencilRefLessFrontAMD: return "StencilRefLessFrontAMD"; + case ExecutionModeStencilRefUnchangedBackAMD: return "StencilRefUnchangedBackAMD"; + case ExecutionModeStencilRefGreaterBackAMD: return "StencilRefGreaterBackAMD"; + case ExecutionModeStencilRefLessBackAMD: return "StencilRefLessBackAMD"; + case ExecutionModeQuadDerivativesKHR: return "QuadDerivativesKHR"; + case ExecutionModeRequireFullQuadsKHR: return "RequireFullQuadsKHR"; + case ExecutionModeSharesInputWithAMDX: return "SharesInputWithAMDX"; + case ExecutionModeArithmeticPoisonKHR: return "ArithmeticPoisonKHR"; + case ExecutionModeOutputLinesEXT: return "OutputLinesEXT"; + case ExecutionModeOutputPrimitivesEXT: return "OutputPrimitivesEXT"; + case ExecutionModeDerivativeGroupQuadsKHR: return "DerivativeGroupQuadsKHR"; + case ExecutionModeDerivativeGroupLinearKHR: return "DerivativeGroupLinearKHR"; + case ExecutionModeOutputTrianglesEXT: return "OutputTrianglesEXT"; + case ExecutionModePixelInterlockOrderedEXT: return "PixelInterlockOrderedEXT"; + case ExecutionModePixelInterlockUnorderedEXT: return "PixelInterlockUnorderedEXT"; + case ExecutionModeSampleInterlockOrderedEXT: return "SampleInterlockOrderedEXT"; + case ExecutionModeSampleInterlockUnorderedEXT: return "SampleInterlockUnorderedEXT"; + case ExecutionModeShadingRateInterlockOrderedEXT: return "ShadingRateInterlockOrderedEXT"; + case ExecutionModeShadingRateInterlockUnorderedEXT: return "ShadingRateInterlockUnorderedEXT"; + case ExecutionModeShader64BitIndexingEXT: return "Shader64BitIndexingEXT"; + case ExecutionModeSharedLocalMemorySizeINTEL: return "SharedLocalMemorySizeINTEL"; + case ExecutionModeRoundingModeRTPINTEL: return "RoundingModeRTPINTEL"; + case ExecutionModeRoundingModeRTNINTEL: return "RoundingModeRTNINTEL"; + case ExecutionModeFloatingPointModeALTINTEL: return "FloatingPointModeALTINTEL"; + case ExecutionModeFloatingPointModeIEEEINTEL: return "FloatingPointModeIEEEINTEL"; + case ExecutionModeMaxWorkgroupSizeINTEL: return "MaxWorkgroupSizeINTEL"; + case ExecutionModeMaxWorkDimINTEL: return "MaxWorkDimINTEL"; + case ExecutionModeNoGlobalOffsetINTEL: return "NoGlobalOffsetINTEL"; + case ExecutionModeNumSIMDWorkitemsINTEL: return "NumSIMDWorkitemsINTEL"; + case ExecutionModeSchedulerTargetFmaxMhzINTEL: return "SchedulerTargetFmaxMhzINTEL"; + case ExecutionModeMaximallyReconvergesKHR: return "MaximallyReconvergesKHR"; + case ExecutionModeFPFastMathDefault: return "FPFastMathDefault"; + case ExecutionModeStreamingInterfaceINTEL: return "StreamingInterfaceINTEL"; + case ExecutionModeRegisterMapInterfaceINTEL: return "RegisterMapInterfaceINTEL"; + case ExecutionModeNamedBarrierCountINTEL: return "NamedBarrierCountINTEL"; + case ExecutionModeMaximumRegistersINTEL: return "MaximumRegistersINTEL"; + case ExecutionModeMaximumRegistersIdINTEL: return "MaximumRegistersIdINTEL"; + case ExecutionModeNamedMaximumRegistersINTEL: return "NamedMaximumRegistersINTEL"; + default: return "Unknown"; + } +} + +inline const char* StorageClassToString(StorageClass value) { + switch (value) { + case StorageClassUniformConstant: return "UniformConstant"; + case StorageClassInput: return "Input"; + case StorageClassUniform: return "Uniform"; + case StorageClassOutput: return "Output"; + case StorageClassWorkgroup: return "Workgroup"; + case StorageClassCrossWorkgroup: return "CrossWorkgroup"; + case StorageClassPrivate: return "Private"; + case StorageClassFunction: return "Function"; + case StorageClassGeneric: return "Generic"; + case StorageClassPushConstant: return "PushConstant"; + case StorageClassAtomicCounter: return "AtomicCounter"; + case StorageClassImage: return "Image"; + case StorageClassStorageBuffer: return "StorageBuffer"; + case StorageClassTileImageEXT: return "TileImageEXT"; + case StorageClassTileAttachmentQCOM: return "TileAttachmentQCOM"; + case StorageClassNodePayloadAMDX: return "NodePayloadAMDX"; + case StorageClassCallableDataKHR: return "CallableDataKHR"; + case StorageClassIncomingCallableDataKHR: return "IncomingCallableDataKHR"; + case StorageClassRayPayloadKHR: return "RayPayloadKHR"; + case StorageClassHitAttributeKHR: return "HitAttributeKHR"; + case StorageClassIncomingRayPayloadKHR: return "IncomingRayPayloadKHR"; + case StorageClassShaderRecordBufferKHR: return "ShaderRecordBufferKHR"; + case StorageClassPhysicalStorageBuffer: return "PhysicalStorageBuffer"; + case StorageClassHitObjectAttributeNV: return "HitObjectAttributeNV"; + case StorageClassTaskPayloadWorkgroupEXT: return "TaskPayloadWorkgroupEXT"; + case StorageClassHitObjectAttributeEXT: return "HitObjectAttributeEXT"; + case StorageClassCodeSectionINTEL: return "CodeSectionINTEL"; + case StorageClassDeviceOnlyALTERA: return "DeviceOnlyALTERA"; + case StorageClassHostOnlyALTERA: return "HostOnlyALTERA"; + default: return "Unknown"; + } +} + +inline const char* DimToString(Dim value) { + switch (value) { + case Dim1D: return "1D"; + case Dim2D: return "2D"; + case Dim3D: return "3D"; + case DimCube: return "Cube"; + case DimRect: return "Rect"; + case DimBuffer: return "Buffer"; + case DimSubpassData: return "SubpassData"; + case DimTileImageDataEXT: return "TileImageDataEXT"; + default: return "Unknown"; + } +} + +inline const char* SamplerAddressingModeToString(SamplerAddressingMode value) { + switch (value) { + case SamplerAddressingModeNone: return "None"; + case SamplerAddressingModeClampToEdge: return "ClampToEdge"; + case SamplerAddressingModeClamp: return "Clamp"; + case SamplerAddressingModeRepeat: return "Repeat"; + case SamplerAddressingModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* SamplerFilterModeToString(SamplerFilterMode value) { + switch (value) { + case SamplerFilterModeNearest: return "Nearest"; + case SamplerFilterModeLinear: return "Linear"; + default: return "Unknown"; + } +} + +inline const char* ImageFormatToString(ImageFormat value) { + switch (value) { + case ImageFormatUnknown: return "Unknown"; + case ImageFormatRgba32f: return "Rgba32f"; + case ImageFormatRgba16f: return "Rgba16f"; + case ImageFormatR32f: return "R32f"; + case ImageFormatRgba8: return "Rgba8"; + case ImageFormatRgba8Snorm: return "Rgba8Snorm"; + case ImageFormatRg32f: return "Rg32f"; + case ImageFormatRg16f: return "Rg16f"; + case ImageFormatR11fG11fB10f: return "R11fG11fB10f"; + case ImageFormatR16f: return "R16f"; + case ImageFormatRgba16: return "Rgba16"; + case ImageFormatRgb10A2: return "Rgb10A2"; + case ImageFormatRg16: return "Rg16"; + case ImageFormatRg8: return "Rg8"; + case ImageFormatR16: return "R16"; + case ImageFormatR8: return "R8"; + case ImageFormatRgba16Snorm: return "Rgba16Snorm"; + case ImageFormatRg16Snorm: return "Rg16Snorm"; + case ImageFormatRg8Snorm: return "Rg8Snorm"; + case ImageFormatR16Snorm: return "R16Snorm"; + case ImageFormatR8Snorm: return "R8Snorm"; + case ImageFormatRgba32i: return "Rgba32i"; + case ImageFormatRgba16i: return "Rgba16i"; + case ImageFormatRgba8i: return "Rgba8i"; + case ImageFormatR32i: return "R32i"; + case ImageFormatRg32i: return "Rg32i"; + case ImageFormatRg16i: return "Rg16i"; + case ImageFormatRg8i: return "Rg8i"; + case ImageFormatR16i: return "R16i"; + case ImageFormatR8i: return "R8i"; + case ImageFormatRgba32ui: return "Rgba32ui"; + case ImageFormatRgba16ui: return "Rgba16ui"; + case ImageFormatRgba8ui: return "Rgba8ui"; + case ImageFormatR32ui: return "R32ui"; + case ImageFormatRgb10a2ui: return "Rgb10a2ui"; + case ImageFormatRg32ui: return "Rg32ui"; + case ImageFormatRg16ui: return "Rg16ui"; + case ImageFormatRg8ui: return "Rg8ui"; + case ImageFormatR16ui: return "R16ui"; + case ImageFormatR8ui: return "R8ui"; + case ImageFormatR64ui: return "R64ui"; + case ImageFormatR64i: return "R64i"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelOrderToString(ImageChannelOrder value) { + switch (value) { + case ImageChannelOrderR: return "R"; + case ImageChannelOrderA: return "A"; + case ImageChannelOrderRG: return "RG"; + case ImageChannelOrderRA: return "RA"; + case ImageChannelOrderRGB: return "RGB"; + case ImageChannelOrderRGBA: return "RGBA"; + case ImageChannelOrderBGRA: return "BGRA"; + case ImageChannelOrderARGB: return "ARGB"; + case ImageChannelOrderIntensity: return "Intensity"; + case ImageChannelOrderLuminance: return "Luminance"; + case ImageChannelOrderRx: return "Rx"; + case ImageChannelOrderRGx: return "RGx"; + case ImageChannelOrderRGBx: return "RGBx"; + case ImageChannelOrderDepth: return "Depth"; + case ImageChannelOrderDepthStencil: return "DepthStencil"; + case ImageChannelOrdersRGB: return "sRGB"; + case ImageChannelOrdersRGBx: return "sRGBx"; + case ImageChannelOrdersRGBA: return "sRGBA"; + case ImageChannelOrdersBGRA: return "sBGRA"; + case ImageChannelOrderABGR: return "ABGR"; + default: return "Unknown"; + } +} + +inline const char* ImageChannelDataTypeToString(ImageChannelDataType value) { + switch (value) { + case ImageChannelDataTypeSnormInt8: return "SnormInt8"; + case ImageChannelDataTypeSnormInt16: return "SnormInt16"; + case ImageChannelDataTypeUnormInt8: return "UnormInt8"; + case ImageChannelDataTypeUnormInt16: return "UnormInt16"; + case ImageChannelDataTypeUnormShort565: return "UnormShort565"; + case ImageChannelDataTypeUnormShort555: return "UnormShort555"; + case ImageChannelDataTypeUnormInt101010: return "UnormInt101010"; + case ImageChannelDataTypeSignedInt8: return "SignedInt8"; + case ImageChannelDataTypeSignedInt16: return "SignedInt16"; + case ImageChannelDataTypeSignedInt32: return "SignedInt32"; + case ImageChannelDataTypeUnsignedInt8: return "UnsignedInt8"; + case ImageChannelDataTypeUnsignedInt16: return "UnsignedInt16"; + case ImageChannelDataTypeUnsignedInt32: return "UnsignedInt32"; + case ImageChannelDataTypeHalfFloat: return "HalfFloat"; + case ImageChannelDataTypeFloat: return "Float"; + case ImageChannelDataTypeUnormInt24: return "UnormInt24"; + case ImageChannelDataTypeUnormInt101010_2: return "UnormInt101010_2"; + case ImageChannelDataTypeUnormInt10X6EXT: return "UnormInt10X6EXT"; + case ImageChannelDataTypeUnsignedIntRaw10EXT: return "UnsignedIntRaw10EXT"; + case ImageChannelDataTypeUnsignedIntRaw12EXT: return "UnsignedIntRaw12EXT"; + case ImageChannelDataTypeUnormInt2_101010EXT: return "UnormInt2_101010EXT"; + case ImageChannelDataTypeUnsignedInt10X6EXT: return "UnsignedInt10X6EXT"; + case ImageChannelDataTypeUnsignedInt12X4EXT: return "UnsignedInt12X4EXT"; + case ImageChannelDataTypeUnsignedInt14X2EXT: return "UnsignedInt14X2EXT"; + case ImageChannelDataTypeUnormInt12X4EXT: return "UnormInt12X4EXT"; + case ImageChannelDataTypeUnormInt14X2EXT: return "UnormInt14X2EXT"; + default: return "Unknown"; + } +} + +inline const char* FPRoundingModeToString(FPRoundingMode value) { + switch (value) { + case FPRoundingModeRTE: return "RTE"; + case FPRoundingModeRTZ: return "RTZ"; + case FPRoundingModeRTP: return "RTP"; + case FPRoundingModeRTN: return "RTN"; + default: return "Unknown"; + } +} + +inline const char* LinkageTypeToString(LinkageType value) { + switch (value) { + case LinkageTypeExport: return "Export"; + case LinkageTypeImport: return "Import"; + case LinkageTypeLinkOnceODR: return "LinkOnceODR"; + default: return "Unknown"; + } +} + +inline const char* AccessQualifierToString(AccessQualifier value) { + switch (value) { + case AccessQualifierReadOnly: return "ReadOnly"; + case AccessQualifierWriteOnly: return "WriteOnly"; + case AccessQualifierReadWrite: return "ReadWrite"; + default: return "Unknown"; + } +} + +inline const char* FunctionParameterAttributeToString(FunctionParameterAttribute value) { + switch (value) { + case FunctionParameterAttributeZext: return "Zext"; + case FunctionParameterAttributeSext: return "Sext"; + case FunctionParameterAttributeByVal: return "ByVal"; + case FunctionParameterAttributeSret: return "Sret"; + case FunctionParameterAttributeNoAlias: return "NoAlias"; + case FunctionParameterAttributeNoCapture: return "NoCapture"; + case FunctionParameterAttributeNoWrite: return "NoWrite"; + case FunctionParameterAttributeNoReadWrite: return "NoReadWrite"; + case FunctionParameterAttributeRuntimeAlignedALTERA: return "RuntimeAlignedALTERA"; + default: return "Unknown"; + } +} + +inline const char* DecorationToString(Decoration value) { + switch (value) { + case DecorationRelaxedPrecision: return "RelaxedPrecision"; + case DecorationSpecId: return "SpecId"; + case DecorationBlock: return "Block"; + case DecorationBufferBlock: return "BufferBlock"; + case DecorationRowMajor: return "RowMajor"; + case DecorationColMajor: return "ColMajor"; + case DecorationArrayStride: return "ArrayStride"; + case DecorationMatrixStride: return "MatrixStride"; + case DecorationGLSLShared: return "GLSLShared"; + case DecorationGLSLPacked: return "GLSLPacked"; + case DecorationCPacked: return "CPacked"; + case DecorationBuiltIn: return "BuiltIn"; + case DecorationNoPerspective: return "NoPerspective"; + case DecorationFlat: return "Flat"; + case DecorationPatch: return "Patch"; + case DecorationCentroid: return "Centroid"; + case DecorationSample: return "Sample"; + case DecorationInvariant: return "Invariant"; + case DecorationRestrict: return "Restrict"; + case DecorationAliased: return "Aliased"; + case DecorationVolatile: return "Volatile"; + case DecorationConstant: return "Constant"; + case DecorationCoherent: return "Coherent"; + case DecorationNonWritable: return "NonWritable"; + case DecorationNonReadable: return "NonReadable"; + case DecorationUniform: return "Uniform"; + case DecorationUniformId: return "UniformId"; + case DecorationSaturatedConversion: return "SaturatedConversion"; + case DecorationStream: return "Stream"; + case DecorationLocation: return "Location"; + case DecorationComponent: return "Component"; + case DecorationIndex: return "Index"; + case DecorationBinding: return "Binding"; + case DecorationDescriptorSet: return "DescriptorSet"; + case DecorationOffset: return "Offset"; + case DecorationXfbBuffer: return "XfbBuffer"; + case DecorationXfbStride: return "XfbStride"; + case DecorationFuncParamAttr: return "FuncParamAttr"; + case DecorationFPRoundingMode: return "FPRoundingMode"; + case DecorationFPFastMathMode: return "FPFastMathMode"; + case DecorationLinkageAttributes: return "LinkageAttributes"; + case DecorationNoContraction: return "NoContraction"; + case DecorationInputAttachmentIndex: return "InputAttachmentIndex"; + case DecorationAlignment: return "Alignment"; + case DecorationMaxByteOffset: return "MaxByteOffset"; + case DecorationAlignmentId: return "AlignmentId"; + case DecorationMaxByteOffsetId: return "MaxByteOffsetId"; + case DecorationSaturatedToLargestFloat8NormalConversionEXT: return "SaturatedToLargestFloat8NormalConversionEXT"; + case DecorationNoSignedWrap: return "NoSignedWrap"; + case DecorationNoUnsignedWrap: return "NoUnsignedWrap"; + case DecorationWeightTextureQCOM: return "WeightTextureQCOM"; + case DecorationBlockMatchTextureQCOM: return "BlockMatchTextureQCOM"; + case DecorationBlockMatchSamplerQCOM: return "BlockMatchSamplerQCOM"; + case DecorationExplicitInterpAMD: return "ExplicitInterpAMD"; + case DecorationNodeSharesPayloadLimitsWithAMDX: return "NodeSharesPayloadLimitsWithAMDX"; + case DecorationNodeMaxPayloadsAMDX: return "NodeMaxPayloadsAMDX"; + case DecorationTrackFinishWritingAMDX: return "TrackFinishWritingAMDX"; + case DecorationPayloadNodeNameAMDX: return "PayloadNodeNameAMDX"; + case DecorationPayloadNodeBaseIndexAMDX: return "PayloadNodeBaseIndexAMDX"; + case DecorationPayloadNodeSparseArrayAMDX: return "PayloadNodeSparseArrayAMDX"; + case DecorationPayloadNodeArraySizeAMDX: return "PayloadNodeArraySizeAMDX"; + case DecorationPayloadDispatchIndirectAMDX: return "PayloadDispatchIndirectAMDX"; + case DecorationArrayStrideIdEXT: return "ArrayStrideIdEXT"; + case DecorationOffsetIdEXT: return "OffsetIdEXT"; + case DecorationUTFEncodedKHR: return "UTFEncodedKHR"; + case DecorationOverrideCoverageNV: return "OverrideCoverageNV"; + case DecorationPassthroughNV: return "PassthroughNV"; + case DecorationViewportRelativeNV: return "ViewportRelativeNV"; + case DecorationSecondaryViewportRelativeNV: return "SecondaryViewportRelativeNV"; + case DecorationPerPrimitiveEXT: return "PerPrimitiveEXT"; + case DecorationPerViewNV: return "PerViewNV"; + case DecorationPerTaskNV: return "PerTaskNV"; + case DecorationPerVertexKHR: return "PerVertexKHR"; + case DecorationNonUniform: return "NonUniform"; + case DecorationRestrictPointer: return "RestrictPointer"; + case DecorationAliasedPointer: return "AliasedPointer"; + case DecorationMemberOffsetNV: return "MemberOffsetNV"; + case DecorationHitObjectShaderRecordBufferNV: return "HitObjectShaderRecordBufferNV"; + case DecorationHitObjectShaderRecordBufferEXT: return "HitObjectShaderRecordBufferEXT"; + case DecorationBankNV: return "BankNV"; + case DecorationBindlessSamplerNV: return "BindlessSamplerNV"; + case DecorationBindlessImageNV: return "BindlessImageNV"; + case DecorationBoundSamplerNV: return "BoundSamplerNV"; + case DecorationBoundImageNV: return "BoundImageNV"; + case DecorationSIMTCallINTEL: return "SIMTCallINTEL"; + case DecorationReferencedIndirectlyINTEL: return "ReferencedIndirectlyINTEL"; + case DecorationClobberINTEL: return "ClobberINTEL"; + case DecorationSideEffectsINTEL: return "SideEffectsINTEL"; + case DecorationVectorComputeVariableINTEL: return "VectorComputeVariableINTEL"; + case DecorationFuncParamIOKindINTEL: return "FuncParamIOKindINTEL"; + case DecorationVectorComputeFunctionINTEL: return "VectorComputeFunctionINTEL"; + case DecorationStackCallINTEL: return "StackCallINTEL"; + case DecorationGlobalVariableOffsetINTEL: return "GlobalVariableOffsetINTEL"; + case DecorationCounterBuffer: return "CounterBuffer"; + case DecorationHlslSemanticGOOGLE: return "HlslSemanticGOOGLE"; + case DecorationUserTypeGOOGLE: return "UserTypeGOOGLE"; + case DecorationFunctionRoundingModeINTEL: return "FunctionRoundingModeINTEL"; + case DecorationFunctionDenormModeINTEL: return "FunctionDenormModeINTEL"; + case DecorationRegisterALTERA: return "RegisterALTERA"; + case DecorationMemoryALTERA: return "MemoryALTERA"; + case DecorationNumbanksALTERA: return "NumbanksALTERA"; + case DecorationBankwidthALTERA: return "BankwidthALTERA"; + case DecorationMaxPrivateCopiesALTERA: return "MaxPrivateCopiesALTERA"; + case DecorationSinglepumpALTERA: return "SinglepumpALTERA"; + case DecorationDoublepumpALTERA: return "DoublepumpALTERA"; + case DecorationMaxReplicatesALTERA: return "MaxReplicatesALTERA"; + case DecorationSimpleDualPortALTERA: return "SimpleDualPortALTERA"; + case DecorationMergeALTERA: return "MergeALTERA"; + case DecorationBankBitsALTERA: return "BankBitsALTERA"; + case DecorationForcePow2DepthALTERA: return "ForcePow2DepthALTERA"; + case DecorationStridesizeALTERA: return "StridesizeALTERA"; + case DecorationWordsizeALTERA: return "WordsizeALTERA"; + case DecorationTrueDualPortALTERA: return "TrueDualPortALTERA"; + case DecorationBurstCoalesceALTERA: return "BurstCoalesceALTERA"; + case DecorationCacheSizeALTERA: return "CacheSizeALTERA"; + case DecorationDontStaticallyCoalesceALTERA: return "DontStaticallyCoalesceALTERA"; + case DecorationPrefetchALTERA: return "PrefetchALTERA"; + case DecorationStallEnableALTERA: return "StallEnableALTERA"; + case DecorationFuseLoopsInFunctionALTERA: return "FuseLoopsInFunctionALTERA"; + case DecorationMathOpDSPModeALTERA: return "MathOpDSPModeALTERA"; + case DecorationAliasScopeINTEL: return "AliasScopeINTEL"; + case DecorationNoAliasINTEL: return "NoAliasINTEL"; + case DecorationInitiationIntervalALTERA: return "InitiationIntervalALTERA"; + case DecorationMaxConcurrencyALTERA: return "MaxConcurrencyALTERA"; + case DecorationPipelineEnableALTERA: return "PipelineEnableALTERA"; + case DecorationBufferLocationALTERA: return "BufferLocationALTERA"; + case DecorationIOPipeStorageALTERA: return "IOPipeStorageALTERA"; + case DecorationFunctionFloatingPointModeINTEL: return "FunctionFloatingPointModeINTEL"; + case DecorationSingleElementVectorINTEL: return "SingleElementVectorINTEL"; + case DecorationVectorComputeCallableFunctionINTEL: return "VectorComputeCallableFunctionINTEL"; + case DecorationMediaBlockIOINTEL: return "MediaBlockIOINTEL"; + case DecorationStallFreeALTERA: return "StallFreeALTERA"; + case DecorationFPMaxErrorDecorationINTEL: return "FPMaxErrorDecorationINTEL"; + case DecorationLatencyControlLabelALTERA: return "LatencyControlLabelALTERA"; + case DecorationLatencyControlConstraintALTERA: return "LatencyControlConstraintALTERA"; + case DecorationConduitKernelArgumentALTERA: return "ConduitKernelArgumentALTERA"; + case DecorationRegisterMapKernelArgumentALTERA: return "RegisterMapKernelArgumentALTERA"; + case DecorationMMHostInterfaceAddressWidthALTERA: return "MMHostInterfaceAddressWidthALTERA"; + case DecorationMMHostInterfaceDataWidthALTERA: return "MMHostInterfaceDataWidthALTERA"; + case DecorationMMHostInterfaceLatencyALTERA: return "MMHostInterfaceLatencyALTERA"; + case DecorationMMHostInterfaceReadWriteModeALTERA: return "MMHostInterfaceReadWriteModeALTERA"; + case DecorationMMHostInterfaceMaxBurstALTERA: return "MMHostInterfaceMaxBurstALTERA"; + case DecorationMMHostInterfaceWaitRequestALTERA: return "MMHostInterfaceWaitRequestALTERA"; + case DecorationStableKernelArgumentALTERA: return "StableKernelArgumentALTERA"; + case DecorationHostAccessINTEL: return "HostAccessINTEL"; + case DecorationInitModeALTERA: return "InitModeALTERA"; + case DecorationImplementInRegisterMapALTERA: return "ImplementInRegisterMapALTERA"; + case DecorationConditionalINTEL: return "ConditionalINTEL"; + case DecorationCacheControlLoadINTEL: return "CacheControlLoadINTEL"; + case DecorationCacheControlStoreINTEL: return "CacheControlStoreINTEL"; + default: return "Unknown"; + } +} + +inline const char* BuiltInToString(BuiltIn value) { + switch (value) { + case BuiltInPosition: return "Position"; + case BuiltInPointSize: return "PointSize"; + case BuiltInClipDistance: return "ClipDistance"; + case BuiltInCullDistance: return "CullDistance"; + case BuiltInVertexId: return "VertexId"; + case BuiltInInstanceId: return "InstanceId"; + case BuiltInPrimitiveId: return "PrimitiveId"; + case BuiltInInvocationId: return "InvocationId"; + case BuiltInLayer: return "Layer"; + case BuiltInViewportIndex: return "ViewportIndex"; + case BuiltInTessLevelOuter: return "TessLevelOuter"; + case BuiltInTessLevelInner: return "TessLevelInner"; + case BuiltInTessCoord: return "TessCoord"; + case BuiltInPatchVertices: return "PatchVertices"; + case BuiltInFragCoord: return "FragCoord"; + case BuiltInPointCoord: return "PointCoord"; + case BuiltInFrontFacing: return "FrontFacing"; + case BuiltInSampleId: return "SampleId"; + case BuiltInSamplePosition: return "SamplePosition"; + case BuiltInSampleMask: return "SampleMask"; + case BuiltInFragDepth: return "FragDepth"; + case BuiltInHelperInvocation: return "HelperInvocation"; + case BuiltInNumWorkgroups: return "NumWorkgroups"; + case BuiltInWorkgroupSize: return "WorkgroupSize"; + case BuiltInWorkgroupId: return "WorkgroupId"; + case BuiltInLocalInvocationId: return "LocalInvocationId"; + case BuiltInGlobalInvocationId: return "GlobalInvocationId"; + case BuiltInLocalInvocationIndex: return "LocalInvocationIndex"; + case BuiltInWorkDim: return "WorkDim"; + case BuiltInGlobalSize: return "GlobalSize"; + case BuiltInEnqueuedWorkgroupSize: return "EnqueuedWorkgroupSize"; + case BuiltInGlobalOffset: return "GlobalOffset"; + case BuiltInGlobalLinearId: return "GlobalLinearId"; + case BuiltInSubgroupSize: return "SubgroupSize"; + case BuiltInSubgroupMaxSize: return "SubgroupMaxSize"; + case BuiltInNumSubgroups: return "NumSubgroups"; + case BuiltInNumEnqueuedSubgroups: return "NumEnqueuedSubgroups"; + case BuiltInSubgroupId: return "SubgroupId"; + case BuiltInSubgroupLocalInvocationId: return "SubgroupLocalInvocationId"; + case BuiltInVertexIndex: return "VertexIndex"; + case BuiltInInstanceIndex: return "InstanceIndex"; + case BuiltInCoreIDARM: return "CoreIDARM"; + case BuiltInCoreCountARM: return "CoreCountARM"; + case BuiltInCoreMaxIDARM: return "CoreMaxIDARM"; + case BuiltInWarpIDARM: return "WarpIDARM"; + case BuiltInWarpMaxIDARM: return "WarpMaxIDARM"; + case BuiltInSubgroupEqMask: return "SubgroupEqMask"; + case BuiltInSubgroupGeMask: return "SubgroupGeMask"; + case BuiltInSubgroupGtMask: return "SubgroupGtMask"; + case BuiltInSubgroupLeMask: return "SubgroupLeMask"; + case BuiltInSubgroupLtMask: return "SubgroupLtMask"; + case BuiltInBaseVertex: return "BaseVertex"; + case BuiltInBaseInstance: return "BaseInstance"; + case BuiltInDrawIndex: return "DrawIndex"; + case BuiltInPrimitiveShadingRateKHR: return "PrimitiveShadingRateKHR"; + case BuiltInDeviceIndex: return "DeviceIndex"; + case BuiltInViewIndex: return "ViewIndex"; + case BuiltInShadingRateKHR: return "ShadingRateKHR"; + case BuiltInTileOffsetQCOM: return "TileOffsetQCOM"; + case BuiltInTileDimensionQCOM: return "TileDimensionQCOM"; + case BuiltInTileApronSizeQCOM: return "TileApronSizeQCOM"; + case BuiltInBaryCoordNoPerspAMD: return "BaryCoordNoPerspAMD"; + case BuiltInBaryCoordNoPerspCentroidAMD: return "BaryCoordNoPerspCentroidAMD"; + case BuiltInBaryCoordNoPerspSampleAMD: return "BaryCoordNoPerspSampleAMD"; + case BuiltInBaryCoordSmoothAMD: return "BaryCoordSmoothAMD"; + case BuiltInBaryCoordSmoothCentroidAMD: return "BaryCoordSmoothCentroidAMD"; + case BuiltInBaryCoordSmoothSampleAMD: return "BaryCoordSmoothSampleAMD"; + case BuiltInBaryCoordPullModelAMD: return "BaryCoordPullModelAMD"; + case BuiltInFragStencilRefEXT: return "FragStencilRefEXT"; + case BuiltInRemainingRecursionLevelsAMDX: return "RemainingRecursionLevelsAMDX"; + case BuiltInShaderIndexAMDX: return "ShaderIndexAMDX"; + case BuiltInSamplerHeapEXT: return "SamplerHeapEXT"; + case BuiltInResourceHeapEXT: return "ResourceHeapEXT"; + case BuiltInViewportMaskNV: return "ViewportMaskNV"; + case BuiltInSecondaryPositionNV: return "SecondaryPositionNV"; + case BuiltInSecondaryViewportMaskNV: return "SecondaryViewportMaskNV"; + case BuiltInPositionPerViewNV: return "PositionPerViewNV"; + case BuiltInViewportMaskPerViewNV: return "ViewportMaskPerViewNV"; + case BuiltInFullyCoveredEXT: return "FullyCoveredEXT"; + case BuiltInTaskCountNV: return "TaskCountNV"; + case BuiltInPrimitiveCountNV: return "PrimitiveCountNV"; + case BuiltInPrimitiveIndicesNV: return "PrimitiveIndicesNV"; + case BuiltInClipDistancePerViewNV: return "ClipDistancePerViewNV"; + case BuiltInCullDistancePerViewNV: return "CullDistancePerViewNV"; + case BuiltInLayerPerViewNV: return "LayerPerViewNV"; + case BuiltInMeshViewCountNV: return "MeshViewCountNV"; + case BuiltInMeshViewIndicesNV: return "MeshViewIndicesNV"; + case BuiltInBaryCoordKHR: return "BaryCoordKHR"; + case BuiltInBaryCoordNoPerspKHR: return "BaryCoordNoPerspKHR"; + case BuiltInFragSizeEXT: return "FragSizeEXT"; + case BuiltInFragInvocationCountEXT: return "FragInvocationCountEXT"; + case BuiltInPrimitivePointIndicesEXT: return "PrimitivePointIndicesEXT"; + case BuiltInPrimitiveLineIndicesEXT: return "PrimitiveLineIndicesEXT"; + case BuiltInPrimitiveTriangleIndicesEXT: return "PrimitiveTriangleIndicesEXT"; + case BuiltInCullPrimitiveEXT: return "CullPrimitiveEXT"; + case BuiltInLaunchIdKHR: return "LaunchIdKHR"; + case BuiltInLaunchSizeKHR: return "LaunchSizeKHR"; + case BuiltInWorldRayOriginKHR: return "WorldRayOriginKHR"; + case BuiltInWorldRayDirectionKHR: return "WorldRayDirectionKHR"; + case BuiltInObjectRayOriginKHR: return "ObjectRayOriginKHR"; + case BuiltInObjectRayDirectionKHR: return "ObjectRayDirectionKHR"; + case BuiltInRayTminKHR: return "RayTminKHR"; + case BuiltInRayTmaxKHR: return "RayTmaxKHR"; + case BuiltInInstanceCustomIndexKHR: return "InstanceCustomIndexKHR"; + case BuiltInObjectToWorldKHR: return "ObjectToWorldKHR"; + case BuiltInWorldToObjectKHR: return "WorldToObjectKHR"; + case BuiltInHitTNV: return "HitTNV"; + case BuiltInHitKindKHR: return "HitKindKHR"; + case BuiltInCurrentRayTimeNV: return "CurrentRayTimeNV"; + case BuiltInHitTriangleVertexPositionsKHR: return "HitTriangleVertexPositionsKHR"; + case BuiltInHitMicroTriangleVertexPositionsNV: return "HitMicroTriangleVertexPositionsNV"; + case BuiltInHitMicroTriangleVertexBarycentricsNV: return "HitMicroTriangleVertexBarycentricsNV"; + case BuiltInIncomingRayFlagsKHR: return "IncomingRayFlagsKHR"; + case BuiltInRayGeometryIndexKHR: return "RayGeometryIndexKHR"; + case BuiltInHitIsSphereNV: return "HitIsSphereNV"; + case BuiltInHitIsLSSNV: return "HitIsLSSNV"; + case BuiltInHitSpherePositionNV: return "HitSpherePositionNV"; + case BuiltInWarpsPerSMNV: return "WarpsPerSMNV"; + case BuiltInSMCountNV: return "SMCountNV"; + case BuiltInWarpIDNV: return "WarpIDNV"; + case BuiltInSMIDNV: return "SMIDNV"; + case BuiltInHitLSSPositionsNV: return "HitLSSPositionsNV"; + case BuiltInHitKindFrontFacingMicroTriangleNV: return "HitKindFrontFacingMicroTriangleNV"; + case BuiltInHitKindBackFacingMicroTriangleNV: return "HitKindBackFacingMicroTriangleNV"; + case BuiltInHitSphereRadiusNV: return "HitSphereRadiusNV"; + case BuiltInHitLSSRadiiNV: return "HitLSSRadiiNV"; + case BuiltInClusterIDNV: return "ClusterIDNV"; + case BuiltInCullMaskKHR: return "CullMaskKHR"; + default: return "Unknown"; + } +} + +inline const char* ScopeToString(Scope value) { + switch (value) { + case ScopeCrossDevice: return "CrossDevice"; + case ScopeDevice: return "Device"; + case ScopeWorkgroup: return "Workgroup"; + case ScopeSubgroup: return "Subgroup"; + case ScopeInvocation: return "Invocation"; + case ScopeQueueFamily: return "QueueFamily"; + case ScopeShaderCallKHR: return "ShaderCallKHR"; + default: return "Unknown"; + } +} + +inline const char* GroupOperationToString(GroupOperation value) { + switch (value) { + case GroupOperationReduce: return "Reduce"; + case GroupOperationInclusiveScan: return "InclusiveScan"; + case GroupOperationExclusiveScan: return "ExclusiveScan"; + case GroupOperationClusteredReduce: return "ClusteredReduce"; + case GroupOperationPartitionedReduceEXT: return "PartitionedReduceEXT"; + case GroupOperationPartitionedInclusiveScanEXT: return "PartitionedInclusiveScanEXT"; + case GroupOperationPartitionedExclusiveScanEXT: return "PartitionedExclusiveScanEXT"; + default: return "Unknown"; + } +} + +inline const char* KernelEnqueueFlagsToString(KernelEnqueueFlags value) { + switch (value) { + case KernelEnqueueFlagsNoWait: return "NoWait"; + case KernelEnqueueFlagsWaitKernel: return "WaitKernel"; + case KernelEnqueueFlagsWaitWorkGroup: return "WaitWorkGroup"; + default: return "Unknown"; + } +} + +inline const char* CapabilityToString(Capability value) { + switch (value) { + case CapabilityMatrix: return "Matrix"; + case CapabilityShader: return "Shader"; + case CapabilityGeometry: return "Geometry"; + case CapabilityTessellation: return "Tessellation"; + case CapabilityAddresses: return "Addresses"; + case CapabilityLinkage: return "Linkage"; + case CapabilityKernel: return "Kernel"; + case CapabilityVector16: return "Vector16"; + case CapabilityFloat16Buffer: return "Float16Buffer"; + case CapabilityFloat16: return "Float16"; + case CapabilityFloat64: return "Float64"; + case CapabilityInt64: return "Int64"; + case CapabilityInt64Atomics: return "Int64Atomics"; + case CapabilityImageBasic: return "ImageBasic"; + case CapabilityImageReadWrite: return "ImageReadWrite"; + case CapabilityImageMipmap: return "ImageMipmap"; + case CapabilityPipes: return "Pipes"; + case CapabilityGroups: return "Groups"; + case CapabilityDeviceEnqueue: return "DeviceEnqueue"; + case CapabilityLiteralSampler: return "LiteralSampler"; + case CapabilityAtomicStorage: return "AtomicStorage"; + case CapabilityInt16: return "Int16"; + case CapabilityTessellationPointSize: return "TessellationPointSize"; + case CapabilityGeometryPointSize: return "GeometryPointSize"; + case CapabilityImageGatherExtended: return "ImageGatherExtended"; + case CapabilityStorageImageMultisample: return "StorageImageMultisample"; + case CapabilityUniformBufferArrayDynamicIndexing: return "UniformBufferArrayDynamicIndexing"; + case CapabilitySampledImageArrayDynamicIndexing: return "SampledImageArrayDynamicIndexing"; + case CapabilityStorageBufferArrayDynamicIndexing: return "StorageBufferArrayDynamicIndexing"; + case CapabilityStorageImageArrayDynamicIndexing: return "StorageImageArrayDynamicIndexing"; + case CapabilityClipDistance: return "ClipDistance"; + case CapabilityCullDistance: return "CullDistance"; + case CapabilityImageCubeArray: return "ImageCubeArray"; + case CapabilitySampleRateShading: return "SampleRateShading"; + case CapabilityImageRect: return "ImageRect"; + case CapabilitySampledRect: return "SampledRect"; + case CapabilityGenericPointer: return "GenericPointer"; + case CapabilityInt8: return "Int8"; + case CapabilityInputAttachment: return "InputAttachment"; + case CapabilitySparseResidency: return "SparseResidency"; + case CapabilityMinLod: return "MinLod"; + case CapabilitySampled1D: return "Sampled1D"; + case CapabilityImage1D: return "Image1D"; + case CapabilitySampledCubeArray: return "SampledCubeArray"; + case CapabilitySampledBuffer: return "SampledBuffer"; + case CapabilityImageBuffer: return "ImageBuffer"; + case CapabilityImageMSArray: return "ImageMSArray"; + case CapabilityStorageImageExtendedFormats: return "StorageImageExtendedFormats"; + case CapabilityImageQuery: return "ImageQuery"; + case CapabilityDerivativeControl: return "DerivativeControl"; + case CapabilityInterpolationFunction: return "InterpolationFunction"; + case CapabilityTransformFeedback: return "TransformFeedback"; + case CapabilityGeometryStreams: return "GeometryStreams"; + case CapabilityStorageImageReadWithoutFormat: return "StorageImageReadWithoutFormat"; + case CapabilityStorageImageWriteWithoutFormat: return "StorageImageWriteWithoutFormat"; + case CapabilityMultiViewport: return "MultiViewport"; + case CapabilitySubgroupDispatch: return "SubgroupDispatch"; + case CapabilityNamedBarrier: return "NamedBarrier"; + case CapabilityPipeStorage: return "PipeStorage"; + case CapabilityGroupNonUniform: return "GroupNonUniform"; + case CapabilityGroupNonUniformVote: return "GroupNonUniformVote"; + case CapabilityGroupNonUniformArithmetic: return "GroupNonUniformArithmetic"; + case CapabilityGroupNonUniformBallot: return "GroupNonUniformBallot"; + case CapabilityGroupNonUniformShuffle: return "GroupNonUniformShuffle"; + case CapabilityGroupNonUniformShuffleRelative: return "GroupNonUniformShuffleRelative"; + case CapabilityGroupNonUniformClustered: return "GroupNonUniformClustered"; + case CapabilityGroupNonUniformQuad: return "GroupNonUniformQuad"; + case CapabilityShaderLayer: return "ShaderLayer"; + case CapabilityShaderViewportIndex: return "ShaderViewportIndex"; + case CapabilityUniformDecoration: return "UniformDecoration"; + case CapabilityCoreBuiltinsARM: return "CoreBuiltinsARM"; + case CapabilityTileImageColorReadAccessEXT: return "TileImageColorReadAccessEXT"; + case CapabilityTileImageDepthReadAccessEXT: return "TileImageDepthReadAccessEXT"; + case CapabilityTileImageStencilReadAccessEXT: return "TileImageStencilReadAccessEXT"; + case CapabilityTensorsARM: return "TensorsARM"; + case CapabilityStorageTensorArrayDynamicIndexingARM: return "StorageTensorArrayDynamicIndexingARM"; + case CapabilityStorageTensorArrayNonUniformIndexingARM: return "StorageTensorArrayNonUniformIndexingARM"; + case CapabilityGraphARM: return "GraphARM"; + case CapabilityCooperativeMatrixLayoutsARM: return "CooperativeMatrixLayoutsARM"; + case CapabilityFloat8EXT: return "Float8EXT"; + case CapabilityFloat8CooperativeMatrixEXT: return "Float8CooperativeMatrixEXT"; + case CapabilityFragmentShadingRateKHR: return "FragmentShadingRateKHR"; + case CapabilitySubgroupBallotKHR: return "SubgroupBallotKHR"; + case CapabilityDrawParameters: return "DrawParameters"; + case CapabilityWorkgroupMemoryExplicitLayoutKHR: return "WorkgroupMemoryExplicitLayoutKHR"; + case CapabilityWorkgroupMemoryExplicitLayout8BitAccessKHR: return "WorkgroupMemoryExplicitLayout8BitAccessKHR"; + case CapabilityWorkgroupMemoryExplicitLayout16BitAccessKHR: return "WorkgroupMemoryExplicitLayout16BitAccessKHR"; + case CapabilitySubgroupVoteKHR: return "SubgroupVoteKHR"; + case CapabilityStorageBuffer16BitAccess: return "StorageBuffer16BitAccess"; + case CapabilityStorageUniform16: return "StorageUniform16"; + case CapabilityStoragePushConstant16: return "StoragePushConstant16"; + case CapabilityStorageInputOutput16: return "StorageInputOutput16"; + case CapabilityDeviceGroup: return "DeviceGroup"; + case CapabilityMultiView: return "MultiView"; + case CapabilityVariablePointersStorageBuffer: return "VariablePointersStorageBuffer"; + case CapabilityVariablePointers: return "VariablePointers"; + case CapabilityAtomicStorageOps: return "AtomicStorageOps"; + case CapabilitySampleMaskPostDepthCoverage: return "SampleMaskPostDepthCoverage"; + case CapabilityStorageBuffer8BitAccess: return "StorageBuffer8BitAccess"; + case CapabilityUniformAndStorageBuffer8BitAccess: return "UniformAndStorageBuffer8BitAccess"; + case CapabilityStoragePushConstant8: return "StoragePushConstant8"; + case CapabilityDenormPreserve: return "DenormPreserve"; + case CapabilityDenormFlushToZero: return "DenormFlushToZero"; + case CapabilitySignedZeroInfNanPreserve: return "SignedZeroInfNanPreserve"; + case CapabilityRoundingModeRTE: return "RoundingModeRTE"; + case CapabilityRoundingModeRTZ: return "RoundingModeRTZ"; + case CapabilityRayQueryProvisionalKHR: return "RayQueryProvisionalKHR"; + case CapabilityRayQueryKHR: return "RayQueryKHR"; + case CapabilityUntypedPointersKHR: return "UntypedPointersKHR"; + case CapabilityRayTraversalPrimitiveCullingKHR: return "RayTraversalPrimitiveCullingKHR"; + case CapabilityRayTracingKHR: return "RayTracingKHR"; + case CapabilityTextureSampleWeightedQCOM: return "TextureSampleWeightedQCOM"; + case CapabilityTextureBoxFilterQCOM: return "TextureBoxFilterQCOM"; + case CapabilityTextureBlockMatchQCOM: return "TextureBlockMatchQCOM"; + case CapabilityTileShadingQCOM: return "TileShadingQCOM"; + case CapabilityCooperativeMatrixConversionQCOM: return "CooperativeMatrixConversionQCOM"; + case CapabilityTextureBlockMatch2QCOM: return "TextureBlockMatch2QCOM"; + case CapabilityFloat16ImageAMD: return "Float16ImageAMD"; + case CapabilityImageGatherBiasLodAMD: return "ImageGatherBiasLodAMD"; + case CapabilityFragmentMaskAMD: return "FragmentMaskAMD"; + case CapabilityStencilExportEXT: return "StencilExportEXT"; + case CapabilityImageReadWriteLodAMD: return "ImageReadWriteLodAMD"; + case CapabilityInt64ImageEXT: return "Int64ImageEXT"; + case CapabilityShaderClockKHR: return "ShaderClockKHR"; + case CapabilityShaderEnqueueAMDX: return "ShaderEnqueueAMDX"; + case CapabilityQuadControlKHR: return "QuadControlKHR"; + case CapabilityInt4TypeINTEL: return "Int4TypeINTEL"; + case CapabilityInt4CooperativeMatrixINTEL: return "Int4CooperativeMatrixINTEL"; + case CapabilityBFloat16TypeKHR: return "BFloat16TypeKHR"; + case CapabilityBFloat16DotProductKHR: return "BFloat16DotProductKHR"; + case CapabilityBFloat16CooperativeMatrixKHR: return "BFloat16CooperativeMatrixKHR"; + case CapabilityAbortKHR: return "AbortKHR"; + case CapabilityDescriptorHeapEXT: return "DescriptorHeapEXT"; + case CapabilityConstantDataKHR: return "ConstantDataKHR"; + case CapabilityPoisonFreezeKHR: return "PoisonFreezeKHR"; + case CapabilitySampleMaskOverrideCoverageNV: return "SampleMaskOverrideCoverageNV"; + case CapabilityGeometryShaderPassthroughNV: return "GeometryShaderPassthroughNV"; + case CapabilityShaderViewportIndexLayerEXT: return "ShaderViewportIndexLayerEXT"; + case CapabilityShaderViewportMaskNV: return "ShaderViewportMaskNV"; + case CapabilityShaderStereoViewNV: return "ShaderStereoViewNV"; + case CapabilityPerViewAttributesNV: return "PerViewAttributesNV"; + case CapabilityFragmentFullyCoveredEXT: return "FragmentFullyCoveredEXT"; + case CapabilityMeshShadingNV: return "MeshShadingNV"; + case CapabilityImageFootprintNV: return "ImageFootprintNV"; + case CapabilityMeshShadingEXT: return "MeshShadingEXT"; + case CapabilityFragmentBarycentricKHR: return "FragmentBarycentricKHR"; + case CapabilityComputeDerivativeGroupQuadsKHR: return "ComputeDerivativeGroupQuadsKHR"; + case CapabilityFragmentDensityEXT: return "FragmentDensityEXT"; + case CapabilityGroupNonUniformPartitionedEXT: return "GroupNonUniformPartitionedEXT"; + case CapabilityShaderNonUniform: return "ShaderNonUniform"; + case CapabilityRuntimeDescriptorArray: return "RuntimeDescriptorArray"; + case CapabilityInputAttachmentArrayDynamicIndexing: return "InputAttachmentArrayDynamicIndexing"; + case CapabilityUniformTexelBufferArrayDynamicIndexing: return "UniformTexelBufferArrayDynamicIndexing"; + case CapabilityStorageTexelBufferArrayDynamicIndexing: return "StorageTexelBufferArrayDynamicIndexing"; + case CapabilityUniformBufferArrayNonUniformIndexing: return "UniformBufferArrayNonUniformIndexing"; + case CapabilitySampledImageArrayNonUniformIndexing: return "SampledImageArrayNonUniformIndexing"; + case CapabilityStorageBufferArrayNonUniformIndexing: return "StorageBufferArrayNonUniformIndexing"; + case CapabilityStorageImageArrayNonUniformIndexing: return "StorageImageArrayNonUniformIndexing"; + case CapabilityInputAttachmentArrayNonUniformIndexing: return "InputAttachmentArrayNonUniformIndexing"; + case CapabilityUniformTexelBufferArrayNonUniformIndexing: return "UniformTexelBufferArrayNonUniformIndexing"; + case CapabilityStorageTexelBufferArrayNonUniformIndexing: return "StorageTexelBufferArrayNonUniformIndexing"; + case CapabilityRayTracingPositionFetchKHR: return "RayTracingPositionFetchKHR"; + case CapabilityRayTracingNV: return "RayTracingNV"; + case CapabilityRayTracingMotionBlurNV: return "RayTracingMotionBlurNV"; + case CapabilityVulkanMemoryModel: return "VulkanMemoryModel"; + case CapabilityVulkanMemoryModelDeviceScope: return "VulkanMemoryModelDeviceScope"; + case CapabilityPhysicalStorageBufferAddresses: return "PhysicalStorageBufferAddresses"; + case CapabilityComputeDerivativeGroupLinearKHR: return "ComputeDerivativeGroupLinearKHR"; + case CapabilityRayTracingProvisionalKHR: return "RayTracingProvisionalKHR"; + case CapabilityCooperativeMatrixNV: return "CooperativeMatrixNV"; + case CapabilityFragmentShaderSampleInterlockEXT: return "FragmentShaderSampleInterlockEXT"; + case CapabilityFragmentShaderShadingRateInterlockEXT: return "FragmentShaderShadingRateInterlockEXT"; + case CapabilityShaderSMBuiltinsNV: return "ShaderSMBuiltinsNV"; + case CapabilityFragmentShaderPixelInterlockEXT: return "FragmentShaderPixelInterlockEXT"; + case CapabilityDemoteToHelperInvocation: return "DemoteToHelperInvocation"; + case CapabilityDisplacementMicromapNV: return "DisplacementMicromapNV"; + case CapabilityRayTracingOpacityMicromapEXT: return "RayTracingOpacityMicromapEXT"; + case CapabilityShaderInvocationReorderNV: return "ShaderInvocationReorderNV"; + case CapabilityShaderInvocationReorderEXT: return "ShaderInvocationReorderEXT"; + case CapabilityBindlessTextureNV: return "BindlessTextureNV"; + case CapabilityRayQueryPositionFetchKHR: return "RayQueryPositionFetchKHR"; + case CapabilityCooperativeVectorNV: return "CooperativeVectorNV"; + case CapabilityAtomicFloat16VectorNV: return "AtomicFloat16VectorNV"; + case CapabilityRayTracingDisplacementMicromapNV: return "RayTracingDisplacementMicromapNV"; + case CapabilityRawAccessChainsNV: return "RawAccessChainsNV"; + case CapabilityRayTracingSpheresGeometryNV: return "RayTracingSpheresGeometryNV"; + case CapabilityRayTracingLinearSweptSpheresGeometryNV: return "RayTracingLinearSweptSpheresGeometryNV"; + case CapabilityPushConstantBanksNV: return "PushConstantBanksNV"; + case CapabilityLongVectorEXT: return "LongVectorEXT"; + case CapabilityShader64BitIndexingEXT: return "Shader64BitIndexingEXT"; + case CapabilityCooperativeMatrixReductionsNV: return "CooperativeMatrixReductionsNV"; + case CapabilityCooperativeMatrixConversionsNV: return "CooperativeMatrixConversionsNV"; + case CapabilityCooperativeMatrixPerElementOperationsNV: return "CooperativeMatrixPerElementOperationsNV"; + case CapabilityCooperativeMatrixTensorAddressingNV: return "CooperativeMatrixTensorAddressingNV"; + case CapabilityCooperativeMatrixBlockLoadsNV: return "CooperativeMatrixBlockLoadsNV"; + case CapabilityCooperativeVectorTrainingNV: return "CooperativeVectorTrainingNV"; + case CapabilityRayTracingClusterAccelerationStructureNV: return "RayTracingClusterAccelerationStructureNV"; + case CapabilityTensorAddressingNV: return "TensorAddressingNV"; + case CapabilitySubgroupShuffleINTEL: return "SubgroupShuffleINTEL"; + case CapabilitySubgroupBufferBlockIOINTEL: return "SubgroupBufferBlockIOINTEL"; + case CapabilitySubgroupImageBlockIOINTEL: return "SubgroupImageBlockIOINTEL"; + case CapabilitySubgroupImageMediaBlockIOINTEL: return "SubgroupImageMediaBlockIOINTEL"; + case CapabilityRoundToInfinityINTEL: return "RoundToInfinityINTEL"; + case CapabilityFloatingPointModeINTEL: return "FloatingPointModeINTEL"; + case CapabilityIntegerFunctions2INTEL: return "IntegerFunctions2INTEL"; + case CapabilityFunctionPointersINTEL: return "FunctionPointersINTEL"; + case CapabilityIndirectReferencesINTEL: return "IndirectReferencesINTEL"; + case CapabilityAsmINTEL: return "AsmINTEL"; + case CapabilityAtomicFloat32MinMaxEXT: return "AtomicFloat32MinMaxEXT"; + case CapabilityAtomicFloat64MinMaxEXT: return "AtomicFloat64MinMaxEXT"; + case CapabilityAtomicFloat16MinMaxEXT: return "AtomicFloat16MinMaxEXT"; + case CapabilityVectorComputeINTEL: return "VectorComputeINTEL"; + case CapabilityVectorAnyINTEL: return "VectorAnyINTEL"; + case CapabilityExpectAssumeKHR: return "ExpectAssumeKHR"; + case CapabilitySubgroupAvcMotionEstimationINTEL: return "SubgroupAvcMotionEstimationINTEL"; + case CapabilitySubgroupAvcMotionEstimationIntraINTEL: return "SubgroupAvcMotionEstimationIntraINTEL"; + case CapabilitySubgroupAvcMotionEstimationChromaINTEL: return "SubgroupAvcMotionEstimationChromaINTEL"; + case CapabilityVariableLengthArrayINTEL: return "VariableLengthArrayINTEL"; + case CapabilityFunctionFloatControlINTEL: return "FunctionFloatControlINTEL"; + case CapabilityFPGAMemoryAttributesALTERA: return "FPGAMemoryAttributesALTERA"; + case CapabilityFPFastMathModeINTEL: return "FPFastMathModeINTEL"; + case CapabilityArbitraryPrecisionIntegersALTERA: return "ArbitraryPrecisionIntegersALTERA"; + case CapabilityArbitraryPrecisionFloatingPointALTERA: return "ArbitraryPrecisionFloatingPointALTERA"; + case CapabilityUnstructuredLoopControlsINTEL: return "UnstructuredLoopControlsINTEL"; + case CapabilityFPGALoopControlsALTERA: return "FPGALoopControlsALTERA"; + case CapabilityKernelAttributesINTEL: return "KernelAttributesINTEL"; + case CapabilityFPGAKernelAttributesINTEL: return "FPGAKernelAttributesINTEL"; + case CapabilityFPGAMemoryAccessesALTERA: return "FPGAMemoryAccessesALTERA"; + case CapabilityFPGAClusterAttributesALTERA: return "FPGAClusterAttributesALTERA"; + case CapabilityLoopFuseALTERA: return "LoopFuseALTERA"; + case CapabilityFPGADSPControlALTERA: return "FPGADSPControlALTERA"; + case CapabilityMemoryAccessAliasingINTEL: return "MemoryAccessAliasingINTEL"; + case CapabilityFPGAInvocationPipeliningAttributesALTERA: return "FPGAInvocationPipeliningAttributesALTERA"; + case CapabilityFPGABufferLocationALTERA: return "FPGABufferLocationALTERA"; + case CapabilityArbitraryPrecisionFixedPointALTERA: return "ArbitraryPrecisionFixedPointALTERA"; + case CapabilityUSMStorageClassesALTERA: return "USMStorageClassesALTERA"; + case CapabilityRuntimeAlignedAttributeALTERA: return "RuntimeAlignedAttributeALTERA"; + case CapabilityIOPipesALTERA: return "IOPipesALTERA"; + case CapabilityBlockingPipesALTERA: return "BlockingPipesALTERA"; + case CapabilityFPGARegALTERA: return "FPGARegALTERA"; + case CapabilityDotProductInputAll: return "DotProductInputAll"; + case CapabilityDotProductInput4x8Bit: return "DotProductInput4x8Bit"; + case CapabilityDotProductInput4x8BitPacked: return "DotProductInput4x8BitPacked"; + case CapabilityDotProduct: return "DotProduct"; + case CapabilityRayCullMaskKHR: return "RayCullMaskKHR"; + case CapabilityCooperativeMatrixKHR: return "CooperativeMatrixKHR"; + case CapabilityReplicatedCompositesEXT: return "ReplicatedCompositesEXT"; + case CapabilityBitInstructions: return "BitInstructions"; + case CapabilityGroupNonUniformRotateKHR: return "GroupNonUniformRotateKHR"; + case CapabilityFloatControls2: return "FloatControls2"; + case CapabilityFMAKHR: return "FMAKHR"; + case CapabilityAtomicFloat32AddEXT: return "AtomicFloat32AddEXT"; + case CapabilityAtomicFloat64AddEXT: return "AtomicFloat64AddEXT"; + case CapabilityLongCompositesINTEL: return "LongCompositesINTEL"; + case CapabilityOptNoneEXT: return "OptNoneEXT"; + case CapabilityAtomicFloat16AddEXT: return "AtomicFloat16AddEXT"; + case CapabilityDebugInfoModuleINTEL: return "DebugInfoModuleINTEL"; + case CapabilityBFloat16ConversionINTEL: return "BFloat16ConversionINTEL"; + case CapabilitySplitBarrierINTEL: return "SplitBarrierINTEL"; + case CapabilityArithmeticFenceEXT: return "ArithmeticFenceEXT"; + case CapabilityFPGAClusterAttributesV2ALTERA: return "FPGAClusterAttributesV2ALTERA"; + case CapabilityFPGAKernelAttributesv2INTEL: return "FPGAKernelAttributesv2INTEL"; + case CapabilityTaskSequenceALTERA: return "TaskSequenceALTERA"; + case CapabilityFPMaxErrorINTEL: return "FPMaxErrorINTEL"; + case CapabilityFPGALatencyControlALTERA: return "FPGALatencyControlALTERA"; + case CapabilityFPGAArgumentInterfacesALTERA: return "FPGAArgumentInterfacesALTERA"; + case CapabilityGlobalVariableHostAccessINTEL: return "GlobalVariableHostAccessINTEL"; + case CapabilityGlobalVariableFPGADecorationsALTERA: return "GlobalVariableFPGADecorationsALTERA"; + case CapabilitySubgroupBufferPrefetchINTEL: return "SubgroupBufferPrefetchINTEL"; + case CapabilitySubgroup2DBlockIOINTEL: return "Subgroup2DBlockIOINTEL"; + case CapabilitySubgroup2DBlockTransformINTEL: return "Subgroup2DBlockTransformINTEL"; + case CapabilitySubgroup2DBlockTransposeINTEL: return "Subgroup2DBlockTransposeINTEL"; + case CapabilitySubgroupMatrixMultiplyAccumulateINTEL: return "SubgroupMatrixMultiplyAccumulateINTEL"; + case CapabilityTernaryBitwiseFunctionINTEL: return "TernaryBitwiseFunctionINTEL"; + case CapabilityUntypedVariableLengthArrayINTEL: return "UntypedVariableLengthArrayINTEL"; + case CapabilitySpecConditionalINTEL: return "SpecConditionalINTEL"; + case CapabilityFunctionVariantsINTEL: return "FunctionVariantsINTEL"; + case CapabilityGroupUniformArithmeticKHR: return "GroupUniformArithmeticKHR"; + case CapabilityTensorFloat32RoundingINTEL: return "TensorFloat32RoundingINTEL"; + case CapabilityMaskedGatherScatterINTEL: return "MaskedGatherScatterINTEL"; + case CapabilityCacheControlsINTEL: return "CacheControlsINTEL"; + case CapabilityRegisterLimitsINTEL: return "RegisterLimitsINTEL"; + case CapabilityBindlessImagesINTEL: return "BindlessImagesINTEL"; + case CapabilityDotProductFloat16AccFloat32VALVE: return "DotProductFloat16AccFloat32VALVE"; + case CapabilityDotProductFloat16AccFloat16VALVE: return "DotProductFloat16AccFloat16VALVE"; + case CapabilityDotProductBFloat16AccVALVE: return "DotProductBFloat16AccVALVE"; + case CapabilityDotProductFloat8AccFloat32VALVE: return "DotProductFloat8AccFloat32VALVE"; + default: return "Unknown"; + } +} + +inline const char* RayQueryIntersectionToString(RayQueryIntersection value) { + switch (value) { + case RayQueryIntersectionRayQueryCandidateIntersectionKHR: return "RayQueryCandidateIntersectionKHR"; + case RayQueryIntersectionRayQueryCommittedIntersectionKHR: return "RayQueryCommittedIntersectionKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCommittedIntersectionTypeToString(RayQueryCommittedIntersectionType value) { + switch (value) { + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionNoneKHR: return "RayQueryCommittedIntersectionNoneKHR"; + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionTriangleKHR: return "RayQueryCommittedIntersectionTriangleKHR"; + case RayQueryCommittedIntersectionTypeRayQueryCommittedIntersectionGeneratedKHR: return "RayQueryCommittedIntersectionGeneratedKHR"; + default: return "Unknown"; + } +} + +inline const char* RayQueryCandidateIntersectionTypeToString(RayQueryCandidateIntersectionType value) { + switch (value) { + case RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionTriangleKHR: return "RayQueryCandidateIntersectionTriangleKHR"; + case RayQueryCandidateIntersectionTypeRayQueryCandidateIntersectionAABBKHR: return "RayQueryCandidateIntersectionAABBKHR"; + default: return "Unknown"; + } +} + +inline const char* FPDenormModeToString(FPDenormMode value) { + switch (value) { + case FPDenormModePreserve: return "Preserve"; + case FPDenormModeFlushToZero: return "FlushToZero"; + default: return "Unknown"; + } +} + +inline const char* FPOperationModeToString(FPOperationMode value) { + switch (value) { + case FPOperationModeIEEE: return "IEEE"; + case FPOperationModeALT: return "ALT"; + default: return "Unknown"; + } +} + +inline const char* QuantizationModesToString(QuantizationModes value) { + switch (value) { + case QuantizationModesTRN: return "TRN"; + case QuantizationModesTRN_ZERO: return "TRN_ZERO"; + case QuantizationModesRND: return "RND"; + case QuantizationModesRND_ZERO: return "RND_ZERO"; + case QuantizationModesRND_INF: return "RND_INF"; + case QuantizationModesRND_MIN_INF: return "RND_MIN_INF"; + case QuantizationModesRND_CONV: return "RND_CONV"; + case QuantizationModesRND_CONV_ODD: return "RND_CONV_ODD"; + default: return "Unknown"; + } +} + +inline const char* OverflowModesToString(OverflowModes value) { + switch (value) { + case OverflowModesWRAP: return "WRAP"; + case OverflowModesSAT: return "SAT"; + case OverflowModesSAT_ZERO: return "SAT_ZERO"; + case OverflowModesSAT_SYM: return "SAT_SYM"; + default: return "Unknown"; + } +} + +inline const char* PackedVectorFormatToString(PackedVectorFormat value) { + switch (value) { + case PackedVectorFormatPackedVectorFormat4x8Bit: return "PackedVectorFormat4x8Bit"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixLayoutToString(CooperativeMatrixLayout value) { + switch (value) { + case CooperativeMatrixLayoutRowMajorKHR: return "RowMajorKHR"; + case CooperativeMatrixLayoutColumnMajorKHR: return "ColumnMajorKHR"; + case CooperativeMatrixLayoutRowBlockedInterleavedARM: return "RowBlockedInterleavedARM"; + case CooperativeMatrixLayoutColumnBlockedInterleavedARM: return "ColumnBlockedInterleavedARM"; + default: return "Unknown"; + } +} + +inline const char* CooperativeMatrixUseToString(CooperativeMatrixUse value) { + switch (value) { + case CooperativeMatrixUseMatrixAKHR: return "MatrixAKHR"; + case CooperativeMatrixUseMatrixBKHR: return "MatrixBKHR"; + case CooperativeMatrixUseMatrixAccumulatorKHR: return "MatrixAccumulatorKHR"; + default: return "Unknown"; + } +} + +inline const char* TensorClampModeToString(TensorClampMode value) { + switch (value) { + case TensorClampModeUndefined: return "Undefined"; + case TensorClampModeConstant: return "Constant"; + case TensorClampModeClampToEdge: return "ClampToEdge"; + case TensorClampModeRepeat: return "Repeat"; + case TensorClampModeRepeatMirrored: return "RepeatMirrored"; + default: return "Unknown"; + } +} + +inline const char* InitializationModeQualifierToString(InitializationModeQualifier value) { + switch (value) { + case InitializationModeQualifierInitOnDeviceReprogramALTERA: return "InitOnDeviceReprogramALTERA"; + case InitializationModeQualifierInitOnDeviceResetALTERA: return "InitOnDeviceResetALTERA"; + default: return "Unknown"; + } +} + +inline const char* HostAccessQualifierToString(HostAccessQualifier value) { + switch (value) { + case HostAccessQualifierNoneINTEL: return "NoneINTEL"; + case HostAccessQualifierReadINTEL: return "ReadINTEL"; + case HostAccessQualifierWriteINTEL: return "WriteINTEL"; + case HostAccessQualifierReadWriteINTEL: return "ReadWriteINTEL"; + default: return "Unknown"; + } +} + +inline const char* LoadCacheControlToString(LoadCacheControl value) { + switch (value) { + case LoadCacheControlUncachedINTEL: return "UncachedINTEL"; + case LoadCacheControlCachedINTEL: return "CachedINTEL"; + case LoadCacheControlStreamingINTEL: return "StreamingINTEL"; + case LoadCacheControlInvalidateAfterReadINTEL: return "InvalidateAfterReadINTEL"; + case LoadCacheControlConstCachedINTEL: return "ConstCachedINTEL"; + default: return "Unknown"; + } +} + +inline const char* StoreCacheControlToString(StoreCacheControl value) { + switch (value) { + case StoreCacheControlUncachedINTEL: return "UncachedINTEL"; + case StoreCacheControlWriteThroughINTEL: return "WriteThroughINTEL"; + case StoreCacheControlWriteBackINTEL: return "WriteBackINTEL"; + case StoreCacheControlStreamingINTEL: return "StreamingINTEL"; + default: return "Unknown"; + } +} + +inline const char* NamedMaximumNumberOfRegistersToString(NamedMaximumNumberOfRegisters value) { + switch (value) { + case NamedMaximumNumberOfRegistersAutoINTEL: return "AutoINTEL"; + default: return "Unknown"; + } +} + +inline const char* FPEncodingToString(FPEncoding value) { + switch (value) { + case FPEncodingBFloat16KHR: return "BFloat16KHR"; + case FPEncodingFloat8E4M3EXT: return "Float8E4M3EXT"; + case FPEncodingFloat8E5M2EXT: return "Float8E5M2EXT"; + default: return "Unknown"; + } +} + +inline const char* CooperativeVectorMatrixLayoutToString(CooperativeVectorMatrixLayout value) { + switch (value) { + case CooperativeVectorMatrixLayoutRowMajorNV: return "RowMajorNV"; + case CooperativeVectorMatrixLayoutColumnMajorNV: return "ColumnMajorNV"; + case CooperativeVectorMatrixLayoutInferencingOptimalNV: return "InferencingOptimalNV"; + case CooperativeVectorMatrixLayoutTrainingOptimalNV: return "TrainingOptimalNV"; + default: return "Unknown"; + } +} + +inline const char* ComponentTypeToString(ComponentType value) { + switch (value) { + case ComponentTypeFloat16NV: return "Float16NV"; + case ComponentTypeFloat32NV: return "Float32NV"; + case ComponentTypeFloat64NV: return "Float64NV"; + case ComponentTypeSignedInt8NV: return "SignedInt8NV"; + case ComponentTypeSignedInt16NV: return "SignedInt16NV"; + case ComponentTypeSignedInt32NV: return "SignedInt32NV"; + case ComponentTypeSignedInt64NV: return "SignedInt64NV"; + case ComponentTypeUnsignedInt8NV: return "UnsignedInt8NV"; + case ComponentTypeUnsignedInt16NV: return "UnsignedInt16NV"; + case ComponentTypeUnsignedInt32NV: return "UnsignedInt32NV"; + case ComponentTypeUnsignedInt64NV: return "UnsignedInt64NV"; + case ComponentTypeSignedInt8PackedNV: return "SignedInt8PackedNV"; + case ComponentTypeUnsignedInt8PackedNV: return "UnsignedInt8PackedNV"; + case ComponentTypeFloatE4M3NV: return "FloatE4M3NV"; + case ComponentTypeFloatE5M2NV: return "FloatE5M2NV"; + default: return "Unknown"; + } +} + +inline const char* OpToString(Op value) { + switch (value) { + case OpNop: return "OpNop"; + case OpUndef: return "OpUndef"; + case OpSourceContinued: return "OpSourceContinued"; + case OpSource: return "OpSource"; + case OpSourceExtension: return "OpSourceExtension"; + case OpName: return "OpName"; + case OpMemberName: return "OpMemberName"; + case OpString: return "OpString"; + case OpLine: return "OpLine"; + case OpExtension: return "OpExtension"; + case OpExtInstImport: return "OpExtInstImport"; + case OpExtInst: return "OpExtInst"; + case OpMemoryModel: return "OpMemoryModel"; + case OpEntryPoint: return "OpEntryPoint"; + case OpExecutionMode: return "OpExecutionMode"; + case OpCapability: return "OpCapability"; + case OpTypeVoid: return "OpTypeVoid"; + case OpTypeBool: return "OpTypeBool"; + case OpTypeInt: return "OpTypeInt"; + case OpTypeFloat: return "OpTypeFloat"; + case OpTypeVector: return "OpTypeVector"; + case OpTypeMatrix: return "OpTypeMatrix"; + case OpTypeImage: return "OpTypeImage"; + case OpTypeSampler: return "OpTypeSampler"; + case OpTypeSampledImage: return "OpTypeSampledImage"; + case OpTypeArray: return "OpTypeArray"; + case OpTypeRuntimeArray: return "OpTypeRuntimeArray"; + case OpTypeStruct: return "OpTypeStruct"; + case OpTypeOpaque: return "OpTypeOpaque"; + case OpTypePointer: return "OpTypePointer"; + case OpTypeFunction: return "OpTypeFunction"; + case OpTypeEvent: return "OpTypeEvent"; + case OpTypeDeviceEvent: return "OpTypeDeviceEvent"; + case OpTypeReserveId: return "OpTypeReserveId"; + case OpTypeQueue: return "OpTypeQueue"; + case OpTypePipe: return "OpTypePipe"; + case OpTypeForwardPointer: return "OpTypeForwardPointer"; + case OpConstantTrue: return "OpConstantTrue"; + case OpConstantFalse: return "OpConstantFalse"; + case OpConstant: return "OpConstant"; + case OpConstantComposite: return "OpConstantComposite"; + case OpConstantSampler: return "OpConstantSampler"; + case OpConstantNull: return "OpConstantNull"; + case OpSpecConstantTrue: return "OpSpecConstantTrue"; + case OpSpecConstantFalse: return "OpSpecConstantFalse"; + case OpSpecConstant: return "OpSpecConstant"; + case OpSpecConstantComposite: return "OpSpecConstantComposite"; + case OpSpecConstantOp: return "OpSpecConstantOp"; + case OpFunction: return "OpFunction"; + case OpFunctionParameter: return "OpFunctionParameter"; + case OpFunctionEnd: return "OpFunctionEnd"; + case OpFunctionCall: return "OpFunctionCall"; + case OpVariable: return "OpVariable"; + case OpImageTexelPointer: return "OpImageTexelPointer"; + case OpLoad: return "OpLoad"; + case OpStore: return "OpStore"; + case OpCopyMemory: return "OpCopyMemory"; + case OpCopyMemorySized: return "OpCopyMemorySized"; + case OpAccessChain: return "OpAccessChain"; + case OpInBoundsAccessChain: return "OpInBoundsAccessChain"; + case OpPtrAccessChain: return "OpPtrAccessChain"; + case OpArrayLength: return "OpArrayLength"; + case OpGenericPtrMemSemantics: return "OpGenericPtrMemSemantics"; + case OpInBoundsPtrAccessChain: return "OpInBoundsPtrAccessChain"; + case OpDecorate: return "OpDecorate"; + case OpMemberDecorate: return "OpMemberDecorate"; + case OpDecorationGroup: return "OpDecorationGroup"; + case OpGroupDecorate: return "OpGroupDecorate"; + case OpGroupMemberDecorate: return "OpGroupMemberDecorate"; + case OpVectorExtractDynamic: return "OpVectorExtractDynamic"; + case OpVectorInsertDynamic: return "OpVectorInsertDynamic"; + case OpVectorShuffle: return "OpVectorShuffle"; + case OpCompositeConstruct: return "OpCompositeConstruct"; + case OpCompositeExtract: return "OpCompositeExtract"; + case OpCompositeInsert: return "OpCompositeInsert"; + case OpCopyObject: return "OpCopyObject"; + case OpTranspose: return "OpTranspose"; + case OpSampledImage: return "OpSampledImage"; + case OpImageSampleImplicitLod: return "OpImageSampleImplicitLod"; + case OpImageSampleExplicitLod: return "OpImageSampleExplicitLod"; + case OpImageSampleDrefImplicitLod: return "OpImageSampleDrefImplicitLod"; + case OpImageSampleDrefExplicitLod: return "OpImageSampleDrefExplicitLod"; + case OpImageSampleProjImplicitLod: return "OpImageSampleProjImplicitLod"; + case OpImageSampleProjExplicitLod: return "OpImageSampleProjExplicitLod"; + case OpImageSampleProjDrefImplicitLod: return "OpImageSampleProjDrefImplicitLod"; + case OpImageSampleProjDrefExplicitLod: return "OpImageSampleProjDrefExplicitLod"; + case OpImageFetch: return "OpImageFetch"; + case OpImageGather: return "OpImageGather"; + case OpImageDrefGather: return "OpImageDrefGather"; + case OpImageRead: return "OpImageRead"; + case OpImageWrite: return "OpImageWrite"; + case OpImage: return "OpImage"; + case OpImageQueryFormat: return "OpImageQueryFormat"; + case OpImageQueryOrder: return "OpImageQueryOrder"; + case OpImageQuerySizeLod: return "OpImageQuerySizeLod"; + case OpImageQuerySize: return "OpImageQuerySize"; + case OpImageQueryLod: return "OpImageQueryLod"; + case OpImageQueryLevels: return "OpImageQueryLevels"; + case OpImageQuerySamples: return "OpImageQuerySamples"; + case OpConvertFToU: return "OpConvertFToU"; + case OpConvertFToS: return "OpConvertFToS"; + case OpConvertSToF: return "OpConvertSToF"; + case OpConvertUToF: return "OpConvertUToF"; + case OpUConvert: return "OpUConvert"; + case OpSConvert: return "OpSConvert"; + case OpFConvert: return "OpFConvert"; + case OpQuantizeToF16: return "OpQuantizeToF16"; + case OpConvertPtrToU: return "OpConvertPtrToU"; + case OpSatConvertSToU: return "OpSatConvertSToU"; + case OpSatConvertUToS: return "OpSatConvertUToS"; + case OpConvertUToPtr: return "OpConvertUToPtr"; + case OpPtrCastToGeneric: return "OpPtrCastToGeneric"; + case OpGenericCastToPtr: return "OpGenericCastToPtr"; + case OpGenericCastToPtrExplicit: return "OpGenericCastToPtrExplicit"; + case OpBitcast: return "OpBitcast"; + case OpSNegate: return "OpSNegate"; + case OpFNegate: return "OpFNegate"; + case OpIAdd: return "OpIAdd"; + case OpFAdd: return "OpFAdd"; + case OpISub: return "OpISub"; + case OpFSub: return "OpFSub"; + case OpIMul: return "OpIMul"; + case OpFMul: return "OpFMul"; + case OpUDiv: return "OpUDiv"; + case OpSDiv: return "OpSDiv"; + case OpFDiv: return "OpFDiv"; + case OpUMod: return "OpUMod"; + case OpSRem: return "OpSRem"; + case OpSMod: return "OpSMod"; + case OpFRem: return "OpFRem"; + case OpFMod: return "OpFMod"; + case OpVectorTimesScalar: return "OpVectorTimesScalar"; + case OpMatrixTimesScalar: return "OpMatrixTimesScalar"; + case OpVectorTimesMatrix: return "OpVectorTimesMatrix"; + case OpMatrixTimesVector: return "OpMatrixTimesVector"; + case OpMatrixTimesMatrix: return "OpMatrixTimesMatrix"; + case OpOuterProduct: return "OpOuterProduct"; + case OpDot: return "OpDot"; + case OpIAddCarry: return "OpIAddCarry"; + case OpISubBorrow: return "OpISubBorrow"; + case OpUMulExtended: return "OpUMulExtended"; + case OpSMulExtended: return "OpSMulExtended"; + case OpAny: return "OpAny"; + case OpAll: return "OpAll"; + case OpIsNan: return "OpIsNan"; + case OpIsInf: return "OpIsInf"; + case OpIsFinite: return "OpIsFinite"; + case OpIsNormal: return "OpIsNormal"; + case OpSignBitSet: return "OpSignBitSet"; + case OpLessOrGreater: return "OpLessOrGreater"; + case OpOrdered: return "OpOrdered"; + case OpUnordered: return "OpUnordered"; + case OpLogicalEqual: return "OpLogicalEqual"; + case OpLogicalNotEqual: return "OpLogicalNotEqual"; + case OpLogicalOr: return "OpLogicalOr"; + case OpLogicalAnd: return "OpLogicalAnd"; + case OpLogicalNot: return "OpLogicalNot"; + case OpSelect: return "OpSelect"; + case OpIEqual: return "OpIEqual"; + case OpINotEqual: return "OpINotEqual"; + case OpUGreaterThan: return "OpUGreaterThan"; + case OpSGreaterThan: return "OpSGreaterThan"; + case OpUGreaterThanEqual: return "OpUGreaterThanEqual"; + case OpSGreaterThanEqual: return "OpSGreaterThanEqual"; + case OpULessThan: return "OpULessThan"; + case OpSLessThan: return "OpSLessThan"; + case OpULessThanEqual: return "OpULessThanEqual"; + case OpSLessThanEqual: return "OpSLessThanEqual"; + case OpFOrdEqual: return "OpFOrdEqual"; + case OpFUnordEqual: return "OpFUnordEqual"; + case OpFOrdNotEqual: return "OpFOrdNotEqual"; + case OpFUnordNotEqual: return "OpFUnordNotEqual"; + case OpFOrdLessThan: return "OpFOrdLessThan"; + case OpFUnordLessThan: return "OpFUnordLessThan"; + case OpFOrdGreaterThan: return "OpFOrdGreaterThan"; + case OpFUnordGreaterThan: return "OpFUnordGreaterThan"; + case OpFOrdLessThanEqual: return "OpFOrdLessThanEqual"; + case OpFUnordLessThanEqual: return "OpFUnordLessThanEqual"; + case OpFOrdGreaterThanEqual: return "OpFOrdGreaterThanEqual"; + case OpFUnordGreaterThanEqual: return "OpFUnordGreaterThanEqual"; + case OpShiftRightLogical: return "OpShiftRightLogical"; + case OpShiftRightArithmetic: return "OpShiftRightArithmetic"; + case OpShiftLeftLogical: return "OpShiftLeftLogical"; + case OpBitwiseOr: return "OpBitwiseOr"; + case OpBitwiseXor: return "OpBitwiseXor"; + case OpBitwiseAnd: return "OpBitwiseAnd"; + case OpNot: return "OpNot"; + case OpBitFieldInsert: return "OpBitFieldInsert"; + case OpBitFieldSExtract: return "OpBitFieldSExtract"; + case OpBitFieldUExtract: return "OpBitFieldUExtract"; + case OpBitReverse: return "OpBitReverse"; + case OpBitCount: return "OpBitCount"; + case OpDPdx: return "OpDPdx"; + case OpDPdy: return "OpDPdy"; + case OpFwidth: return "OpFwidth"; + case OpDPdxFine: return "OpDPdxFine"; + case OpDPdyFine: return "OpDPdyFine"; + case OpFwidthFine: return "OpFwidthFine"; + case OpDPdxCoarse: return "OpDPdxCoarse"; + case OpDPdyCoarse: return "OpDPdyCoarse"; + case OpFwidthCoarse: return "OpFwidthCoarse"; + case OpEmitVertex: return "OpEmitVertex"; + case OpEndPrimitive: return "OpEndPrimitive"; + case OpEmitStreamVertex: return "OpEmitStreamVertex"; + case OpEndStreamPrimitive: return "OpEndStreamPrimitive"; + case OpControlBarrier: return "OpControlBarrier"; + case OpMemoryBarrier: return "OpMemoryBarrier"; + case OpAtomicLoad: return "OpAtomicLoad"; + case OpAtomicStore: return "OpAtomicStore"; + case OpAtomicExchange: return "OpAtomicExchange"; + case OpAtomicCompareExchange: return "OpAtomicCompareExchange"; + case OpAtomicCompareExchangeWeak: return "OpAtomicCompareExchangeWeak"; + case OpAtomicIIncrement: return "OpAtomicIIncrement"; + case OpAtomicIDecrement: return "OpAtomicIDecrement"; + case OpAtomicIAdd: return "OpAtomicIAdd"; + case OpAtomicISub: return "OpAtomicISub"; + case OpAtomicSMin: return "OpAtomicSMin"; + case OpAtomicUMin: return "OpAtomicUMin"; + case OpAtomicSMax: return "OpAtomicSMax"; + case OpAtomicUMax: return "OpAtomicUMax"; + case OpAtomicAnd: return "OpAtomicAnd"; + case OpAtomicOr: return "OpAtomicOr"; + case OpAtomicXor: return "OpAtomicXor"; + case OpPhi: return "OpPhi"; + case OpLoopMerge: return "OpLoopMerge"; + case OpSelectionMerge: return "OpSelectionMerge"; + case OpLabel: return "OpLabel"; + case OpBranch: return "OpBranch"; + case OpBranchConditional: return "OpBranchConditional"; + case OpSwitch: return "OpSwitch"; + case OpKill: return "OpKill"; + case OpReturn: return "OpReturn"; + case OpReturnValue: return "OpReturnValue"; + case OpUnreachable: return "OpUnreachable"; + case OpLifetimeStart: return "OpLifetimeStart"; + case OpLifetimeStop: return "OpLifetimeStop"; + case OpGroupAsyncCopy: return "OpGroupAsyncCopy"; + case OpGroupWaitEvents: return "OpGroupWaitEvents"; + case OpGroupAll: return "OpGroupAll"; + case OpGroupAny: return "OpGroupAny"; + case OpGroupBroadcast: return "OpGroupBroadcast"; + case OpGroupIAdd: return "OpGroupIAdd"; + case OpGroupFAdd: return "OpGroupFAdd"; + case OpGroupFMin: return "OpGroupFMin"; + case OpGroupUMin: return "OpGroupUMin"; + case OpGroupSMin: return "OpGroupSMin"; + case OpGroupFMax: return "OpGroupFMax"; + case OpGroupUMax: return "OpGroupUMax"; + case OpGroupSMax: return "OpGroupSMax"; + case OpReadPipe: return "OpReadPipe"; + case OpWritePipe: return "OpWritePipe"; + case OpReservedReadPipe: return "OpReservedReadPipe"; + case OpReservedWritePipe: return "OpReservedWritePipe"; + case OpReserveReadPipePackets: return "OpReserveReadPipePackets"; + case OpReserveWritePipePackets: return "OpReserveWritePipePackets"; + case OpCommitReadPipe: return "OpCommitReadPipe"; + case OpCommitWritePipe: return "OpCommitWritePipe"; + case OpIsValidReserveId: return "OpIsValidReserveId"; + case OpGetNumPipePackets: return "OpGetNumPipePackets"; + case OpGetMaxPipePackets: return "OpGetMaxPipePackets"; + case OpGroupReserveReadPipePackets: return "OpGroupReserveReadPipePackets"; + case OpGroupReserveWritePipePackets: return "OpGroupReserveWritePipePackets"; + case OpGroupCommitReadPipe: return "OpGroupCommitReadPipe"; + case OpGroupCommitWritePipe: return "OpGroupCommitWritePipe"; + case OpEnqueueMarker: return "OpEnqueueMarker"; + case OpEnqueueKernel: return "OpEnqueueKernel"; + case OpGetKernelNDrangeSubGroupCount: return "OpGetKernelNDrangeSubGroupCount"; + case OpGetKernelNDrangeMaxSubGroupSize: return "OpGetKernelNDrangeMaxSubGroupSize"; + case OpGetKernelWorkGroupSize: return "OpGetKernelWorkGroupSize"; + case OpGetKernelPreferredWorkGroupSizeMultiple: return "OpGetKernelPreferredWorkGroupSizeMultiple"; + case OpRetainEvent: return "OpRetainEvent"; + case OpReleaseEvent: return "OpReleaseEvent"; + case OpCreateUserEvent: return "OpCreateUserEvent"; + case OpIsValidEvent: return "OpIsValidEvent"; + case OpSetUserEventStatus: return "OpSetUserEventStatus"; + case OpCaptureEventProfilingInfo: return "OpCaptureEventProfilingInfo"; + case OpGetDefaultQueue: return "OpGetDefaultQueue"; + case OpBuildNDRange: return "OpBuildNDRange"; + case OpImageSparseSampleImplicitLod: return "OpImageSparseSampleImplicitLod"; + case OpImageSparseSampleExplicitLod: return "OpImageSparseSampleExplicitLod"; + case OpImageSparseSampleDrefImplicitLod: return "OpImageSparseSampleDrefImplicitLod"; + case OpImageSparseSampleDrefExplicitLod: return "OpImageSparseSampleDrefExplicitLod"; + case OpImageSparseSampleProjImplicitLod: return "OpImageSparseSampleProjImplicitLod"; + case OpImageSparseSampleProjExplicitLod: return "OpImageSparseSampleProjExplicitLod"; + case OpImageSparseSampleProjDrefImplicitLod: return "OpImageSparseSampleProjDrefImplicitLod"; + case OpImageSparseSampleProjDrefExplicitLod: return "OpImageSparseSampleProjDrefExplicitLod"; + case OpImageSparseFetch: return "OpImageSparseFetch"; + case OpImageSparseGather: return "OpImageSparseGather"; + case OpImageSparseDrefGather: return "OpImageSparseDrefGather"; + case OpImageSparseTexelsResident: return "OpImageSparseTexelsResident"; + case OpNoLine: return "OpNoLine"; + case OpAtomicFlagTestAndSet: return "OpAtomicFlagTestAndSet"; + case OpAtomicFlagClear: return "OpAtomicFlagClear"; + case OpImageSparseRead: return "OpImageSparseRead"; + case OpSizeOf: return "OpSizeOf"; + case OpTypePipeStorage: return "OpTypePipeStorage"; + case OpConstantPipeStorage: return "OpConstantPipeStorage"; + case OpCreatePipeFromPipeStorage: return "OpCreatePipeFromPipeStorage"; + case OpGetKernelLocalSizeForSubgroupCount: return "OpGetKernelLocalSizeForSubgroupCount"; + case OpGetKernelMaxNumSubgroups: return "OpGetKernelMaxNumSubgroups"; + case OpTypeNamedBarrier: return "OpTypeNamedBarrier"; + case OpNamedBarrierInitialize: return "OpNamedBarrierInitialize"; + case OpMemoryNamedBarrier: return "OpMemoryNamedBarrier"; + case OpModuleProcessed: return "OpModuleProcessed"; + case OpExecutionModeId: return "OpExecutionModeId"; + case OpDecorateId: return "OpDecorateId"; + case OpGroupNonUniformElect: return "OpGroupNonUniformElect"; + case OpGroupNonUniformAll: return "OpGroupNonUniformAll"; + case OpGroupNonUniformAny: return "OpGroupNonUniformAny"; + case OpGroupNonUniformAllEqual: return "OpGroupNonUniformAllEqual"; + case OpGroupNonUniformBroadcast: return "OpGroupNonUniformBroadcast"; + case OpGroupNonUniformBroadcastFirst: return "OpGroupNonUniformBroadcastFirst"; + case OpGroupNonUniformBallot: return "OpGroupNonUniformBallot"; + case OpGroupNonUniformInverseBallot: return "OpGroupNonUniformInverseBallot"; + case OpGroupNonUniformBallotBitExtract: return "OpGroupNonUniformBallotBitExtract"; + case OpGroupNonUniformBallotBitCount: return "OpGroupNonUniformBallotBitCount"; + case OpGroupNonUniformBallotFindLSB: return "OpGroupNonUniformBallotFindLSB"; + case OpGroupNonUniformBallotFindMSB: return "OpGroupNonUniformBallotFindMSB"; + case OpGroupNonUniformShuffle: return "OpGroupNonUniformShuffle"; + case OpGroupNonUniformShuffleXor: return "OpGroupNonUniformShuffleXor"; + case OpGroupNonUniformShuffleUp: return "OpGroupNonUniformShuffleUp"; + case OpGroupNonUniformShuffleDown: return "OpGroupNonUniformShuffleDown"; + case OpGroupNonUniformIAdd: return "OpGroupNonUniformIAdd"; + case OpGroupNonUniformFAdd: return "OpGroupNonUniformFAdd"; + case OpGroupNonUniformIMul: return "OpGroupNonUniformIMul"; + case OpGroupNonUniformFMul: return "OpGroupNonUniformFMul"; + case OpGroupNonUniformSMin: return "OpGroupNonUniformSMin"; + case OpGroupNonUniformUMin: return "OpGroupNonUniformUMin"; + case OpGroupNonUniformFMin: return "OpGroupNonUniformFMin"; + case OpGroupNonUniformSMax: return "OpGroupNonUniformSMax"; + case OpGroupNonUniformUMax: return "OpGroupNonUniformUMax"; + case OpGroupNonUniformFMax: return "OpGroupNonUniformFMax"; + case OpGroupNonUniformBitwiseAnd: return "OpGroupNonUniformBitwiseAnd"; + case OpGroupNonUniformBitwiseOr: return "OpGroupNonUniformBitwiseOr"; + case OpGroupNonUniformBitwiseXor: return "OpGroupNonUniformBitwiseXor"; + case OpGroupNonUniformLogicalAnd: return "OpGroupNonUniformLogicalAnd"; + case OpGroupNonUniformLogicalOr: return "OpGroupNonUniformLogicalOr"; + case OpGroupNonUniformLogicalXor: return "OpGroupNonUniformLogicalXor"; + case OpGroupNonUniformQuadBroadcast: return "OpGroupNonUniformQuadBroadcast"; + case OpGroupNonUniformQuadSwap: return "OpGroupNonUniformQuadSwap"; + case OpCopyLogical: return "OpCopyLogical"; + case OpPtrEqual: return "OpPtrEqual"; + case OpPtrNotEqual: return "OpPtrNotEqual"; + case OpPtrDiff: return "OpPtrDiff"; + case OpColorAttachmentReadEXT: return "OpColorAttachmentReadEXT"; + case OpDepthAttachmentReadEXT: return "OpDepthAttachmentReadEXT"; + case OpStencilAttachmentReadEXT: return "OpStencilAttachmentReadEXT"; + case OpTypeTensorARM: return "OpTypeTensorARM"; + case OpTensorReadARM: return "OpTensorReadARM"; + case OpTensorWriteARM: return "OpTensorWriteARM"; + case OpTensorQuerySizeARM: return "OpTensorQuerySizeARM"; + case OpGraphConstantARM: return "OpGraphConstantARM"; + case OpGraphEntryPointARM: return "OpGraphEntryPointARM"; + case OpGraphARM: return "OpGraphARM"; + case OpGraphInputARM: return "OpGraphInputARM"; + case OpGraphSetOutputARM: return "OpGraphSetOutputARM"; + case OpGraphEndARM: return "OpGraphEndARM"; + case OpTypeGraphARM: return "OpTypeGraphARM"; + case OpTerminateInvocation: return "OpTerminateInvocation"; + case OpTypeUntypedPointerKHR: return "OpTypeUntypedPointerKHR"; + case OpUntypedVariableKHR: return "OpUntypedVariableKHR"; + case OpUntypedAccessChainKHR: return "OpUntypedAccessChainKHR"; + case OpUntypedInBoundsAccessChainKHR: return "OpUntypedInBoundsAccessChainKHR"; + case OpSubgroupBallotKHR: return "OpSubgroupBallotKHR"; + case OpSubgroupFirstInvocationKHR: return "OpSubgroupFirstInvocationKHR"; + case OpUntypedPtrAccessChainKHR: return "OpUntypedPtrAccessChainKHR"; + case OpUntypedInBoundsPtrAccessChainKHR: return "OpUntypedInBoundsPtrAccessChainKHR"; + case OpUntypedArrayLengthKHR: return "OpUntypedArrayLengthKHR"; + case OpUntypedPrefetchKHR: return "OpUntypedPrefetchKHR"; + case OpFmaKHR: return "OpFmaKHR"; + case OpSubgroupAllKHR: return "OpSubgroupAllKHR"; + case OpSubgroupAnyKHR: return "OpSubgroupAnyKHR"; + case OpSubgroupAllEqualKHR: return "OpSubgroupAllEqualKHR"; + case OpGroupNonUniformRotateKHR: return "OpGroupNonUniformRotateKHR"; + case OpSubgroupReadInvocationKHR: return "OpSubgroupReadInvocationKHR"; + case OpExtInstWithForwardRefsKHR: return "OpExtInstWithForwardRefsKHR"; + case OpUntypedGroupAsyncCopyKHR: return "OpUntypedGroupAsyncCopyKHR"; + case OpTraceRayKHR: return "OpTraceRayKHR"; + case OpExecuteCallableKHR: return "OpExecuteCallableKHR"; + case OpConvertUToAccelerationStructureKHR: return "OpConvertUToAccelerationStructureKHR"; + case OpIgnoreIntersectionKHR: return "OpIgnoreIntersectionKHR"; + case OpTerminateRayKHR: return "OpTerminateRayKHR"; + case OpSDot: return "OpSDot"; + case OpUDot: return "OpUDot"; + case OpSUDot: return "OpSUDot"; + case OpSDotAccSat: return "OpSDotAccSat"; + case OpUDotAccSat: return "OpUDotAccSat"; + case OpSUDotAccSat: return "OpSUDotAccSat"; + case OpTypeCooperativeMatrixKHR: return "OpTypeCooperativeMatrixKHR"; + case OpCooperativeMatrixLoadKHR: return "OpCooperativeMatrixLoadKHR"; + case OpCooperativeMatrixStoreKHR: return "OpCooperativeMatrixStoreKHR"; + case OpCooperativeMatrixMulAddKHR: return "OpCooperativeMatrixMulAddKHR"; + case OpCooperativeMatrixLengthKHR: return "OpCooperativeMatrixLengthKHR"; + case OpConstantCompositeReplicateEXT: return "OpConstantCompositeReplicateEXT"; + case OpSpecConstantCompositeReplicateEXT: return "OpSpecConstantCompositeReplicateEXT"; + case OpCompositeConstructReplicateEXT: return "OpCompositeConstructReplicateEXT"; + case OpTypeRayQueryKHR: return "OpTypeRayQueryKHR"; + case OpRayQueryInitializeKHR: return "OpRayQueryInitializeKHR"; + case OpRayQueryTerminateKHR: return "OpRayQueryTerminateKHR"; + case OpRayQueryGenerateIntersectionKHR: return "OpRayQueryGenerateIntersectionKHR"; + case OpRayQueryConfirmIntersectionKHR: return "OpRayQueryConfirmIntersectionKHR"; + case OpRayQueryProceedKHR: return "OpRayQueryProceedKHR"; + case OpRayQueryGetIntersectionTypeKHR: return "OpRayQueryGetIntersectionTypeKHR"; + case OpImageSampleWeightedQCOM: return "OpImageSampleWeightedQCOM"; + case OpImageBoxFilterQCOM: return "OpImageBoxFilterQCOM"; + case OpImageBlockMatchSSDQCOM: return "OpImageBlockMatchSSDQCOM"; + case OpImageBlockMatchSADQCOM: return "OpImageBlockMatchSADQCOM"; + case OpBitCastArrayQCOM: return "OpBitCastArrayQCOM"; + case OpImageBlockMatchWindowSSDQCOM: return "OpImageBlockMatchWindowSSDQCOM"; + case OpImageBlockMatchWindowSADQCOM: return "OpImageBlockMatchWindowSADQCOM"; + case OpImageBlockMatchGatherSSDQCOM: return "OpImageBlockMatchGatherSSDQCOM"; + case OpImageBlockMatchGatherSADQCOM: return "OpImageBlockMatchGatherSADQCOM"; + case OpCompositeConstructCoopMatQCOM: return "OpCompositeConstructCoopMatQCOM"; + case OpCompositeExtractCoopMatQCOM: return "OpCompositeExtractCoopMatQCOM"; + case OpExtractSubArrayQCOM: return "OpExtractSubArrayQCOM"; + case OpGroupIAddNonUniformAMD: return "OpGroupIAddNonUniformAMD"; + case OpGroupFAddNonUniformAMD: return "OpGroupFAddNonUniformAMD"; + case OpGroupFMinNonUniformAMD: return "OpGroupFMinNonUniformAMD"; + case OpGroupUMinNonUniformAMD: return "OpGroupUMinNonUniformAMD"; + case OpGroupSMinNonUniformAMD: return "OpGroupSMinNonUniformAMD"; + case OpGroupFMaxNonUniformAMD: return "OpGroupFMaxNonUniformAMD"; + case OpGroupUMaxNonUniformAMD: return "OpGroupUMaxNonUniformAMD"; + case OpGroupSMaxNonUniformAMD: return "OpGroupSMaxNonUniformAMD"; + case OpFragmentMaskFetchAMD: return "OpFragmentMaskFetchAMD"; + case OpFragmentFetchAMD: return "OpFragmentFetchAMD"; + case OpReadClockKHR: return "OpReadClockKHR"; + case OpAllocateNodePayloadsAMDX: return "OpAllocateNodePayloadsAMDX"; + case OpEnqueueNodePayloadsAMDX: return "OpEnqueueNodePayloadsAMDX"; + case OpTypeNodePayloadArrayAMDX: return "OpTypeNodePayloadArrayAMDX"; + case OpFinishWritingNodePayloadAMDX: return "OpFinishWritingNodePayloadAMDX"; + case OpNodePayloadArrayLengthAMDX: return "OpNodePayloadArrayLengthAMDX"; + case OpIsNodePayloadValidAMDX: return "OpIsNodePayloadValidAMDX"; + case OpConstantStringAMDX: return "OpConstantStringAMDX"; + case OpSpecConstantStringAMDX: return "OpSpecConstantStringAMDX"; + case OpGroupNonUniformQuadAllKHR: return "OpGroupNonUniformQuadAllKHR"; + case OpGroupNonUniformQuadAnyKHR: return "OpGroupNonUniformQuadAnyKHR"; + case OpTypeBufferEXT: return "OpTypeBufferEXT"; + case OpBufferPointerEXT: return "OpBufferPointerEXT"; + case OpAbortKHR: return "OpAbortKHR"; + case OpUntypedImageTexelPointerEXT: return "OpUntypedImageTexelPointerEXT"; + case OpMemberDecorateIdEXT: return "OpMemberDecorateIdEXT"; + case OpConstantSizeOfEXT: return "OpConstantSizeOfEXT"; + case OpConstantDataKHR: return "OpConstantDataKHR"; + case OpSpecConstantDataKHR: return "OpSpecConstantDataKHR"; + case OpPoisonKHR: return "OpPoisonKHR"; + case OpFreezeKHR: return "OpFreezeKHR"; + case OpHitObjectRecordHitMotionNV: return "OpHitObjectRecordHitMotionNV"; + case OpHitObjectRecordHitWithIndexMotionNV: return "OpHitObjectRecordHitWithIndexMotionNV"; + case OpHitObjectRecordMissMotionNV: return "OpHitObjectRecordMissMotionNV"; + case OpHitObjectGetWorldToObjectNV: return "OpHitObjectGetWorldToObjectNV"; + case OpHitObjectGetObjectToWorldNV: return "OpHitObjectGetObjectToWorldNV"; + case OpHitObjectGetObjectRayDirectionNV: return "OpHitObjectGetObjectRayDirectionNV"; + case OpHitObjectGetObjectRayOriginNV: return "OpHitObjectGetObjectRayOriginNV"; + case OpHitObjectTraceRayMotionNV: return "OpHitObjectTraceRayMotionNV"; + case OpHitObjectGetShaderRecordBufferHandleNV: return "OpHitObjectGetShaderRecordBufferHandleNV"; + case OpHitObjectGetShaderBindingTableRecordIndexNV: return "OpHitObjectGetShaderBindingTableRecordIndexNV"; + case OpHitObjectRecordEmptyNV: return "OpHitObjectRecordEmptyNV"; + case OpHitObjectTraceRayNV: return "OpHitObjectTraceRayNV"; + case OpHitObjectRecordHitNV: return "OpHitObjectRecordHitNV"; + case OpHitObjectRecordHitWithIndexNV: return "OpHitObjectRecordHitWithIndexNV"; + case OpHitObjectRecordMissNV: return "OpHitObjectRecordMissNV"; + case OpHitObjectExecuteShaderNV: return "OpHitObjectExecuteShaderNV"; + case OpHitObjectGetCurrentTimeNV: return "OpHitObjectGetCurrentTimeNV"; + case OpHitObjectGetAttributesNV: return "OpHitObjectGetAttributesNV"; + case OpHitObjectGetHitKindNV: return "OpHitObjectGetHitKindNV"; + case OpHitObjectGetPrimitiveIndexNV: return "OpHitObjectGetPrimitiveIndexNV"; + case OpHitObjectGetGeometryIndexNV: return "OpHitObjectGetGeometryIndexNV"; + case OpHitObjectGetInstanceIdNV: return "OpHitObjectGetInstanceIdNV"; + case OpHitObjectGetInstanceCustomIndexNV: return "OpHitObjectGetInstanceCustomIndexNV"; + case OpHitObjectGetWorldRayDirectionNV: return "OpHitObjectGetWorldRayDirectionNV"; + case OpHitObjectGetWorldRayOriginNV: return "OpHitObjectGetWorldRayOriginNV"; + case OpHitObjectGetRayTMaxNV: return "OpHitObjectGetRayTMaxNV"; + case OpHitObjectGetRayTMinNV: return "OpHitObjectGetRayTMinNV"; + case OpHitObjectIsEmptyNV: return "OpHitObjectIsEmptyNV"; + case OpHitObjectIsHitNV: return "OpHitObjectIsHitNV"; + case OpHitObjectIsMissNV: return "OpHitObjectIsMissNV"; + case OpReorderThreadWithHitObjectNV: return "OpReorderThreadWithHitObjectNV"; + case OpReorderThreadWithHintNV: return "OpReorderThreadWithHintNV"; + case OpTypeHitObjectNV: return "OpTypeHitObjectNV"; + case OpImageSampleFootprintNV: return "OpImageSampleFootprintNV"; + case OpTypeCooperativeVectorNV: return "OpTypeCooperativeVectorNV"; + case OpCooperativeVectorMatrixMulNV: return "OpCooperativeVectorMatrixMulNV"; + case OpCooperativeVectorOuterProductAccumulateNV: return "OpCooperativeVectorOuterProductAccumulateNV"; + case OpCooperativeVectorReduceSumAccumulateNV: return "OpCooperativeVectorReduceSumAccumulateNV"; + case OpCooperativeVectorMatrixMulAddNV: return "OpCooperativeVectorMatrixMulAddNV"; + case OpCooperativeMatrixConvertNV: return "OpCooperativeMatrixConvertNV"; + case OpEmitMeshTasksEXT: return "OpEmitMeshTasksEXT"; + case OpSetMeshOutputsEXT: return "OpSetMeshOutputsEXT"; + case OpGroupNonUniformPartitionEXT: return "OpGroupNonUniformPartitionEXT"; + case OpWritePackedPrimitiveIndices4x8NV: return "OpWritePackedPrimitiveIndices4x8NV"; + case OpFetchMicroTriangleVertexPositionNV: return "OpFetchMicroTriangleVertexPositionNV"; + case OpFetchMicroTriangleVertexBarycentricNV: return "OpFetchMicroTriangleVertexBarycentricNV"; + case OpCooperativeVectorLoadNV: return "OpCooperativeVectorLoadNV"; + case OpCooperativeVectorStoreNV: return "OpCooperativeVectorStoreNV"; + case OpHitObjectRecordFromQueryEXT: return "OpHitObjectRecordFromQueryEXT"; + case OpHitObjectRecordMissEXT: return "OpHitObjectRecordMissEXT"; + case OpHitObjectRecordMissMotionEXT: return "OpHitObjectRecordMissMotionEXT"; + case OpHitObjectGetIntersectionTriangleVertexPositionsEXT: return "OpHitObjectGetIntersectionTriangleVertexPositionsEXT"; + case OpHitObjectGetRayFlagsEXT: return "OpHitObjectGetRayFlagsEXT"; + case OpHitObjectSetShaderBindingTableRecordIndexEXT: return "OpHitObjectSetShaderBindingTableRecordIndexEXT"; + case OpHitObjectReorderExecuteShaderEXT: return "OpHitObjectReorderExecuteShaderEXT"; + case OpHitObjectTraceReorderExecuteEXT: return "OpHitObjectTraceReorderExecuteEXT"; + case OpHitObjectTraceMotionReorderExecuteEXT: return "OpHitObjectTraceMotionReorderExecuteEXT"; + case OpTypeHitObjectEXT: return "OpTypeHitObjectEXT"; + case OpReorderThreadWithHintEXT: return "OpReorderThreadWithHintEXT"; + case OpReorderThreadWithHitObjectEXT: return "OpReorderThreadWithHitObjectEXT"; + case OpHitObjectTraceRayEXT: return "OpHitObjectTraceRayEXT"; + case OpHitObjectTraceRayMotionEXT: return "OpHitObjectTraceRayMotionEXT"; + case OpHitObjectRecordEmptyEXT: return "OpHitObjectRecordEmptyEXT"; + case OpHitObjectExecuteShaderEXT: return "OpHitObjectExecuteShaderEXT"; + case OpHitObjectGetCurrentTimeEXT: return "OpHitObjectGetCurrentTimeEXT"; + case OpHitObjectGetAttributesEXT: return "OpHitObjectGetAttributesEXT"; + case OpHitObjectGetHitKindEXT: return "OpHitObjectGetHitKindEXT"; + case OpHitObjectGetPrimitiveIndexEXT: return "OpHitObjectGetPrimitiveIndexEXT"; + case OpHitObjectGetGeometryIndexEXT: return "OpHitObjectGetGeometryIndexEXT"; + case OpHitObjectGetInstanceIdEXT: return "OpHitObjectGetInstanceIdEXT"; + case OpHitObjectGetInstanceCustomIndexEXT: return "OpHitObjectGetInstanceCustomIndexEXT"; + case OpHitObjectGetObjectRayOriginEXT: return "OpHitObjectGetObjectRayOriginEXT"; + case OpHitObjectGetObjectRayDirectionEXT: return "OpHitObjectGetObjectRayDirectionEXT"; + case OpHitObjectGetWorldRayDirectionEXT: return "OpHitObjectGetWorldRayDirectionEXT"; + case OpHitObjectGetWorldRayOriginEXT: return "OpHitObjectGetWorldRayOriginEXT"; + case OpHitObjectGetObjectToWorldEXT: return "OpHitObjectGetObjectToWorldEXT"; + case OpHitObjectGetWorldToObjectEXT: return "OpHitObjectGetWorldToObjectEXT"; + case OpHitObjectGetRayTMaxEXT: return "OpHitObjectGetRayTMaxEXT"; + case OpReportIntersectionKHR: return "OpReportIntersectionKHR"; + case OpIgnoreIntersectionNV: return "OpIgnoreIntersectionNV"; + case OpTerminateRayNV: return "OpTerminateRayNV"; + case OpTraceNV: return "OpTraceNV"; + case OpTraceMotionNV: return "OpTraceMotionNV"; + case OpTraceRayMotionNV: return "OpTraceRayMotionNV"; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: return "OpRayQueryGetIntersectionTriangleVertexPositionsKHR"; + case OpTypeAccelerationStructureKHR: return "OpTypeAccelerationStructureKHR"; + case OpExecuteCallableNV: return "OpExecuteCallableNV"; + case OpRayQueryGetClusterIdNV: return "OpRayQueryGetClusterIdNV"; + case OpHitObjectGetClusterIdNV: return "OpHitObjectGetClusterIdNV"; + case OpHitObjectGetRayTMinEXT: return "OpHitObjectGetRayTMinEXT"; + case OpHitObjectGetShaderBindingTableRecordIndexEXT: return "OpHitObjectGetShaderBindingTableRecordIndexEXT"; + case OpHitObjectGetShaderRecordBufferHandleEXT: return "OpHitObjectGetShaderRecordBufferHandleEXT"; + case OpHitObjectIsEmptyEXT: return "OpHitObjectIsEmptyEXT"; + case OpHitObjectIsHitEXT: return "OpHitObjectIsHitEXT"; + case OpHitObjectIsMissEXT: return "OpHitObjectIsMissEXT"; + case OpTypeCooperativeMatrixNV: return "OpTypeCooperativeMatrixNV"; + case OpCooperativeMatrixLoadNV: return "OpCooperativeMatrixLoadNV"; + case OpCooperativeMatrixStoreNV: return "OpCooperativeMatrixStoreNV"; + case OpCooperativeMatrixMulAddNV: return "OpCooperativeMatrixMulAddNV"; + case OpCooperativeMatrixLengthNV: return "OpCooperativeMatrixLengthNV"; + case OpBeginInvocationInterlockEXT: return "OpBeginInvocationInterlockEXT"; + case OpEndInvocationInterlockEXT: return "OpEndInvocationInterlockEXT"; + case OpCooperativeMatrixReduceNV: return "OpCooperativeMatrixReduceNV"; + case OpCooperativeMatrixLoadTensorNV: return "OpCooperativeMatrixLoadTensorNV"; + case OpCooperativeMatrixStoreTensorNV: return "OpCooperativeMatrixStoreTensorNV"; + case OpCooperativeMatrixPerElementOpNV: return "OpCooperativeMatrixPerElementOpNV"; + case OpTypeTensorLayoutNV: return "OpTypeTensorLayoutNV"; + case OpTypeTensorViewNV: return "OpTypeTensorViewNV"; + case OpCreateTensorLayoutNV: return "OpCreateTensorLayoutNV"; + case OpTensorLayoutSetDimensionNV: return "OpTensorLayoutSetDimensionNV"; + case OpTensorLayoutSetStrideNV: return "OpTensorLayoutSetStrideNV"; + case OpTensorLayoutSliceNV: return "OpTensorLayoutSliceNV"; + case OpTensorLayoutSetClampValueNV: return "OpTensorLayoutSetClampValueNV"; + case OpCreateTensorViewNV: return "OpCreateTensorViewNV"; + case OpTensorViewSetDimensionNV: return "OpTensorViewSetDimensionNV"; + case OpTensorViewSetStrideNV: return "OpTensorViewSetStrideNV"; + case OpDemoteToHelperInvocation: return "OpDemoteToHelperInvocation"; + case OpIsHelperInvocationEXT: return "OpIsHelperInvocationEXT"; + case OpTensorViewSetClipNV: return "OpTensorViewSetClipNV"; + case OpTensorLayoutSetBlockSizeNV: return "OpTensorLayoutSetBlockSizeNV"; + case OpCooperativeMatrixTransposeNV: return "OpCooperativeMatrixTransposeNV"; + case OpConvertUToImageNV: return "OpConvertUToImageNV"; + case OpConvertUToSamplerNV: return "OpConvertUToSamplerNV"; + case OpConvertImageToUNV: return "OpConvertImageToUNV"; + case OpConvertSamplerToUNV: return "OpConvertSamplerToUNV"; + case OpConvertUToSampledImageNV: return "OpConvertUToSampledImageNV"; + case OpConvertSampledImageToUNV: return "OpConvertSampledImageToUNV"; + case OpSamplerImageAddressingModeNV: return "OpSamplerImageAddressingModeNV"; + case OpRawAccessChainNV: return "OpRawAccessChainNV"; + case OpRayQueryGetIntersectionSpherePositionNV: return "OpRayQueryGetIntersectionSpherePositionNV"; + case OpRayQueryGetIntersectionSphereRadiusNV: return "OpRayQueryGetIntersectionSphereRadiusNV"; + case OpRayQueryGetIntersectionLSSPositionsNV: return "OpRayQueryGetIntersectionLSSPositionsNV"; + case OpRayQueryGetIntersectionLSSRadiiNV: return "OpRayQueryGetIntersectionLSSRadiiNV"; + case OpRayQueryGetIntersectionLSSHitValueNV: return "OpRayQueryGetIntersectionLSSHitValueNV"; + case OpHitObjectGetSpherePositionNV: return "OpHitObjectGetSpherePositionNV"; + case OpHitObjectGetSphereRadiusNV: return "OpHitObjectGetSphereRadiusNV"; + case OpHitObjectGetLSSPositionsNV: return "OpHitObjectGetLSSPositionsNV"; + case OpHitObjectGetLSSRadiiNV: return "OpHitObjectGetLSSRadiiNV"; + case OpHitObjectIsSphereHitNV: return "OpHitObjectIsSphereHitNV"; + case OpHitObjectIsLSSHitNV: return "OpHitObjectIsLSSHitNV"; + case OpRayQueryIsSphereHitNV: return "OpRayQueryIsSphereHitNV"; + case OpRayQueryIsLSSHitNV: return "OpRayQueryIsLSSHitNV"; + case OpSubgroupShuffleINTEL: return "OpSubgroupShuffleINTEL"; + case OpSubgroupShuffleDownINTEL: return "OpSubgroupShuffleDownINTEL"; + case OpSubgroupShuffleUpINTEL: return "OpSubgroupShuffleUpINTEL"; + case OpSubgroupShuffleXorINTEL: return "OpSubgroupShuffleXorINTEL"; + case OpSubgroupBlockReadINTEL: return "OpSubgroupBlockReadINTEL"; + case OpSubgroupBlockWriteINTEL: return "OpSubgroupBlockWriteINTEL"; + case OpSubgroupImageBlockReadINTEL: return "OpSubgroupImageBlockReadINTEL"; + case OpSubgroupImageBlockWriteINTEL: return "OpSubgroupImageBlockWriteINTEL"; + case OpSubgroupImageMediaBlockReadINTEL: return "OpSubgroupImageMediaBlockReadINTEL"; + case OpSubgroupImageMediaBlockWriteINTEL: return "OpSubgroupImageMediaBlockWriteINTEL"; + case OpUCountLeadingZerosINTEL: return "OpUCountLeadingZerosINTEL"; + case OpUCountTrailingZerosINTEL: return "OpUCountTrailingZerosINTEL"; + case OpAbsISubINTEL: return "OpAbsISubINTEL"; + case OpAbsUSubINTEL: return "OpAbsUSubINTEL"; + case OpIAddSatINTEL: return "OpIAddSatINTEL"; + case OpUAddSatINTEL: return "OpUAddSatINTEL"; + case OpIAverageINTEL: return "OpIAverageINTEL"; + case OpUAverageINTEL: return "OpUAverageINTEL"; + case OpIAverageRoundedINTEL: return "OpIAverageRoundedINTEL"; + case OpUAverageRoundedINTEL: return "OpUAverageRoundedINTEL"; + case OpISubSatINTEL: return "OpISubSatINTEL"; + case OpUSubSatINTEL: return "OpUSubSatINTEL"; + case OpIMul32x16INTEL: return "OpIMul32x16INTEL"; + case OpUMul32x16INTEL: return "OpUMul32x16INTEL"; + case OpConstantFunctionPointerINTEL: return "OpConstantFunctionPointerINTEL"; + case OpFunctionPointerCallINTEL: return "OpFunctionPointerCallINTEL"; + case OpAsmTargetINTEL: return "OpAsmTargetINTEL"; + case OpAsmINTEL: return "OpAsmINTEL"; + case OpAsmCallINTEL: return "OpAsmCallINTEL"; + case OpAtomicFMinEXT: return "OpAtomicFMinEXT"; + case OpAtomicFMaxEXT: return "OpAtomicFMaxEXT"; + case OpAssumeTrueKHR: return "OpAssumeTrueKHR"; + case OpExpectKHR: return "OpExpectKHR"; + case OpDecorateString: return "OpDecorateString"; + case OpMemberDecorateString: return "OpMemberDecorateString"; + case OpVmeImageINTEL: return "OpVmeImageINTEL"; + case OpTypeVmeImageINTEL: return "OpTypeVmeImageINTEL"; + case OpTypeAvcImePayloadINTEL: return "OpTypeAvcImePayloadINTEL"; + case OpTypeAvcRefPayloadINTEL: return "OpTypeAvcRefPayloadINTEL"; + case OpTypeAvcSicPayloadINTEL: return "OpTypeAvcSicPayloadINTEL"; + case OpTypeAvcMcePayloadINTEL: return "OpTypeAvcMcePayloadINTEL"; + case OpTypeAvcMceResultINTEL: return "OpTypeAvcMceResultINTEL"; + case OpTypeAvcImeResultINTEL: return "OpTypeAvcImeResultINTEL"; + case OpTypeAvcImeResultSingleReferenceStreamoutINTEL: return "OpTypeAvcImeResultSingleReferenceStreamoutINTEL"; + case OpTypeAvcImeResultDualReferenceStreamoutINTEL: return "OpTypeAvcImeResultDualReferenceStreamoutINTEL"; + case OpTypeAvcImeSingleReferenceStreaminINTEL: return "OpTypeAvcImeSingleReferenceStreaminINTEL"; + case OpTypeAvcImeDualReferenceStreaminINTEL: return "OpTypeAvcImeDualReferenceStreaminINTEL"; + case OpTypeAvcRefResultINTEL: return "OpTypeAvcRefResultINTEL"; + case OpTypeAvcSicResultINTEL: return "OpTypeAvcSicResultINTEL"; + case OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterBaseMultiReferencePenaltyINTEL"; + case OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL: return "OpSubgroupAvcMceSetInterBaseMultiReferencePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterShapePenaltyINTEL"; + case OpSubgroupAvcMceSetInterShapePenaltyINTEL: return "OpSubgroupAvcMceSetInterShapePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultInterDirectionPenaltyINTEL"; + case OpSubgroupAvcMceSetInterDirectionPenaltyINTEL: return "OpSubgroupAvcMceSetInterDirectionPenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaShapePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL: return "OpSubgroupAvcMceGetDefaultInterMotionVectorCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultHighPenaltyCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultMediumPenaltyCostTableINTEL"; + case OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL: return "OpSubgroupAvcMceGetDefaultLowPenaltyCostTableINTEL"; + case OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL: return "OpSubgroupAvcMceSetMotionVectorCostFunctionINTEL"; + case OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraLumaModePenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL: return "OpSubgroupAvcMceGetDefaultNonDcLumaIntraPenaltyINTEL"; + case OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL: return "OpSubgroupAvcMceGetDefaultIntraChromaModeBasePenaltyINTEL"; + case OpSubgroupAvcMceSetAcOnlyHaarINTEL: return "OpSubgroupAvcMceSetAcOnlyHaarINTEL"; + case OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSourceInterlacedFieldPolarityINTEL"; + case OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL: return "OpSubgroupAvcMceSetSingleReferenceInterlacedFieldPolarityINTEL"; + case OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceSetDualReferenceInterlacedFieldPolaritiesINTEL"; + case OpSubgroupAvcMceConvertToImePayloadINTEL: return "OpSubgroupAvcMceConvertToImePayloadINTEL"; + case OpSubgroupAvcMceConvertToImeResultINTEL: return "OpSubgroupAvcMceConvertToImeResultINTEL"; + case OpSubgroupAvcMceConvertToRefPayloadINTEL: return "OpSubgroupAvcMceConvertToRefPayloadINTEL"; + case OpSubgroupAvcMceConvertToRefResultINTEL: return "OpSubgroupAvcMceConvertToRefResultINTEL"; + case OpSubgroupAvcMceConvertToSicPayloadINTEL: return "OpSubgroupAvcMceConvertToSicPayloadINTEL"; + case OpSubgroupAvcMceConvertToSicResultINTEL: return "OpSubgroupAvcMceConvertToSicResultINTEL"; + case OpSubgroupAvcMceGetMotionVectorsINTEL: return "OpSubgroupAvcMceGetMotionVectorsINTEL"; + case OpSubgroupAvcMceGetInterDistortionsINTEL: return "OpSubgroupAvcMceGetInterDistortionsINTEL"; + case OpSubgroupAvcMceGetBestInterDistortionsINTEL: return "OpSubgroupAvcMceGetBestInterDistortionsINTEL"; + case OpSubgroupAvcMceGetInterMajorShapeINTEL: return "OpSubgroupAvcMceGetInterMajorShapeINTEL"; + case OpSubgroupAvcMceGetInterMinorShapeINTEL: return "OpSubgroupAvcMceGetInterMinorShapeINTEL"; + case OpSubgroupAvcMceGetInterDirectionsINTEL: return "OpSubgroupAvcMceGetInterDirectionsINTEL"; + case OpSubgroupAvcMceGetInterMotionVectorCountINTEL: return "OpSubgroupAvcMceGetInterMotionVectorCountINTEL"; + case OpSubgroupAvcMceGetInterReferenceIdsINTEL: return "OpSubgroupAvcMceGetInterReferenceIdsINTEL"; + case OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL: return "OpSubgroupAvcMceGetInterReferenceInterlacedFieldPolaritiesINTEL"; + case OpSubgroupAvcImeInitializeINTEL: return "OpSubgroupAvcImeInitializeINTEL"; + case OpSubgroupAvcImeSetSingleReferenceINTEL: return "OpSubgroupAvcImeSetSingleReferenceINTEL"; + case OpSubgroupAvcImeSetDualReferenceINTEL: return "OpSubgroupAvcImeSetDualReferenceINTEL"; + case OpSubgroupAvcImeRefWindowSizeINTEL: return "OpSubgroupAvcImeRefWindowSizeINTEL"; + case OpSubgroupAvcImeAdjustRefOffsetINTEL: return "OpSubgroupAvcImeAdjustRefOffsetINTEL"; + case OpSubgroupAvcImeConvertToMcePayloadINTEL: return "OpSubgroupAvcImeConvertToMcePayloadINTEL"; + case OpSubgroupAvcImeSetMaxMotionVectorCountINTEL: return "OpSubgroupAvcImeSetMaxMotionVectorCountINTEL"; + case OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL: return "OpSubgroupAvcImeSetUnidirectionalMixDisableINTEL"; + case OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL: return "OpSubgroupAvcImeSetEarlySearchTerminationThresholdINTEL"; + case OpSubgroupAvcImeSetWeightedSadINTEL: return "OpSubgroupAvcImeSetWeightedSadINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreamoutINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreamoutINTEL"; + case OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithSingleReferenceStreaminoutINTEL"; + case OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL: return "OpSubgroupAvcImeEvaluateWithDualReferenceStreaminoutINTEL"; + case OpSubgroupAvcImeConvertToMceResultINTEL: return "OpSubgroupAvcImeConvertToMceResultINTEL"; + case OpSubgroupAvcImeGetSingleReferenceStreaminINTEL: return "OpSubgroupAvcImeGetSingleReferenceStreaminINTEL"; + case OpSubgroupAvcImeGetDualReferenceStreaminINTEL: return "OpSubgroupAvcImeGetDualReferenceStreaminINTEL"; + case OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripSingleReferenceStreamoutINTEL"; + case OpSubgroupAvcImeStripDualReferenceStreamoutINTEL: return "OpSubgroupAvcImeStripDualReferenceStreamoutINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeMotionVectorsINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeDistortionsINTEL"; + case OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutSingleReferenceMajorShapeReferenceIdsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeMotionVectorsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeDistortionsINTEL"; + case OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL: return "OpSubgroupAvcImeGetStreamoutDualReferenceMajorShapeReferenceIdsINTEL"; + case OpSubgroupAvcImeGetBorderReachedINTEL: return "OpSubgroupAvcImeGetBorderReachedINTEL"; + case OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL: return "OpSubgroupAvcImeGetTruncatedSearchIndicationINTEL"; + case OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL: return "OpSubgroupAvcImeGetUnidirectionalEarlySearchTerminationINTEL"; + case OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumMotionVectorINTEL"; + case OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL: return "OpSubgroupAvcImeGetWeightingPatternMinimumDistortionINTEL"; + case OpSubgroupAvcFmeInitializeINTEL: return "OpSubgroupAvcFmeInitializeINTEL"; + case OpSubgroupAvcBmeInitializeINTEL: return "OpSubgroupAvcBmeInitializeINTEL"; + case OpSubgroupAvcRefConvertToMcePayloadINTEL: return "OpSubgroupAvcRefConvertToMcePayloadINTEL"; + case OpSubgroupAvcRefSetBidirectionalMixDisableINTEL: return "OpSubgroupAvcRefSetBidirectionalMixDisableINTEL"; + case OpSubgroupAvcRefSetBilinearFilterEnableINTEL: return "OpSubgroupAvcRefSetBilinearFilterEnableINTEL"; + case OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceINTEL"; + case OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcRefEvaluateWithMultiReferenceInterlacedINTEL"; + case OpSubgroupAvcRefConvertToMceResultINTEL: return "OpSubgroupAvcRefConvertToMceResultINTEL"; + case OpSubgroupAvcSicInitializeINTEL: return "OpSubgroupAvcSicInitializeINTEL"; + case OpSubgroupAvcSicConfigureSkcINTEL: return "OpSubgroupAvcSicConfigureSkcINTEL"; + case OpSubgroupAvcSicConfigureIpeLumaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaINTEL"; + case OpSubgroupAvcSicConfigureIpeLumaChromaINTEL: return "OpSubgroupAvcSicConfigureIpeLumaChromaINTEL"; + case OpSubgroupAvcSicGetMotionVectorMaskINTEL: return "OpSubgroupAvcSicGetMotionVectorMaskINTEL"; + case OpSubgroupAvcSicConvertToMcePayloadINTEL: return "OpSubgroupAvcSicConvertToMcePayloadINTEL"; + case OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL: return "OpSubgroupAvcSicSetIntraLumaShapePenaltyINTEL"; + case OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraLumaModeCostFunctionINTEL"; + case OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL: return "OpSubgroupAvcSicSetIntraChromaModeCostFunctionINTEL"; + case OpSubgroupAvcSicSetBilinearFilterEnableINTEL: return "OpSubgroupAvcSicSetBilinearFilterEnableINTEL"; + case OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL: return "OpSubgroupAvcSicSetSkcForwardTransformEnableINTEL"; + case OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL: return "OpSubgroupAvcSicSetBlockBasedRawSkipSadINTEL"; + case OpSubgroupAvcSicEvaluateIpeINTEL: return "OpSubgroupAvcSicEvaluateIpeINTEL"; + case OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithSingleReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithDualReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithDualReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceINTEL"; + case OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL: return "OpSubgroupAvcSicEvaluateWithMultiReferenceInterlacedINTEL"; + case OpSubgroupAvcSicConvertToMceResultINTEL: return "OpSubgroupAvcSicConvertToMceResultINTEL"; + case OpSubgroupAvcSicGetIpeLumaShapeINTEL: return "OpSubgroupAvcSicGetIpeLumaShapeINTEL"; + case OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeLumaDistortionINTEL"; + case OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL: return "OpSubgroupAvcSicGetBestIpeChromaDistortionINTEL"; + case OpSubgroupAvcSicGetPackedIpeLumaModesINTEL: return "OpSubgroupAvcSicGetPackedIpeLumaModesINTEL"; + case OpSubgroupAvcSicGetIpeChromaModeINTEL: return "OpSubgroupAvcSicGetIpeChromaModeINTEL"; + case OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaCountThresholdINTEL"; + case OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL: return "OpSubgroupAvcSicGetPackedSkcLumaSumThresholdINTEL"; + case OpSubgroupAvcSicGetInterRawSadsINTEL: return "OpSubgroupAvcSicGetInterRawSadsINTEL"; + case OpVariableLengthArrayINTEL: return "OpVariableLengthArrayINTEL"; + case OpSaveMemoryINTEL: return "OpSaveMemoryINTEL"; + case OpRestoreMemoryINTEL: return "OpRestoreMemoryINTEL"; + case OpArbitraryFloatSinCosPiALTERA: return "OpArbitraryFloatSinCosPiALTERA"; + case OpArbitraryFloatCastALTERA: return "OpArbitraryFloatCastALTERA"; + case OpArbitraryFloatCastFromIntALTERA: return "OpArbitraryFloatCastFromIntALTERA"; + case OpArbitraryFloatCastToIntALTERA: return "OpArbitraryFloatCastToIntALTERA"; + case OpArbitraryFloatAddALTERA: return "OpArbitraryFloatAddALTERA"; + case OpArbitraryFloatSubALTERA: return "OpArbitraryFloatSubALTERA"; + case OpArbitraryFloatMulALTERA: return "OpArbitraryFloatMulALTERA"; + case OpArbitraryFloatDivALTERA: return "OpArbitraryFloatDivALTERA"; + case OpArbitraryFloatGTALTERA: return "OpArbitraryFloatGTALTERA"; + case OpArbitraryFloatGEALTERA: return "OpArbitraryFloatGEALTERA"; + case OpArbitraryFloatLTALTERA: return "OpArbitraryFloatLTALTERA"; + case OpArbitraryFloatLEALTERA: return "OpArbitraryFloatLEALTERA"; + case OpArbitraryFloatEQALTERA: return "OpArbitraryFloatEQALTERA"; + case OpArbitraryFloatRecipALTERA: return "OpArbitraryFloatRecipALTERA"; + case OpArbitraryFloatRSqrtALTERA: return "OpArbitraryFloatRSqrtALTERA"; + case OpArbitraryFloatCbrtALTERA: return "OpArbitraryFloatCbrtALTERA"; + case OpArbitraryFloatHypotALTERA: return "OpArbitraryFloatHypotALTERA"; + case OpArbitraryFloatSqrtALTERA: return "OpArbitraryFloatSqrtALTERA"; + case OpArbitraryFloatLogINTEL: return "OpArbitraryFloatLogINTEL"; + case OpArbitraryFloatLog2INTEL: return "OpArbitraryFloatLog2INTEL"; + case OpArbitraryFloatLog10INTEL: return "OpArbitraryFloatLog10INTEL"; + case OpArbitraryFloatLog1pINTEL: return "OpArbitraryFloatLog1pINTEL"; + case OpArbitraryFloatExpINTEL: return "OpArbitraryFloatExpINTEL"; + case OpArbitraryFloatExp2INTEL: return "OpArbitraryFloatExp2INTEL"; + case OpArbitraryFloatExp10INTEL: return "OpArbitraryFloatExp10INTEL"; + case OpArbitraryFloatExpm1INTEL: return "OpArbitraryFloatExpm1INTEL"; + case OpArbitraryFloatSinINTEL: return "OpArbitraryFloatSinINTEL"; + case OpArbitraryFloatCosINTEL: return "OpArbitraryFloatCosINTEL"; + case OpArbitraryFloatSinCosINTEL: return "OpArbitraryFloatSinCosINTEL"; + case OpArbitraryFloatSinPiINTEL: return "OpArbitraryFloatSinPiINTEL"; + case OpArbitraryFloatCosPiINTEL: return "OpArbitraryFloatCosPiINTEL"; + case OpArbitraryFloatASinINTEL: return "OpArbitraryFloatASinINTEL"; + case OpArbitraryFloatASinPiINTEL: return "OpArbitraryFloatASinPiINTEL"; + case OpArbitraryFloatACosINTEL: return "OpArbitraryFloatACosINTEL"; + case OpArbitraryFloatACosPiINTEL: return "OpArbitraryFloatACosPiINTEL"; + case OpArbitraryFloatATanINTEL: return "OpArbitraryFloatATanINTEL"; + case OpArbitraryFloatATanPiINTEL: return "OpArbitraryFloatATanPiINTEL"; + case OpArbitraryFloatATan2INTEL: return "OpArbitraryFloatATan2INTEL"; + case OpArbitraryFloatPowINTEL: return "OpArbitraryFloatPowINTEL"; + case OpArbitraryFloatPowRINTEL: return "OpArbitraryFloatPowRINTEL"; + case OpArbitraryFloatPowNINTEL: return "OpArbitraryFloatPowNINTEL"; + case OpLoopControlINTEL: return "OpLoopControlINTEL"; + case OpAliasDomainDeclINTEL: return "OpAliasDomainDeclINTEL"; + case OpAliasScopeDeclINTEL: return "OpAliasScopeDeclINTEL"; + case OpAliasScopeListDeclINTEL: return "OpAliasScopeListDeclINTEL"; + case OpFixedSqrtALTERA: return "OpFixedSqrtALTERA"; + case OpFixedRecipALTERA: return "OpFixedRecipALTERA"; + case OpFixedRsqrtALTERA: return "OpFixedRsqrtALTERA"; + case OpFixedSinALTERA: return "OpFixedSinALTERA"; + case OpFixedCosALTERA: return "OpFixedCosALTERA"; + case OpFixedSinCosALTERA: return "OpFixedSinCosALTERA"; + case OpFixedSinPiALTERA: return "OpFixedSinPiALTERA"; + case OpFixedCosPiALTERA: return "OpFixedCosPiALTERA"; + case OpFixedSinCosPiALTERA: return "OpFixedSinCosPiALTERA"; + case OpFixedLogALTERA: return "OpFixedLogALTERA"; + case OpFixedExpALTERA: return "OpFixedExpALTERA"; + case OpPtrCastToCrossWorkgroupALTERA: return "OpPtrCastToCrossWorkgroupALTERA"; + case OpCrossWorkgroupCastToPtrALTERA: return "OpCrossWorkgroupCastToPtrALTERA"; + case OpReadPipeBlockingALTERA: return "OpReadPipeBlockingALTERA"; + case OpWritePipeBlockingALTERA: return "OpWritePipeBlockingALTERA"; + case OpFPGARegALTERA: return "OpFPGARegALTERA"; + case OpRayQueryGetRayTMinKHR: return "OpRayQueryGetRayTMinKHR"; + case OpRayQueryGetRayFlagsKHR: return "OpRayQueryGetRayFlagsKHR"; + case OpRayQueryGetIntersectionTKHR: return "OpRayQueryGetIntersectionTKHR"; + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: return "OpRayQueryGetIntersectionInstanceCustomIndexKHR"; + case OpRayQueryGetIntersectionInstanceIdKHR: return "OpRayQueryGetIntersectionInstanceIdKHR"; + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: return "OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR"; + case OpRayQueryGetIntersectionGeometryIndexKHR: return "OpRayQueryGetIntersectionGeometryIndexKHR"; + case OpRayQueryGetIntersectionPrimitiveIndexKHR: return "OpRayQueryGetIntersectionPrimitiveIndexKHR"; + case OpRayQueryGetIntersectionBarycentricsKHR: return "OpRayQueryGetIntersectionBarycentricsKHR"; + case OpRayQueryGetIntersectionFrontFaceKHR: return "OpRayQueryGetIntersectionFrontFaceKHR"; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: return "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR"; + case OpRayQueryGetIntersectionObjectRayDirectionKHR: return "OpRayQueryGetIntersectionObjectRayDirectionKHR"; + case OpRayQueryGetIntersectionObjectRayOriginKHR: return "OpRayQueryGetIntersectionObjectRayOriginKHR"; + case OpRayQueryGetWorldRayDirectionKHR: return "OpRayQueryGetWorldRayDirectionKHR"; + case OpRayQueryGetWorldRayOriginKHR: return "OpRayQueryGetWorldRayOriginKHR"; + case OpRayQueryGetIntersectionObjectToWorldKHR: return "OpRayQueryGetIntersectionObjectToWorldKHR"; + case OpRayQueryGetIntersectionWorldToObjectKHR: return "OpRayQueryGetIntersectionWorldToObjectKHR"; + case OpAtomicFAddEXT: return "OpAtomicFAddEXT"; + case OpTypeBufferSurfaceINTEL: return "OpTypeBufferSurfaceINTEL"; + case OpTypeStructContinuedINTEL: return "OpTypeStructContinuedINTEL"; + case OpConstantCompositeContinuedINTEL: return "OpConstantCompositeContinuedINTEL"; + case OpSpecConstantCompositeContinuedINTEL: return "OpSpecConstantCompositeContinuedINTEL"; + case OpCompositeConstructContinuedINTEL: return "OpCompositeConstructContinuedINTEL"; + case OpConvertFToBF16INTEL: return "OpConvertFToBF16INTEL"; + case OpConvertBF16ToFINTEL: return "OpConvertBF16ToFINTEL"; + case OpControlBarrierArriveINTEL: return "OpControlBarrierArriveINTEL"; + case OpControlBarrierWaitINTEL: return "OpControlBarrierWaitINTEL"; + case OpArithmeticFenceEXT: return "OpArithmeticFenceEXT"; + case OpTaskSequenceCreateALTERA: return "OpTaskSequenceCreateALTERA"; + case OpTaskSequenceAsyncALTERA: return "OpTaskSequenceAsyncALTERA"; + case OpTaskSequenceGetALTERA: return "OpTaskSequenceGetALTERA"; + case OpTaskSequenceReleaseALTERA: return "OpTaskSequenceReleaseALTERA"; + case OpTypeTaskSequenceALTERA: return "OpTypeTaskSequenceALTERA"; + case OpSubgroupBlockPrefetchINTEL: return "OpSubgroupBlockPrefetchINTEL"; + case OpSubgroup2DBlockLoadINTEL: return "OpSubgroup2DBlockLoadINTEL"; + case OpSubgroup2DBlockLoadTransformINTEL: return "OpSubgroup2DBlockLoadTransformINTEL"; + case OpSubgroup2DBlockLoadTransposeINTEL: return "OpSubgroup2DBlockLoadTransposeINTEL"; + case OpSubgroup2DBlockPrefetchINTEL: return "OpSubgroup2DBlockPrefetchINTEL"; + case OpSubgroup2DBlockStoreINTEL: return "OpSubgroup2DBlockStoreINTEL"; + case OpSubgroupMatrixMultiplyAccumulateINTEL: return "OpSubgroupMatrixMultiplyAccumulateINTEL"; + case OpBitwiseFunctionINTEL: return "OpBitwiseFunctionINTEL"; + case OpUntypedVariableLengthArrayINTEL: return "OpUntypedVariableLengthArrayINTEL"; + case OpConditionalExtensionINTEL: return "OpConditionalExtensionINTEL"; + case OpConditionalEntryPointINTEL: return "OpConditionalEntryPointINTEL"; + case OpConditionalCapabilityINTEL: return "OpConditionalCapabilityINTEL"; + case OpSpecConstantTargetINTEL: return "OpSpecConstantTargetINTEL"; + case OpSpecConstantArchitectureINTEL: return "OpSpecConstantArchitectureINTEL"; + case OpSpecConstantCapabilitiesINTEL: return "OpSpecConstantCapabilitiesINTEL"; + case OpConditionalCopyObjectINTEL: return "OpConditionalCopyObjectINTEL"; + case OpGroupIMulKHR: return "OpGroupIMulKHR"; + case OpGroupFMulKHR: return "OpGroupFMulKHR"; + case OpGroupBitwiseAndKHR: return "OpGroupBitwiseAndKHR"; + case OpGroupBitwiseOrKHR: return "OpGroupBitwiseOrKHR"; + case OpGroupBitwiseXorKHR: return "OpGroupBitwiseXorKHR"; + case OpGroupLogicalAndKHR: return "OpGroupLogicalAndKHR"; + case OpGroupLogicalOrKHR: return "OpGroupLogicalOrKHR"; + case OpGroupLogicalXorKHR: return "OpGroupLogicalXorKHR"; + case OpRoundFToTF32INTEL: return "OpRoundFToTF32INTEL"; + case OpMaskedGatherINTEL: return "OpMaskedGatherINTEL"; + case OpMaskedScatterINTEL: return "OpMaskedScatterINTEL"; + case OpConvertHandleToImageINTEL: return "OpConvertHandleToImageINTEL"; + case OpConvertHandleToSamplerINTEL: return "OpConvertHandleToSamplerINTEL"; + case OpConvertHandleToSampledImageINTEL: return "OpConvertHandleToSampledImageINTEL"; + case OpFDot2MixAcc32VALVE: return "OpFDot2MixAcc32VALVE"; + case OpFDot2MixAcc16VALVE: return "OpFDot2MixAcc16VALVE"; + case OpFDot4MixAcc32VALVE: return "OpFDot4MixAcc32VALVE"; + default: return "Unknown"; + } +} + +#endif /* SPV_ENABLE_UTILITY_CODE */ + +// Overload bitwise operators for mask bit combining + +inline ImageOperandsMask operator|(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) | unsigned(b)); } +inline ImageOperandsMask operator&(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) & unsigned(b)); } +inline ImageOperandsMask operator^(ImageOperandsMask a, ImageOperandsMask b) { return ImageOperandsMask(unsigned(a) ^ unsigned(b)); } +inline ImageOperandsMask operator~(ImageOperandsMask a) { return ImageOperandsMask(~unsigned(a)); } +inline FPFastMathModeMask operator|(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) | unsigned(b)); } +inline FPFastMathModeMask operator&(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) & unsigned(b)); } +inline FPFastMathModeMask operator^(FPFastMathModeMask a, FPFastMathModeMask b) { return FPFastMathModeMask(unsigned(a) ^ unsigned(b)); } +inline FPFastMathModeMask operator~(FPFastMathModeMask a) { return FPFastMathModeMask(~unsigned(a)); } +inline SelectionControlMask operator|(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) | unsigned(b)); } +inline SelectionControlMask operator&(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) & unsigned(b)); } +inline SelectionControlMask operator^(SelectionControlMask a, SelectionControlMask b) { return SelectionControlMask(unsigned(a) ^ unsigned(b)); } +inline SelectionControlMask operator~(SelectionControlMask a) { return SelectionControlMask(~unsigned(a)); } +inline LoopControlMask operator|(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) | unsigned(b)); } +inline LoopControlMask operator&(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) & unsigned(b)); } +inline LoopControlMask operator^(LoopControlMask a, LoopControlMask b) { return LoopControlMask(unsigned(a) ^ unsigned(b)); } +inline LoopControlMask operator~(LoopControlMask a) { return LoopControlMask(~unsigned(a)); } +inline FunctionControlMask operator|(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) | unsigned(b)); } +inline FunctionControlMask operator&(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) & unsigned(b)); } +inline FunctionControlMask operator^(FunctionControlMask a, FunctionControlMask b) { return FunctionControlMask(unsigned(a) ^ unsigned(b)); } +inline FunctionControlMask operator~(FunctionControlMask a) { return FunctionControlMask(~unsigned(a)); } +inline MemorySemanticsMask operator|(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) | unsigned(b)); } +inline MemorySemanticsMask operator&(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) & unsigned(b)); } +inline MemorySemanticsMask operator^(MemorySemanticsMask a, MemorySemanticsMask b) { return MemorySemanticsMask(unsigned(a) ^ unsigned(b)); } +inline MemorySemanticsMask operator~(MemorySemanticsMask a) { return MemorySemanticsMask(~unsigned(a)); } +inline MemoryAccessMask operator|(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) | unsigned(b)); } +inline MemoryAccessMask operator&(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) & unsigned(b)); } +inline MemoryAccessMask operator^(MemoryAccessMask a, MemoryAccessMask b) { return MemoryAccessMask(unsigned(a) ^ unsigned(b)); } +inline MemoryAccessMask operator~(MemoryAccessMask a) { return MemoryAccessMask(~unsigned(a)); } +inline KernelProfilingInfoMask operator|(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) | unsigned(b)); } +inline KernelProfilingInfoMask operator&(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) & unsigned(b)); } +inline KernelProfilingInfoMask operator^(KernelProfilingInfoMask a, KernelProfilingInfoMask b) { return KernelProfilingInfoMask(unsigned(a) ^ unsigned(b)); } +inline KernelProfilingInfoMask operator~(KernelProfilingInfoMask a) { return KernelProfilingInfoMask(~unsigned(a)); } +inline RayFlagsMask operator|(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) | unsigned(b)); } +inline RayFlagsMask operator&(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) & unsigned(b)); } +inline RayFlagsMask operator^(RayFlagsMask a, RayFlagsMask b) { return RayFlagsMask(unsigned(a) ^ unsigned(b)); } +inline RayFlagsMask operator~(RayFlagsMask a) { return RayFlagsMask(~unsigned(a)); } +inline FragmentShadingRateMask operator|(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) | unsigned(b)); } +inline FragmentShadingRateMask operator&(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) & unsigned(b)); } +inline FragmentShadingRateMask operator^(FragmentShadingRateMask a, FragmentShadingRateMask b) { return FragmentShadingRateMask(unsigned(a) ^ unsigned(b)); } +inline FragmentShadingRateMask operator~(FragmentShadingRateMask a) { return FragmentShadingRateMask(~unsigned(a)); } +inline CooperativeMatrixOperandsMask operator|(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) | unsigned(b)); } +inline CooperativeMatrixOperandsMask operator&(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) & unsigned(b)); } +inline CooperativeMatrixOperandsMask operator^(CooperativeMatrixOperandsMask a, CooperativeMatrixOperandsMask b) { return CooperativeMatrixOperandsMask(unsigned(a) ^ unsigned(b)); } +inline CooperativeMatrixOperandsMask operator~(CooperativeMatrixOperandsMask a) { return CooperativeMatrixOperandsMask(~unsigned(a)); } +inline CooperativeMatrixReduceMask operator|(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) | unsigned(b)); } +inline CooperativeMatrixReduceMask operator&(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) & unsigned(b)); } +inline CooperativeMatrixReduceMask operator^(CooperativeMatrixReduceMask a, CooperativeMatrixReduceMask b) { return CooperativeMatrixReduceMask(unsigned(a) ^ unsigned(b)); } +inline CooperativeMatrixReduceMask operator~(CooperativeMatrixReduceMask a) { return CooperativeMatrixReduceMask(~unsigned(a)); } +inline TensorAddressingOperandsMask operator|(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) | unsigned(b)); } +inline TensorAddressingOperandsMask operator&(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) & unsigned(b)); } +inline TensorAddressingOperandsMask operator^(TensorAddressingOperandsMask a, TensorAddressingOperandsMask b) { return TensorAddressingOperandsMask(unsigned(a) ^ unsigned(b)); } +inline TensorAddressingOperandsMask operator~(TensorAddressingOperandsMask a) { return TensorAddressingOperandsMask(~unsigned(a)); } +inline TensorOperandsMask operator|(TensorOperandsMask a, TensorOperandsMask b) { return TensorOperandsMask(unsigned(a) | unsigned(b)); } +inline TensorOperandsMask operator&(TensorOperandsMask a, TensorOperandsMask b) { return TensorOperandsMask(unsigned(a) & unsigned(b)); } +inline TensorOperandsMask operator^(TensorOperandsMask a, TensorOperandsMask b) { return TensorOperandsMask(unsigned(a) ^ unsigned(b)); } +inline TensorOperandsMask operator~(TensorOperandsMask a) { return TensorOperandsMask(~unsigned(a)); } +inline MatrixMultiplyAccumulateOperandsMask operator|(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) | unsigned(b)); } +inline MatrixMultiplyAccumulateOperandsMask operator&(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) & unsigned(b)); } +inline MatrixMultiplyAccumulateOperandsMask operator^(MatrixMultiplyAccumulateOperandsMask a, MatrixMultiplyAccumulateOperandsMask b) { return MatrixMultiplyAccumulateOperandsMask(unsigned(a) ^ unsigned(b)); } +inline MatrixMultiplyAccumulateOperandsMask operator~(MatrixMultiplyAccumulateOperandsMask a) { return MatrixMultiplyAccumulateOperandsMask(~unsigned(a)); } +inline RawAccessChainOperandsMask operator|(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) | unsigned(b)); } +inline RawAccessChainOperandsMask operator&(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) & unsigned(b)); } +inline RawAccessChainOperandsMask operator^(RawAccessChainOperandsMask a, RawAccessChainOperandsMask b) { return RawAccessChainOperandsMask(unsigned(a) ^ unsigned(b)); } +inline RawAccessChainOperandsMask operator~(RawAccessChainOperandsMask a) { return RawAccessChainOperandsMask(~unsigned(a)); } + +} // end namespace spv + +#endif // #ifndef spirv_HPP + diff --git a/thirdparty/SPIRV-Cross/spirv_cfg.cpp b/thirdparty/SPIRV-Cross/spirv_cfg.cpp new file mode 100644 index 000000000..ae928a03a --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cfg.cpp @@ -0,0 +1,525 @@ +/* + * Copyright 2016-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_cfg.hpp" +#include "spirv_cross.hpp" +#include +#include + +using namespace std; + +namespace SPIRV_CROSS_NAMESPACE +{ +CFG::CFG(Compiler &compiler_, const SPIRFunction &func_) + : compiler(compiler_) + , func(func_) +{ + build_post_order_visit_order(); + build_immediate_dominators(); +} + +uint32_t CFG::find_common_dominator(uint32_t a, uint32_t b) const +{ + while (a != b) + { + if (get_visit_order(a) < get_visit_order(b)) + a = get_immediate_dominator(a); + else + b = get_immediate_dominator(b); + } + return a; +} + +void CFG::build_immediate_dominators() +{ + // Traverse the post-order in reverse and build up the immediate dominator tree. + immediate_dominators.clear(); + immediate_dominators[func.entry_block] = func.entry_block; + + for (auto i = post_order.size(); i; i--) + { + uint32_t block = post_order[i - 1]; + + const auto resolve_preds = [&](const SmallVector &pred) + { + if (pred.empty()) // This is for the entry block, but we've already set up the dominators. + return; + + for (auto &edge : pred) + { + if (immediate_dominators[block]) + { + assert(immediate_dominators[edge]); + immediate_dominators[block] = find_common_dominator(immediate_dominators[block], edge); + } + else + immediate_dominators[block] = edge; + } + }; + + resolve_preds(preceding_edges[block]); + resolve_preds(virtual_dominance_preceding_edges[block]); + } +} + +bool CFG::is_back_edge(uint32_t to) const +{ + // We have a back edge if the visit order is set with the temporary magic value 0. + // Crossing edges will have already been recorded with a visit order. + auto itr = visit_order.find(to); + return itr != end(visit_order) && itr->second.visited_branches && !itr->second.visited_resolve; +} + +bool CFG::has_visited_branch(uint32_t to) const +{ + auto itr = visit_order.find(to); + return itr != end(visit_order) && itr->second.visited_branches; +} + +void CFG::post_order_visit_entry(uint32_t block) +{ + visit_stack.push_back(block); + + while (!visit_stack.empty()) + { + bool keep_iterating; + do + { + // Reverse the order to allow for stack-like behavior and preserves the visit order from recursive algorithm. + // Traverse depth first. + uint32_t to_visit = visit_stack.back(); + last_visited_size = visit_stack.size(); + post_order_visit_branches(to_visit); + keep_iterating = last_visited_size != visit_stack.size(); + if (keep_iterating) + std::reverse(visit_stack.begin() + last_visited_size, visit_stack.end()); + } while (keep_iterating); + + // We've reached the end of some tree leaf. Resolve the stack. + // Any node which has been visited for real can be popped now. + while (!visit_stack.empty() && visit_order[visit_stack.back()].visited_branches) + { + post_order_visit_resolve(visit_stack.back()); + visit_stack.pop_back(); + } + } +} + +void CFG::visit_branch(uint32_t block_id) +{ + // Prune obvious duplicates. + if (std::find(visit_stack.begin() + last_visited_size, visit_stack.end(), block_id) == visit_stack.end() && + !has_visited_branch(block_id)) + { + visit_stack.push_back(block_id); + } +} + +void CFG::post_order_visit_branches(uint32_t block_id) +{ + auto &block = compiler.get(block_id); + + auto &visit = visit_order[block_id]; + if (visit.visited_branches) + return; + visit.visited_branches = true; + + if (block.merge == SPIRBlock::MergeLoop) + visit_branch(block.merge_block); + else if (block.merge == SPIRBlock::MergeSelection) + visit_branch(block.next_block); + + // First visit our branch targets. + switch (block.terminator) + { + case SPIRBlock::Direct: + visit_branch(block.next_block); + break; + + case SPIRBlock::Select: + visit_branch(block.true_block); + visit_branch(block.false_block); + break; + + case SPIRBlock::MultiSelect: + { + const auto &cases = compiler.get_case_list(block); + for (const auto &target : cases) + visit_branch(target.block); + if (block.default_block) + visit_branch(block.default_block); + break; + } + + default: + break; + } +} + +void CFG::post_order_visit_resolve(uint32_t block_id) +{ + auto &block = compiler.get(block_id); + + auto &visit_block = visit_order[block_id]; + assert(visit_block.visited_branches); + auto &visited = visit_order[block_id].visited_resolve; + if (visited) + return; + + // If this is a loop header, add an implied branch to the merge target. + // This is needed to avoid annoying cases with do { ... } while(false) loops often generated by inliners. + // To the CFG, this is linear control flow, but we risk picking the do/while scope as our dominating block. + // This makes sure that if we are accessing a variable outside the do/while, we choose the loop header as dominator. + // We could use has_visited_forward_edge, but this break code-gen where the merge block is unreachable in the CFG. + + // Make a point out of visiting merge target first. This is to make sure that post visit order outside the loop + // is lower than inside the loop, which is going to be key for some traversal algorithms like post-dominance analysis. + // For selection constructs true/false blocks will end up visiting the merge block directly and it works out fine, + // but for loops, only the header might end up actually branching to merge block. + if (block.merge == SPIRBlock::MergeLoop && !is_back_edge(block.merge_block)) + add_branch(block_id, block.merge_block); + + // Similar case as do/while loops, but expressed in a different form. + // if (true) { foo = 1; } else { return/unreachable/kill/blah; } access(foo); + // Only consider this branch when computing dominance to avoid breaking other analysis like + // parameter preservation. + if (block.merge == SPIRBlock::MergeSelection && !is_back_edge(block.next_block)) + add_virtual_dominance_branch(block_id, block.next_block); + + // First visit our branch targets. + switch (block.terminator) + { + case SPIRBlock::Direct: + if (!is_back_edge(block.next_block)) + add_branch(block_id, block.next_block); + break; + + case SPIRBlock::Select: + if (!is_back_edge(block.true_block)) + add_branch(block_id, block.true_block); + if (!is_back_edge(block.false_block)) + add_branch(block_id, block.false_block); + break; + + case SPIRBlock::MultiSelect: + { + const auto &cases = compiler.get_case_list(block); + for (const auto &target : cases) + { + if (!is_back_edge(target.block)) + add_branch(block_id, target.block); + } + if (block.default_block && !is_back_edge(block.default_block)) + add_branch(block_id, block.default_block); + break; + } + default: + break; + } + + // If this is a selection merge, add an implied branch to the merge target. + // This is needed to avoid cases where an inner branch dominates the outer branch. + // This can happen if one of the branches exit early, e.g.: + // if (cond) { ...; break; } else { var = 100 } use_var(var); + // We can use the variable without a Phi since there is only one possible parent here. + // However, in this case, we need to hoist out the inner variable to outside the branch. + // Use same strategy as loops. + if (block.merge == SPIRBlock::MergeSelection && !is_back_edge(block.next_block)) + { + // If there is only one preceding edge to the merge block and it's not ourselves, we need a fixup. + // Add a fake branch so any dominator in either the if (), or else () block, or a lone case statement + // will be hoisted out to outside the selection merge. + // If size > 1, the variable will be automatically hoisted, so we should not mess with it. + // The exception here is switch blocks, where we can have multiple edges to merge block, + // all coming from same scope, so be more conservative in this case. + // Adding fake branches unconditionally breaks parameter preservation analysis, + // which looks at how variables are accessed through the CFG. + auto pred_itr = preceding_edges.find(block.next_block); + if (pred_itr != end(preceding_edges)) + { + auto &pred = pred_itr->second; + auto succ_itr = succeeding_edges.find(block_id); + size_t num_succeeding_edges = 0; + if (succ_itr != end(succeeding_edges)) + num_succeeding_edges = succ_itr->second.size(); + + if (block.terminator == SPIRBlock::MultiSelect && num_succeeding_edges == 1) + { + // Multiple branches can come from the same scope due to "break;", so we need to assume that all branches + // come from same case scope in worst case, even if there are multiple preceding edges. + // If we have more than one succeeding edge from the block header, it should be impossible + // to have a dominator be inside the block. + // Only case this can go wrong is if we have 2 or more edges from block header and + // 2 or more edges to merge block, and still have dominator be inside a case label. + if (!pred.empty()) + add_branch(block_id, block.next_block); + } + else + { + if (pred.size() == 1 && *pred.begin() != block_id) + add_branch(block_id, block.next_block); + } + } + else + { + // If the merge block does not have any preceding edges, i.e. unreachable, hallucinate it. + // We're going to do code-gen for it, and domination analysis requires that we have at least one preceding edge. + add_branch(block_id, block.next_block); + } + } + + visited = true; + visit_block.order = ++visit_count; + post_order.push_back(block_id); +} + +void CFG::build_post_order_visit_order() +{ + uint32_t block = func.entry_block; + visit_count = 0; + visit_order.clear(); + post_order.clear(); + post_order_visit_entry(block); +} + +static void add_unique(SmallVector &l, uint32_t value) +{ + auto itr = find(begin(l), end(l), value); + if (itr == end(l)) + l.push_back(value); +} + +void CFG::add_branch(uint32_t from, uint32_t to) +{ + assert(from && to); + add_unique(preceding_edges[to], from); + add_unique(succeeding_edges[from], to); +} + +void CFG::add_virtual_dominance_branch(uint32_t from, uint32_t to) +{ + assert(from && to); + add_unique(virtual_dominance_preceding_edges[to], from); +} + +uint32_t CFG::find_loop_dominator(uint32_t block_id) const +{ + while (block_id != SPIRBlock::NoDominator) + { + auto itr = preceding_edges.find(block_id); + if (itr == end(preceding_edges)) + return SPIRBlock::NoDominator; + if (itr->second.empty()) + return SPIRBlock::NoDominator; + + uint32_t pred_block_id = SPIRBlock::NoDominator; + bool ignore_loop_header = false; + + // If we are a merge block, go directly to the header block. + // Only consider a loop dominator if we are branching from inside a block to a loop header. + // NOTE: In the CFG we forced an edge from header to merge block always to support variable scopes properly. + for (auto &pred : itr->second) + { + auto &pred_block = compiler.get(pred); + if (pred_block.merge == SPIRBlock::MergeLoop && pred_block.merge_block == ID(block_id)) + { + pred_block_id = pred; + ignore_loop_header = true; + break; + } + else if (pred_block.merge == SPIRBlock::MergeSelection && pred_block.next_block == ID(block_id)) + { + pred_block_id = pred; + break; + } + } + + // No merge block means we can just pick any edge. Loop headers dominate the inner loop, so any path we + // take will lead there. + if (pred_block_id == SPIRBlock::NoDominator) + pred_block_id = itr->second.front(); + + block_id = pred_block_id; + + if (!ignore_loop_header && block_id) + { + auto &block = compiler.get(block_id); + if (block.merge == SPIRBlock::MergeLoop) + return block_id; + } + } + + return block_id; +} + +bool CFG::node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const +{ + // Walk backwards, starting from "to" block. + // Only follow pred edges if they have a 1:1 relationship, or a merge relationship. + // If we cannot find a path to "from", we must assume that to is inside control flow in some way. + + auto &from_block = compiler.get(from); + BlockID ignore_block_id = 0; + if (from_block.merge == SPIRBlock::MergeLoop) + ignore_block_id = from_block.merge_block; + + while (to != from) + { + auto pred_itr = preceding_edges.find(to); + if (pred_itr == end(preceding_edges)) + return false; + + DominatorBuilder builder(*this); + for (auto &edge : pred_itr->second) + builder.add_block(edge); + + uint32_t dominator = builder.get_dominator(); + if (dominator == 0) + return false; + + auto &dom = compiler.get(dominator); + + bool true_path_ignore = false; + bool false_path_ignore = false; + + bool merges_to_nothing = dom.merge == SPIRBlock::MergeNone || + (dom.merge == SPIRBlock::MergeSelection && dom.next_block && + compiler.get(dom.next_block).terminator == SPIRBlock::Unreachable) || + (dom.merge == SPIRBlock::MergeLoop && dom.merge_block && + compiler.get(dom.merge_block).terminator == SPIRBlock::Unreachable); + + if (dom.self == from || merges_to_nothing) + { + // We can only ignore inner branchy paths if there is no merge, + // i.e. no code is generated afterwards. E.g. this allows us to elide continue: + // for (;;) { if (cond) { continue; } else { break; } }. + // Codegen here in SPIR-V will be something like either no merge if one path directly breaks, or + // we merge to Unreachable. + if (ignore_block_id && dom.terminator == SPIRBlock::Select) + { + auto &true_block = compiler.get(dom.true_block); + auto &false_block = compiler.get(dom.false_block); + auto &ignore_block = compiler.get(ignore_block_id); + true_path_ignore = compiler.execution_is_branchless(true_block, ignore_block); + false_path_ignore = compiler.execution_is_branchless(false_block, ignore_block); + } + } + + // Cases where we allow traversal. This serves as a proxy for post-dominance in a loop body. + // TODO: Might want to do full post-dominance analysis, but it's a lot of churn for something like this ... + // - We're the merge block of a selection construct. Jump to header. + // - We're the merge block of a loop. Jump to header. + // - Direct branch. Trivial. + // - Allow cases inside a branch if the header cannot merge execution before loop exit. + if ((dom.merge == SPIRBlock::MergeSelection && dom.next_block == to) || + (dom.merge == SPIRBlock::MergeLoop && dom.merge_block == to) || + (dom.terminator == SPIRBlock::Direct && dom.next_block == to) || + (dom.terminator == SPIRBlock::Select && dom.true_block == to && false_path_ignore) || + (dom.terminator == SPIRBlock::Select && dom.false_block == to && true_path_ignore)) + { + // Allow walking selection constructs if the other branch reaches out of a loop construct. + // It cannot be in-scope anymore. + to = dominator; + } + else + return false; + } + + return true; +} + +DominatorBuilder::DominatorBuilder(const CFG &cfg_) + : cfg(cfg_) +{ +} + +void DominatorBuilder::add_block(uint32_t block) +{ + if (!cfg.get_immediate_dominator(block)) + { + // Unreachable block via the CFG, we will never emit this code anyways. + return; + } + + if (!dominator) + { + dominator = block; + return; + } + + if (block != dominator) + dominator = cfg.find_common_dominator(block, dominator); +} + +void DominatorBuilder::lift_continue_block_dominator() +{ + // It is possible for a continue block to be the dominator of a variable is only accessed inside the while block of a do-while loop. + // We cannot safely declare variables inside a continue block, so move any variable declared + // in a continue block to the entry block to simplify. + // It makes very little sense for a continue block to ever be a dominator, so fall back to the simplest + // solution. + + if (!dominator) + return; + + auto &block = cfg.get_compiler().get(dominator); + auto post_order = cfg.get_visit_order(dominator); + + // If we are branching to a block with a higher post-order traversal index (continue blocks), we have a problem + // since we cannot create sensible GLSL code for this, fallback to entry block. + bool back_edge_dominator = false; + switch (block.terminator) + { + case SPIRBlock::Direct: + if (cfg.get_visit_order(block.next_block) > post_order) + back_edge_dominator = true; + break; + + case SPIRBlock::Select: + if (cfg.get_visit_order(block.true_block) > post_order) + back_edge_dominator = true; + if (cfg.get_visit_order(block.false_block) > post_order) + back_edge_dominator = true; + break; + + case SPIRBlock::MultiSelect: + { + auto &cases = cfg.get_compiler().get_case_list(block); + for (auto &target : cases) + { + if (cfg.get_visit_order(target.block) > post_order) + back_edge_dominator = true; + } + if (block.default_block && cfg.get_visit_order(block.default_block) > post_order) + back_edge_dominator = true; + break; + } + + default: + break; + } + + if (back_edge_dominator) + dominator = cfg.get_function().entry_block; +} +} // namespace SPIRV_CROSS_NAMESPACE diff --git a/thirdparty/SPIRV-Cross/spirv_cfg.hpp b/thirdparty/SPIRV-Cross/spirv_cfg.hpp new file mode 100644 index 000000000..9e6141bbe --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cfg.hpp @@ -0,0 +1,168 @@ +/* + * Copyright 2016-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_CFG_HPP +#define SPIRV_CROSS_CFG_HPP + +#include "spirv_common.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +class Compiler; +class CFG +{ +public: + CFG(Compiler &compiler, const SPIRFunction &function); + + Compiler &get_compiler() + { + return compiler; + } + + const Compiler &get_compiler() const + { + return compiler; + } + + const SPIRFunction &get_function() const + { + return func; + } + + uint32_t get_immediate_dominator(uint32_t block) const + { + auto itr = immediate_dominators.find(block); + if (itr != std::end(immediate_dominators)) + return itr->second; + else + return 0; + } + + bool is_reachable(uint32_t block) const + { + return visit_order.count(block) != 0; + } + + uint32_t get_visit_order(uint32_t block) const + { + auto itr = visit_order.find(block); + assert(itr != std::end(visit_order)); + int v = itr->second.order; + assert(v > 0); + return uint32_t(v); + } + + uint32_t find_common_dominator(uint32_t a, uint32_t b) const; + + const SmallVector &get_preceding_edges(uint32_t block) const + { + auto itr = preceding_edges.find(block); + if (itr != std::end(preceding_edges)) + return itr->second; + else + return empty_vector; + } + + const SmallVector &get_succeeding_edges(uint32_t block) const + { + auto itr = succeeding_edges.find(block); + if (itr != std::end(succeeding_edges)) + return itr->second; + else + return empty_vector; + } + + template + void walk_from(std::unordered_set &seen_blocks, uint32_t block, const Op &op) const + { + if (seen_blocks.count(block)) + return; + seen_blocks.insert(block); + + if (op(block)) + { + for (auto b : get_succeeding_edges(block)) + walk_from(seen_blocks, b, op); + } + } + + uint32_t find_loop_dominator(uint32_t block) const; + + bool node_terminates_control_flow_in_sub_graph(BlockID from, BlockID to) const; + +private: + struct VisitOrder + { + int order = -1; + bool visited_resolve = false; + bool visited_branches = false; + }; + + Compiler &compiler; + const SPIRFunction &func; + std::unordered_map> preceding_edges; + std::unordered_map> virtual_dominance_preceding_edges; + std::unordered_map> succeeding_edges; + std::unordered_map immediate_dominators; + std::unordered_map visit_order; + SmallVector post_order; + SmallVector empty_vector; + + void add_branch(uint32_t from, uint32_t to); + void add_virtual_dominance_branch(uint32_t from, uint32_t to); + void build_post_order_visit_order(); + void build_immediate_dominators(); + void post_order_visit_branches(uint32_t block); + void post_order_visit_resolve(uint32_t block); + void post_order_visit_entry(uint32_t block); + uint32_t visit_count = 0; + + bool is_back_edge(uint32_t to) const; + bool has_visited_branch(uint32_t to) const; + void visit_branch(uint32_t block_id); + + SmallVector visit_stack; + size_t last_visited_size = 0; +}; + +class DominatorBuilder +{ +public: + DominatorBuilder(const CFG &cfg); + + void add_block(uint32_t block); + uint32_t get_dominator() const + { + return dominator; + } + + void lift_continue_block_dominator(); + +private: + const CFG &cfg; + uint32_t dominator = 0; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_common.hpp b/thirdparty/SPIRV-Cross/spirv_common.hpp new file mode 100644 index 000000000..b003d4db1 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_common.hpp @@ -0,0 +1,2096 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_COMMON_HPP +#define SPIRV_CROSS_COMMON_HPP + +#ifndef SPV_ENABLE_UTILITY_CODE +#define SPV_ENABLE_UTILITY_CODE +#endif + +// Pragmatic hack to avoid symbol conflicts when including both hpp11 and hpp headers in same translation unit. +// This is an unfortunate SPIRV-Headers issue that we cannot easily deal with ourselves. +#ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE +#define spv SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE +#define SPIRV_CROSS_SPV_HEADER_NAMESPACE SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE +#else +#define SPIRV_CROSS_SPV_HEADER_NAMESPACE spv +#endif + +#include "spirv.hpp" +#include "spirv_cross_containers.hpp" +#include "spirv_cross_error_handling.hpp" +#include + +// A bit crude, but allows projects which embed SPIRV-Cross statically to +// effectively hide all the symbols from other projects. +// There is a case where we have: +// - Project A links against SPIRV-Cross statically. +// - Project A links against Project B statically. +// - Project B links against SPIRV-Cross statically (might be a different version). +// This leads to a conflict with extremely bizarre results. +// By overriding the namespace in one of the project builds, we can work around this. +// If SPIRV-Cross is embedded in dynamic libraries, +// prefer using -fvisibility=hidden on GCC/Clang instead. +#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE +#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE +#else +#define SPIRV_CROSS_NAMESPACE spirv_cross +#endif + +namespace SPIRV_CROSS_NAMESPACE +{ +namespace inner +{ +template +void join_helper(StringStream<> &stream, T &&t) +{ + stream << std::forward(t); +} + +template +void join_helper(StringStream<> &stream, T &&t, Ts &&... ts) +{ + stream << std::forward(t); + join_helper(stream, std::forward(ts)...); +} +} // namespace inner + +class Bitset +{ +public: + Bitset() = default; + explicit inline Bitset(uint64_t lower_) + : lower(lower_) + { + } + + inline bool get(uint32_t bit) const + { + if (bit < 64) + return (lower & (1ull << bit)) != 0; + else + return higher.count(bit) != 0; + } + + inline void set(uint32_t bit) + { + if (bit < 64) + lower |= 1ull << bit; + else + higher.insert(bit); + } + + inline void clear(uint32_t bit) + { + if (bit < 64) + lower &= ~(1ull << bit); + else + higher.erase(bit); + } + + inline uint64_t get_lower() const + { + return lower; + } + + inline void reset() + { + lower = 0; + higher.clear(); + } + + inline void merge_and(const Bitset &other) + { + lower &= other.lower; + std::unordered_set tmp_set; + for (auto &v : higher) + if (other.higher.count(v) != 0) + tmp_set.insert(v); + higher = std::move(tmp_set); + } + + inline void merge_or(const Bitset &other) + { + lower |= other.lower; + for (auto &v : other.higher) + higher.insert(v); + } + + inline bool operator==(const Bitset &other) const + { + if (lower != other.lower) + return false; + + if (higher.size() != other.higher.size()) + return false; + + for (auto &v : higher) + if (other.higher.count(v) == 0) + return false; + + return true; + } + + inline bool operator!=(const Bitset &other) const + { + return !(*this == other); + } + + template + void for_each_bit(const Op &op) const + { + // TODO: Add ctz-based iteration. + for (uint32_t i = 0; i < 64; i++) + { + if (lower & (1ull << i)) + op(i); + } + + if (higher.empty()) + return; + + // Need to enforce an order here for reproducible results, + // but hitting this path should happen extremely rarely, so having this slow path is fine. + SmallVector bits; + bits.reserve(higher.size()); + for (auto &v : higher) + bits.push_back(v); + std::sort(std::begin(bits), std::end(bits)); + + for (auto &v : bits) + op(v); + } + + inline bool empty() const + { + return lower == 0 && higher.empty(); + } + +private: + // The most common bits to set are all lower than 64, + // so optimize for this case. Bits spilling outside 64 go into a slower data structure. + // In almost all cases, higher data structure will not be used. + uint64_t lower = 0; + std::unordered_set higher; +}; + +// Helper template to avoid lots of nasty string temporary munging. +template +std::string join(Ts &&... ts) +{ + StringStream<> stream; + inner::join_helper(stream, std::forward(ts)...); + return stream.str(); +} + +inline std::string merge(const SmallVector &list, const char *between = ", ") +{ + StringStream<> stream; + for (auto &elem : list) + { + stream << elem; + if (&elem != &list.back()) + stream << between; + } + return stream.str(); +} + +// Make sure we don't accidentally call this with float or doubles with SFINAE. +// Have to use the radix-aware overload. +template ::value, int>::type = 0> +inline std::string convert_to_string(const T &t) +{ + return std::to_string(t); +} + +static inline std::string convert_to_string(int32_t value) +{ + // INT_MIN is ... special on some backends. If we use a decimal literal, and negate it, we + // could accidentally promote the literal to long first, then negate. + // To workaround it, emit int(0x80000000) instead. + if (value == (std::numeric_limits::min)()) + return "int(0x80000000)"; + else + return std::to_string(value); +} + +static inline std::string convert_to_string(int64_t value, const std::string &int64_type, bool long_long_literal_suffix) +{ + // INT64_MIN is ... special on some backends. + // If we use a decimal literal, and negate it, we might overflow the representable numbers. + // To workaround it, emit int(0x80000000) instead. + if (value == (std::numeric_limits::min)()) + return join(int64_type, "(0x8000000000000000u", (long_long_literal_suffix ? "ll" : "l"), ")"); + else + return std::to_string(value) + (long_long_literal_suffix ? "ll" : "l"); +} + +// Allow implementations to set a convenient standard precision +#ifndef SPIRV_CROSS_FLT_FMT +#define SPIRV_CROSS_FLT_FMT "%.32g" +#endif + +// Disable sprintf and strcat warnings. +// We cannot rely on snprintf and family existing because, ..., MSVC. +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +static inline void fixup_radix_point(char *str, char radix_point) +{ + // Setting locales is a very risky business in multi-threaded program, + // so just fixup locales instead. We only need to care about the radix point. + if (radix_point != '.') + { + while (*str != '\0') + { + if (*str == radix_point) + *str = '.'; + str++; + } + } +} + +inline std::string convert_to_string(float t, char locale_radix_point) +{ + // std::to_string for floating point values is broken. + // Fallback to something more sane. + char buf[64]; + sprintf(buf, SPIRV_CROSS_FLT_FMT, t); + fixup_radix_point(buf, locale_radix_point); + + // Ensure that the literal is float. + if (!strchr(buf, '.') && !strchr(buf, 'e')) + strcat(buf, ".0"); + return buf; +} + +inline std::string convert_to_string(double t, char locale_radix_point) +{ + // std::to_string for floating point values is broken. + // Fallback to something more sane. + char buf[64]; + sprintf(buf, SPIRV_CROSS_FLT_FMT, t); + fixup_radix_point(buf, locale_radix_point); + + // Ensure that the literal is float. + if (!strchr(buf, '.') && !strchr(buf, 'e')) + strcat(buf, ".0"); + return buf; +} + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +class FloatFormatter +{ +public: + virtual ~FloatFormatter() = default; + virtual std::string format_float(float value) = 0; + virtual std::string format_double(double value) = 0; +}; + +template +struct ValueSaver +{ + explicit ValueSaver(T ¤t_) + : current(current_) + , saved(current_) + { + } + + void release() + { + current = saved; + } + + ~ValueSaver() + { + release(); + } + + T ¤t; + T saved; +}; + +struct Instruction +{ + uint16_t op = 0; + uint16_t count = 0; + // If offset is 0 (not a valid offset into the instruction stream), + // we have an instruction stream which is embedded in the object. + uint32_t offset = 0; + uint32_t length = 0; + + inline bool is_embedded() const + { + return offset == 0; + } +}; + +struct EmbeddedInstruction : Instruction +{ + SmallVector ops; +}; + +enum Types +{ + TypeNone, + TypeType, + TypeVariable, + TypeConstant, + TypeFunction, + TypeFunctionPrototype, + TypeBlock, + TypeExtension, + TypeExpression, + TypeConstantOp, + TypeCombinedImageSampler, + TypeAccessChain, + TypeUndef, + TypeString, + TypeDebugLocalVariable, + TypeCount +}; + +template +class TypedID; + +template <> +class TypedID +{ +public: + TypedID() = default; + TypedID(uint32_t id_) + : id(id_) + { + } + + template + TypedID(const TypedID &other) + { + *this = other; + } + + template + TypedID &operator=(const TypedID &other) + { + id = uint32_t(other); + return *this; + } + + // Implicit conversion to u32 is desired here. + // As long as we block implicit conversion between TypedID and TypedID we're good. + operator uint32_t() const + { + return id; + } + + template + operator TypedID() const + { + return TypedID(*this); + } + +private: + uint32_t id = 0; +}; + +template +class TypedID +{ +public: + TypedID() = default; + TypedID(uint32_t id_) + : id(id_) + { + } + + explicit TypedID(const TypedID &other) + : id(uint32_t(other)) + { + } + + operator uint32_t() const + { + return id; + } + +private: + uint32_t id = 0; +}; + +using VariableID = TypedID; +using TypeID = TypedID; +using ConstantID = TypedID; +using FunctionID = TypedID; +using BlockID = TypedID; +using ID = TypedID; + +// Helper for Variant interface. +struct IVariant +{ + virtual ~IVariant() = default; + virtual IVariant *clone(ObjectPoolBase *pool) = 0; + ID self = 0; + +protected: + IVariant() = default; + IVariant(const IVariant&) = default; + IVariant &operator=(const IVariant&) = default; +}; + +#define SPIRV_CROSS_DECLARE_CLONE(T) \ + IVariant *clone(ObjectPoolBase *pool) override \ + { \ + return static_cast *>(pool)->allocate(*this); \ + } + +struct SPIRUndef : IVariant +{ + enum + { + type = TypeUndef + }; + + explicit SPIRUndef(TypeID basetype_) + : basetype(basetype_) + { + } + TypeID basetype; + + SPIRV_CROSS_DECLARE_CLONE(SPIRUndef) +}; + +struct SPIRString : IVariant +{ + enum + { + type = TypeString + }; + + explicit SPIRString(std::string str_) + : str(std::move(str_)) + { + } + + std::string str; + + SPIRV_CROSS_DECLARE_CLONE(SPIRString) +}; + +struct SPIRDebugLocalVariable : IVariant +{ + enum + { + type = TypeDebugLocalVariable + }; + + uint32_t name_id; + + SPIRV_CROSS_DECLARE_CLONE(SPIRDebugLocalVariable) +}; + +// This type is only used by backends which need to access the combined image and sampler IDs separately after +// the OpSampledImage opcode. +struct SPIRCombinedImageSampler : IVariant +{ + enum + { + type = TypeCombinedImageSampler + }; + SPIRCombinedImageSampler(TypeID type_, VariableID image_, VariableID sampler_) + : combined_type(type_) + , image(image_) + , sampler(sampler_) + { + } + TypeID combined_type; + VariableID image; + VariableID sampler; + + SPIRV_CROSS_DECLARE_CLONE(SPIRCombinedImageSampler) +}; + +struct SPIRConstantOp : IVariant +{ + enum + { + type = TypeConstantOp + }; + + SPIRConstantOp(TypeID result_type, spv::Op op, const uint32_t *args, uint32_t length) + : opcode(op) + , basetype(result_type) + { + arguments.reserve(length); + for (uint32_t i = 0; i < length; i++) + arguments.push_back(args[i]); + } + + spv::Op opcode; + SmallVector arguments; + TypeID basetype; + + SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp) +}; + +struct SPIRType : IVariant +{ + enum + { + type = TypeType + }; + + spv::Op op = spv::Op::OpNop; + explicit SPIRType(spv::Op op_) : op(op_) {} + + enum BaseType + { + Unknown, + Void, + Boolean, + SByte, + UByte, + Short, + UShort, + Int, + UInt, + Int64, + UInt64, + AtomicCounter, + Half, + Float, + Double, + Struct, + Image, + SampledImage, + Sampler, + AccelerationStructure, + RayQuery, + CoopVecNV, + + // Keep internal types at the end. + ControlPointArray, + Interpolant, + Char, + // MSL specific type, that is used by 'object'(analog of 'task' from glsl) shader. + MeshGridProperties, + BFloat16, + FloatE4M3, + FloatE5M2, + + Tensor, + DescriptorHeapBuffer + }; + + // Scalar/vector/matrix support. + BaseType basetype = Unknown; + uint32_t width = 0; + uint32_t vecsize = 1; + uint32_t columns = 1; + + // Arrays, support array of arrays by having a vector of array sizes. + SmallVector array; + + // Array elements can be either specialization constants or specialization ops. + // This array determines how to interpret the array size. + // If an element is true, the element is a literal, + // otherwise, it's an expression, which must be resolved on demand. + // The actual size is not really known until runtime. + SmallVector array_size_literal; + + // Pointers + // Keep track of how many pointer layers we have. + uint32_t pointer_depth = 0; + bool pointer = false; + bool forward_pointer = false; + + union + { + struct + { + uint32_t use_id; + uint32_t rows_id; + uint32_t columns_id; + uint32_t scope_id; + } cooperative; + + struct + { + uint32_t component_type_id; + uint32_t component_count_id; + } coopVecNV; + + struct + { + uint32_t type; + uint32_t rank; + uint32_t shape; + } tensor; + + struct + { + spv::StorageClass storage; + } descriptor_heap_buffer; + } ext; + + spv::StorageClass storage = spv::StorageClassGeneric; + + SmallVector member_types; + + // If member order has been rewritten to handle certain scenarios with Offset, + // allow codegen to rewrite the index. + SmallVector member_type_index_redirection; + + struct ImageType + { + TypeID type; + spv::Dim dim; + bool depth; + bool arrayed; + bool ms; + uint32_t sampled; + spv::ImageFormat format; + spv::AccessQualifier access; + } image = {}; + + // Structs can be declared multiple times if they are used as part of interface blocks. + // We want to detect this so that we only emit the struct definition once. + // Since we cannot rely on OpName to be equal, we need to figure out aliases. + TypeID type_alias = 0; + + // Denotes the type which this type is based on. + // Allows the backend to traverse how a complex type is built up during access chains. + TypeID parent_type = 0; + + // Used in backends to avoid emitting members with conflicting names. + std::unordered_set member_name_cache; + + SPIRV_CROSS_DECLARE_CLONE(SPIRType) +}; + +struct SPIRExtension : IVariant +{ + enum + { + type = TypeExtension + }; + + enum Extension + { + Unsupported, + GLSL, + SPV_debug_info, + SPV_AMD_shader_ballot, + SPV_AMD_shader_explicit_vertex_parameter, + SPV_AMD_shader_trinary_minmax, + SPV_AMD_gcn_shader, + NonSemanticDebugPrintf, + NonSemanticShaderDebugInfo, + NonSemanticGeneric + }; + + enum ShaderDebugInfoOps + { + DebugLine = 103, + DebugSource = 35 + }; + + explicit SPIRExtension(Extension ext_) + : ext(ext_) + { + } + + Extension ext; + SPIRV_CROSS_DECLARE_CLONE(SPIRExtension) +}; + +// SPIREntryPoint is not a variant since its IDs are used to decorate OpFunction, +// so in order to avoid conflicts, we can't stick them in the ids array. +struct SPIREntryPoint +{ + SPIREntryPoint(FunctionID self_, spv::ExecutionModel execution_model, const std::string &entry_name) + : self(self_) + , name(entry_name) + , orig_name(entry_name) + , model(execution_model) + { + } + SPIREntryPoint() = default; + + FunctionID self = 0; + std::string name; + std::string orig_name; + std::unordered_map fp_fast_math_defaults; + bool signed_zero_inf_nan_preserve_8 = false; + bool signed_zero_inf_nan_preserve_16 = false; + bool signed_zero_inf_nan_preserve_32 = false; + bool signed_zero_inf_nan_preserve_64 = false; + SmallVector interface_variables; + + Bitset flags; + struct WorkgroupSize + { + uint32_t x = 0, y = 0, z = 0; + uint32_t id_x = 0, id_y = 0, id_z = 0; + uint32_t constant = 0; // Workgroup size can be expressed as a constant/spec-constant instead. + } workgroup_size; + uint32_t invocations = 0; + uint32_t output_vertices = 0; + uint32_t output_primitives = 0; + spv::ExecutionModel model = spv::ExecutionModelMax; + bool geometry_passthrough = false; +}; + +struct SPIRExpression : IVariant +{ + enum + { + type = TypeExpression + }; + + // Only created by the backend target to avoid creating tons of temporaries. + SPIRExpression(std::string expr, TypeID expression_type_, bool immutable_) + : expression(std::move(expr)) + , expression_type(expression_type_) + , immutable(immutable_) + { + } + + // If non-zero, prepend expression with to_expression(base_expression). + // Used in amortizing multiple calls to to_expression() + // where in certain cases that would quickly force a temporary when not needed. + ID base_expression = 0; + + std::string expression; + TypeID expression_type = 0; + + // If this expression is a forwarded load, + // allow us to reference the original variable. + ID loaded_from = 0; + + // If this expression will never change, we can avoid lots of temporaries + // in high level source. + // An expression being immutable can be speculative, + // it is assumed that this is true almost always. + bool immutable = false; + + // Before use, this expression must be transposed. + // This is needed for targets which don't support row_major layouts. + bool need_transpose = false; + + // Whether or not this is an access chain expression. + bool access_chain = false; + + // Whether or not gl_MeshVerticesEXT[].gl_Position (as a whole or .y) is referenced + bool access_meshlet_position_y = false; + + // If this expression represents a OpBufferPointerEXT cast. + bool buffer_pointer = false; + + // Temporaries which can remain forwarded as long as this variable is not modified. + // Only used for buffer pointers. + SmallVector buffer_pointer_dependees; + + // A list of expressions which this expression depends on. + SmallVector expression_dependencies; + + // Similar as expression dependencies, but does not stop the tracking for force-temporary variables. + // We need to know the full chain from store back to any SSA variable. + SmallVector invariance_dependencies; + + // By reading this expression, we implicitly read these expressions as well. + // Used by access chain Store and Load since we read multiple expressions in this case. + SmallVector implied_read_expressions; + + // The expression was emitted at a certain scope. Lets us track when an expression read means multiple reads. + uint32_t emitted_loop_level = 0; + + SPIRV_CROSS_DECLARE_CLONE(SPIRExpression) +}; + +struct SPIRFunctionPrototype : IVariant +{ + enum + { + type = TypeFunctionPrototype + }; + + explicit SPIRFunctionPrototype(TypeID return_type_) + : return_type(return_type_) + { + } + + TypeID return_type; + SmallVector parameter_types; + + SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype) +}; + +struct SPIRBlock : IVariant +{ + enum + { + type = TypeBlock + }; + + enum Terminator + { + Unknown, + Direct, // Emit next block directly without a particular condition. + + Select, // Block ends with an if/else block. + MultiSelect, // Block ends with switch statement. + + Return, // Block ends with return. + Unreachable, // Noop + Kill, // Discard + IgnoreIntersection, // Ray Tracing + TerminateRay, // Ray Tracing + EmitMeshTasks // Mesh shaders + }; + + enum Merge + { + MergeNone, + MergeLoop, + MergeSelection + }; + + enum Hints + { + HintNone, + HintUnroll, + HintDontUnroll, + HintFlatten, + HintDontFlatten + }; + + enum Method + { + MergeToSelectForLoop, + MergeToDirectForLoop, + MergeToSelectContinueForLoop + }; + + enum ContinueBlockType + { + ContinueNone, + + // Continue block is branchless and has at least one instruction. + ForLoop, + + // Noop continue block. + WhileLoop, + + // Continue block is conditional. + DoWhileLoop, + + // Highly unlikely that anything will use this, + // since it is really awkward/impossible to express in GLSL. + ComplexLoop + }; + + enum : uint32_t + { + NoDominator = 0xffffffffu + }; + + Terminator terminator = Unknown; + Merge merge = MergeNone; + Hints hint = HintNone; + BlockID next_block = 0; + BlockID merge_block = 0; + BlockID continue_block = 0; + + ID return_value = 0; // If 0, return nothing (void). + ID condition = 0; + BlockID true_block = 0; + BlockID false_block = 0; + BlockID default_block = 0; + + // If terminator is EmitMeshTasksEXT. + struct + { + ID groups[3]; + ID payload; + } mesh = {}; + + SmallVector ops; + + struct Phi + { + ID local_variable; // flush local variable ... + BlockID parent; // If we're in from_block and want to branch into this block ... + VariableID function_variable; // to this function-global "phi" variable first. + }; + + // Before entering this block flush out local variables to magical "phi" variables. + SmallVector phi_variables; + + // Declare these temporaries before beginning the block. + // Used for handling complex continue blocks which have side effects. + SmallVector> declare_temporary; + + // Declare these temporaries, but only conditionally if this block turns out to be + // a complex loop header. + SmallVector> potential_declare_temporary; + + struct Case + { + uint64_t value; + BlockID block; + }; + SmallVector cases_32bit; + SmallVector cases_64bit; + + // If we have tried to optimize code for this block but failed, + // keep track of this. + bool disable_block_optimization = false; + + // If the continue block is complex, fallback to "dumb" for loops. + bool complex_continue = false; + + // Do we need a ladder variable to defer breaking out of a loop construct after a switch block? + bool need_ladder_break = false; + + // If marked, we have explicitly handled Phi from this block, so skip any flushes related to that on a branch. + // Used to handle an edge case with switch and case-label fallthrough where fall-through writes to Phi. + BlockID ignore_phi_from_block = 0; + + // The dominating block which this block might be within. + // Used in continue; blocks to determine if we really need to write continue. + BlockID loop_dominator = 0; + + // All access to these variables are dominated by this block, + // so before branching anywhere we need to make sure that we declare these variables. + SmallVector dominated_variables; + SmallVector rearm_dominated_variables; + + // These are variables which should be declared in a for loop header, if we + // fail to use a classic for-loop, + // we remove these variables, and fall back to regular variables outside the loop. + SmallVector loop_variables; + + // Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or + // sub-group-like operations. + // Make sure that we only use these expressions in the original block. + SmallVector invalidate_expressions; + + SPIRV_CROSS_DECLARE_CLONE(SPIRBlock) +}; + +struct SPIRFunction : IVariant +{ + enum + { + type = TypeFunction + }; + + SPIRFunction(TypeID return_type_, TypeID function_type_) + : return_type(return_type_) + , function_type(function_type_) + { + } + + struct Parameter + { + TypeID type; + ID id; + uint32_t read_count; + uint32_t write_count; + + // Set to true if this parameter aliases a global variable, + // used mostly in Metal where global variables + // have to be passed down to functions as regular arguments. + // However, for this kind of variable, we should not care about + // read and write counts as access to the function arguments + // is not local to the function in question. + bool alias_global_variable; + }; + + // When calling a function, and we're remapping separate image samplers, + // resolve these arguments into combined image samplers and pass them + // as additional arguments in this order. + // It gets more complicated as functions can pull in their own globals + // and combine them with parameters, + // so we need to distinguish if something is local parameter index + // or a global ID. + struct CombinedImageSamplerParameter + { + VariableID id; + VariableID image_id; + VariableID sampler_id; + bool global_image; + bool global_sampler; + bool depth; + }; + + TypeID return_type; + TypeID function_type; + SmallVector arguments; + + // Can be used by backends to add magic arguments. + // Currently used by combined image/sampler implementation. + + SmallVector shadow_arguments; + SmallVector local_variables; + BlockID entry_block = 0; + SmallVector blocks; + SmallVector combined_parameters; + + struct EntryLine + { + uint32_t file_id = 0; + uint32_t line_literal = 0; + }; + EntryLine entry_line; + + void add_local_variable(VariableID id) + { + local_variables.push_back(id); + } + + void add_parameter(TypeID parameter_type, ID id, bool alias_global_variable = false) + { + // Arguments are read-only until proven otherwise. + arguments.push_back({ parameter_type, id, 0u, 0u, alias_global_variable }); + } + + // Hooks to be run when the function returns. + // Mostly used for lowering internal data structures onto flattened structures. + // Need to defer this, because they might rely on things which change during compilation. + // Intentionally not a small vector, this one is rare, and std::function can be large. + Vector> fixup_hooks_out; + + // Hooks to be run when the function begins. + // Mostly used for populating internal data structures from flattened structures. + // Need to defer this, because they might rely on things which change during compilation. + // Intentionally not a small vector, this one is rare, and std::function can be large. + Vector> fixup_hooks_in; + + // On function entry, make sure to copy a constant array into thread addr space to work around + // the case where we are passing a constant array by value to a function on backends which do not + // consider arrays value types. + SmallVector constant_arrays_needed_on_stack; + + // Does this function (or any function called by it), emit geometry? + bool emits_geometry = false; + + bool active = false; + bool flush_undeclared = true; + bool do_combined_parameters = true; + + SPIRV_CROSS_DECLARE_CLONE(SPIRFunction) +}; + +struct SPIRAccessChain : IVariant +{ + enum + { + type = TypeAccessChain + }; + + SPIRAccessChain(TypeID basetype_, spv::StorageClass storage_, std::string base_, std::string dynamic_index_, + int32_t static_index_) + : basetype(basetype_) + , storage(storage_) + , base(std::move(base_)) + , dynamic_index(std::move(dynamic_index_)) + , static_index(static_index_) + { + } + + // The access chain represents an offset into a buffer. + // Some backends need more complicated handling of access chains to be able to use buffers, like HLSL + // which has no usable buffer type ala GLSL SSBOs. + // StructuredBuffer is too limited, so our only option is to deal with ByteAddressBuffer which works with raw addresses. + + TypeID basetype; + spv::StorageClass storage; + std::string base; + std::string dynamic_index; + int32_t static_index; + + VariableID loaded_from = 0; + uint32_t matrix_stride = 0; + uint32_t array_stride = 0; + bool row_major_matrix = false; + bool immutable = false; + + // By reading this expression, we implicitly read these expressions as well. + // Used by access chain Store and Load since we read multiple expressions in this case. + SmallVector implied_read_expressions; + + SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain) +}; + +struct SPIRVariable : IVariant +{ + enum + { + type = TypeVariable + }; + + SPIRVariable() = default; + SPIRVariable(TypeID basetype_, spv::StorageClass storage_, ID initializer_ = 0, VariableID basevariable_ = 0) + : basetype(basetype_) + , storage(storage_) + , initializer(initializer_) + , basevariable(basevariable_) + { + } + + TypeID basetype = 0; + spv::StorageClass storage = spv::StorageClassGeneric; + uint32_t decoration = 0; + ID initializer = 0; + VariableID basevariable = 0; + + SmallVector dereference_chain; + bool compat_builtin = false; + + // If a variable is shadowed, we only statically assign to it + // and never actually emit a statement for it. + // When we read the variable as an expression, just forward + // shadowed_id as the expression. + bool statically_assigned = false; + ID static_expression = 0; + + // Temporaries which can remain forwarded as long as this variable is not modified. + SmallVector dependees; + + // ShaderDebugInfo local variables attached to this variable via DebugDeclare + SmallVector debug_local_variables; + + bool deferred_declaration = false; + bool phi_variable = false; + + // Used to deal with Phi variable flushes. See flush_phi(). + bool allocate_temporary_copy = false; + + bool remapped_variable = false; + uint32_t remapped_components = 0; + + // The block which dominates all access to this variable. + BlockID dominator = 0; + // If true, this variable is a loop variable, when accessing the variable + // outside a loop, + // we should statically forward it. + bool loop_variable = false; + // Set to true while we're inside the for loop. + bool loop_variable_enable = false; + + // Used to find global LUTs + bool is_written_to = false; + + // Untyped pointer. The pointer of the variable is effectively void. + // The underlying payload for allocation is in alloca_type, but may be 0 too. + // This is mostly here to support descriptor heap proxy. + bool untyped = false; + ID untyped_alloca_type = 0; + + SPIRFunction::Parameter *parameter = nullptr; + + SPIRV_CROSS_DECLARE_CLONE(SPIRVariable) +}; + +struct SPIRConstant : IVariant +{ + enum + { + type = TypeConstant + }; + + union Constant + { + uint32_t u32; + int32_t i32; + float f32; + + uint64_t u64; + int64_t i64; + double f64; + }; + + struct ConstantVector + { + Constant r[4]; + // If != 0, this element is a specialization constant, and we should keep track of it as such. + ID id[4]; + uint32_t vecsize = 1; + + ConstantVector() + { + memset(r, 0, sizeof(r)); + } + }; + + struct ConstantMatrix + { + ConstantVector c[4]; + // If != 0, this column is a specialization constant, and we should keep track of it as such. + ID id[4]; + uint32_t columns = 1; + }; + + static inline float f16_to_f32(uint16_t u16_value) + { + // Based on the GLM implementation. + int s = (u16_value >> 15) & 0x1; + int e = (u16_value >> 10) & 0x1f; + int m = (u16_value >> 0) & 0x3ff; + + union + { + float f32; + uint32_t u32; + } u; + + if (e == 0) + { + if (m == 0) + { + u.u32 = uint32_t(s) << 31; + return u.f32; + } + else + { + while ((m & 0x400) == 0) + { + m <<= 1; + e--; + } + + e++; + m &= ~0x400; + } + } + else if (e == 31) + { + if (m == 0) + { + u.u32 = (uint32_t(s) << 31) | 0x7f800000u; + return u.f32; + } + else + { + u.u32 = (uint32_t(s) << 31) | 0x7f800000u | (m << 13); + return u.f32; + } + } + + e += 127 - 15; + m <<= 13; + u.u32 = (uint32_t(s) << 31) | (e << 23) | m; + return u.f32; + } + + static inline float fe4m3_to_f32(uint8_t v) + { + if ((v & 0x7f) == 0x7f) + { + union + { + float f32; + uint32_t u32; + } u; + + u.u32 = (v & 0x80) ? 0xffffffffu : 0x7fffffffu; + return u.f32; + } + else + { + // Reuse the FP16 to FP32 code. Cute bit-hackery. + return f16_to_f32((int16_t(int8_t(v)) << 7) & (0xffff ^ 0x4000)) * 256.0f; + } + } + + inline uint32_t specialization_constant_id(uint32_t col, uint32_t row) const + { + return m.c[col].id[row]; + } + + inline uint32_t specialization_constant_id(uint32_t col) const + { + return m.id[col]; + } + + inline uint32_t scalar(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].u32; + } + + inline int16_t scalar_i16(uint32_t col = 0, uint32_t row = 0) const + { + return int16_t(m.c[col].r[row].u32 & 0xffffu); + } + + inline uint16_t scalar_u16(uint32_t col = 0, uint32_t row = 0) const + { + return uint16_t(m.c[col].r[row].u32 & 0xffffu); + } + + inline int8_t scalar_i8(uint32_t col = 0, uint32_t row = 0) const + { + return int8_t(m.c[col].r[row].u32 & 0xffu); + } + + inline uint8_t scalar_u8(uint32_t col = 0, uint32_t row = 0) const + { + return uint8_t(m.c[col].r[row].u32 & 0xffu); + } + + inline float scalar_f16(uint32_t col = 0, uint32_t row = 0) const + { + return f16_to_f32(scalar_u16(col, row)); + } + + inline float scalar_bf16(uint32_t col = 0, uint32_t row = 0) const + { + uint32_t v = scalar_u16(col, row) << 16; + float fp32; + memcpy(&fp32, &v, sizeof(float)); + return fp32; + } + + inline float scalar_floate4m3(uint32_t col = 0, uint32_t row = 0) const + { + return fe4m3_to_f32(scalar_u8(col, row)); + } + + inline float scalar_bf8(uint32_t col = 0, uint32_t row = 0) const + { + return f16_to_f32(uint16_t(scalar_u8(col, row) << 8)); + } + + inline float scalar_f32(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].f32; + } + + inline int32_t scalar_i32(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].i32; + } + + inline double scalar_f64(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].f64; + } + + inline int64_t scalar_i64(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].i64; + } + + inline uint64_t scalar_u64(uint32_t col = 0, uint32_t row = 0) const + { + return m.c[col].r[row].u64; + } + + inline const ConstantVector &vector() const + { + return m.c[0]; + } + + inline uint32_t vector_size() const + { + return m.c[0].vecsize; + } + + inline uint32_t columns() const + { + return m.columns; + } + + inline void make_null(const SPIRType &constant_type_) + { + m = {}; + m.columns = constant_type_.columns; + for (auto &c : m.c) + c.vecsize = constant_type_.vecsize; + } + + inline bool constant_is_null() const + { + if (specialization) + return false; + if (!subconstants.empty()) + return false; + + for (uint32_t col = 0; col < columns(); col++) + for (uint32_t row = 0; row < vector_size(); row++) + if (scalar_u64(col, row) != 0) + return false; + + return true; + } + + explicit SPIRConstant(uint32_t constant_type_) + : constant_type(constant_type_) + { + } + + SPIRConstant() = default; + + SPIRConstant(TypeID constant_type_, const uint32_t *elements, uint32_t num_elements, bool specialized, bool replicated_ = false) + : constant_type(constant_type_) + , specialization(specialized) + , replicated(replicated_) + { + subconstants.reserve(num_elements); + for (uint32_t i = 0; i < num_elements; i++) + subconstants.push_back(elements[i]); + specialization = specialized; + } + + // Construct scalar (32-bit). + SPIRConstant(TypeID constant_type_, uint32_t v0, bool specialized) + : constant_type(constant_type_) + , specialization(specialized) + { + m.c[0].r[0].u32 = v0; + m.c[0].vecsize = 1; + m.columns = 1; + } + + // Construct scalar (64-bit). + SPIRConstant(TypeID constant_type_, uint64_t v0, bool specialized) + : constant_type(constant_type_) + , specialization(specialized) + { + m.c[0].r[0].u64 = v0; + m.c[0].vecsize = 1; + m.columns = 1; + } + + // Construct vectors and matrices. + SPIRConstant(TypeID constant_type_, const SPIRConstant *const *vector_elements, uint32_t num_elements, + bool specialized) + : constant_type(constant_type_) + , specialization(specialized) + { + bool matrix = vector_elements[0]->m.c[0].vecsize > 1; + + if (matrix) + { + m.columns = num_elements; + + for (uint32_t i = 0; i < num_elements; i++) + { + m.c[i] = vector_elements[i]->m.c[0]; + if (vector_elements[i]->specialization) + m.id[i] = vector_elements[i]->self; + } + } + else + { + m.c[0].vecsize = num_elements; + m.columns = 1; + + for (uint32_t i = 0; i < num_elements; i++) + { + m.c[0].r[i] = vector_elements[i]->m.c[0].r[0]; + if (vector_elements[i]->specialization) + m.c[0].id[i] = vector_elements[i]->self; + } + } + } + + TypeID constant_type = 0; + ConstantMatrix m; + + // If this constant is a specialization constant (i.e. created with OpSpecConstant*). + bool specialization = false; + // If this constant is used as an array length which creates specialization restrictions on some backends. + bool is_used_as_array_length = false; + + // If true, this is a LUT, and should always be declared in the outer scope. + bool is_used_as_lut = false; + + // If this is a null constant of array type with specialized length. + // May require special handling in initializer + bool is_null_array_specialized_length = false; + + // For composites which are constant arrays, etc. + SmallVector subconstants; + + // Whether the subconstants are intended to be replicated (e.g. OpConstantCompositeReplicateEXT) + bool replicated = false; + + // Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant, + // and uses them to initialize the constant. This allows the user + // to still be able to specialize the value by supplying corresponding + // preprocessor directives before compiling the shader. + std::string specialization_constant_macro_name; + + // ConstantSizeOfEXT. + ID size_of_type = 0; + + SPIRV_CROSS_DECLARE_CLONE(SPIRConstant) +}; + +// Variants have a very specific allocation scheme. +struct ObjectPoolGroup +{ + std::unique_ptr pools[TypeCount]; +}; + +class Variant +{ +public: + explicit Variant(ObjectPoolGroup *group_) + : group(group_) + { + } + + ~Variant() + { + if (holder) + group->pools[type]->deallocate_opaque(holder); + } + + // Marking custom move constructor as noexcept is important. + Variant(Variant &&other) SPIRV_CROSS_NOEXCEPT + { + *this = std::move(other); + } + + // We cannot copy from other variant without our own pool group. + // Have to explicitly copy. + Variant(const Variant &variant) = delete; + + // Marking custom move constructor as noexcept is important. + Variant &operator=(Variant &&other) SPIRV_CROSS_NOEXCEPT + { + if (this != &other) + { + if (holder) + group->pools[type]->deallocate_opaque(holder); + holder = other.holder; + group = other.group; + type = other.type; + allow_type_rewrite = other.allow_type_rewrite; + + other.holder = nullptr; + other.type = TypeNone; + } + return *this; + } + + // This copy/clone should only be called in the Compiler constructor. + // If this is called inside ::compile(), we invalidate any references we took higher in the stack. + // This should never happen. + Variant &operator=(const Variant &other) + { +//#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE +#ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE + abort(); +#endif + if (this != &other) + { + if (holder) + group->pools[type]->deallocate_opaque(holder); + + if (other.holder) + holder = other.holder->clone(group->pools[other.type].get()); + else + holder = nullptr; + + type = other.type; + allow_type_rewrite = other.allow_type_rewrite; + } + return *this; + } + + void set(IVariant *val, Types new_type) + { + if (holder) + group->pools[type]->deallocate_opaque(holder); + holder = nullptr; + + if (!allow_type_rewrite && type != TypeNone && type != new_type) + { + if (val) + group->pools[new_type]->deallocate_opaque(val); + SPIRV_CROSS_THROW("Overwriting a variant with new type."); + } + + holder = val; + type = new_type; + allow_type_rewrite = false; + } + + template + T *allocate_and_set(Types new_type, Ts &&... ts) + { + T *val = static_cast &>(*group->pools[new_type]).allocate(std::forward(ts)...); + set(val, new_type); + return val; + } + + template + T &get() + { + if (!holder) + SPIRV_CROSS_THROW("nullptr"); + if (static_cast(T::type) != type) + SPIRV_CROSS_THROW("Bad cast"); + return *static_cast(holder); + } + + template + const T &get() const + { + if (!holder) + SPIRV_CROSS_THROW("nullptr"); + if (static_cast(T::type) != type) + SPIRV_CROSS_THROW("Bad cast"); + return *static_cast(holder); + } + + Types get_type() const + { + return type; + } + + ID get_id() const + { + return holder ? holder->self : ID(0); + } + + bool empty() const + { + return !holder; + } + + void reset() + { + if (holder) + group->pools[type]->deallocate_opaque(holder); + holder = nullptr; + type = TypeNone; + } + + void set_allow_type_rewrite() + { + allow_type_rewrite = true; + } + +private: + ObjectPoolGroup *group = nullptr; + IVariant *holder = nullptr; + Types type = TypeNone; + bool allow_type_rewrite = false; +}; + +template +T &variant_get(Variant &var) +{ + return var.get(); +} + +template +const T &variant_get(const Variant &var) +{ + return var.get(); +} + +template +T &variant_set(Variant &var, P &&... args) +{ + auto *ptr = var.allocate_and_set(static_cast(T::type), std::forward

(args)...); + return *ptr; +} + +struct AccessChainMeta +{ + uint32_t storage_physical_type = 0; + bool need_transpose = false; + bool storage_is_packed = false; + bool storage_is_invariant = false; + bool flattened_struct = false; + bool relaxed_precision = false; + bool access_meshlet_position_y = false; + bool chain_is_builtin = false; + spv::BuiltIn builtin = {}; +}; + +enum ExtendedDecorations +{ + // Marks if a buffer block is re-packed, i.e. member declaration might be subject to PhysicalTypeID remapping and padding. + SPIRVCrossDecorationBufferBlockRepacked = 0, + + // A type in a buffer block might be declared with a different physical type than the logical type. + // If this is not set, PhysicalTypeID == the SPIR-V type as declared. + SPIRVCrossDecorationPhysicalTypeID, + + // Marks if the physical type is to be declared with tight packing rules, i.e. packed_floatN on MSL and friends. + // If this is set, PhysicalTypeID might also be set. It can be set to same as logical type if all we're doing + // is converting float3 to packed_float3 for example. + // If this is marked on a struct, it means the struct itself must use only Packed types for all its members. + SPIRVCrossDecorationPhysicalTypePacked, + + // The padding in bytes before declaring this struct member. + // If used on a struct type, marks the target size of a struct. + SPIRVCrossDecorationPaddingTarget, + + SPIRVCrossDecorationInterfaceMemberIndex, + SPIRVCrossDecorationInterfaceOrigID, + SPIRVCrossDecorationResourceIndexPrimary, + // Used for decorations like resource indices for samplers when part of combined image samplers. + // A variable might need to hold two resource indices in this case. + SPIRVCrossDecorationResourceIndexSecondary, + // Used for resource indices for multiplanar images when part of combined image samplers. + SPIRVCrossDecorationResourceIndexTertiary, + SPIRVCrossDecorationResourceIndexQuaternary, + + // Marks a buffer block for using explicit offsets (GLSL/HLSL). + SPIRVCrossDecorationExplicitOffset, + + // Apply to a variable in the Input storage class; marks it as holding the base group passed to vkCmdDispatchBase(), + // or the base vertex and instance indices passed to vkCmdDrawIndexed(). + // In MSL, this is used to adjust the WorkgroupId and GlobalInvocationId variables in compute shaders, + // and to hold the BaseVertex and BaseInstance variables in vertex shaders. + SPIRVCrossDecorationBuiltInDispatchBase, + + // Apply to a variable that is a function parameter; marks it as being a "dynamic" + // combined image-sampler. In MSL, this is used when a function parameter might hold + // either a regular combined image-sampler or one that has an attached sampler + // Y'CbCr conversion. + SPIRVCrossDecorationDynamicImageSampler, + + // Apply to a variable in the Input storage class; marks it as holding the size of the stage + // input grid. + // In MSL, this is used to hold the vertex and instance counts in a tessellation pipeline + // vertex shader. + SPIRVCrossDecorationBuiltInStageInputSize, + + // Apply to any access chain of a tessellation I/O variable; stores the type of the sub-object + // that was chained to, as recorded in the input variable itself. This is used in case the pointer + // is itself used as the base of an access chain, to calculate the original type of the sub-object + // chained to, in case a swizzle needs to be applied. This should not happen normally with valid + // SPIR-V, but the MSL backend can change the type of input variables, necessitating the + // addition of swizzles to keep the generated code compiling. + SPIRVCrossDecorationTessIOOriginalInputTypeID, + + // Apply to any access chain of an interface variable used with pull-model interpolation, where the variable is a + // vector but the resulting pointer is a scalar; stores the component index that is to be accessed by the chain. + // This is used when emitting calls to interpolation functions on the chain in MSL: in this case, the component + // must be applied to the result, since pull-model interpolants in MSL cannot be swizzled directly, but the + // results of interpolation can. + SPIRVCrossDecorationInterpolantComponentExpr, + + // Apply to any struct type that is used in the Workgroup storage class. + // This causes matrices in MSL prior to Metal 3.0 to be emitted using a special + // class that is convertible to the standard matrix type, to work around the + // lack of constructors in the 'threadgroup' address space. + SPIRVCrossDecorationWorkgroupStruct, + + SPIRVCrossDecorationOverlappingBinding, + + SPIRVCrossDecorationCount +}; + +struct Meta +{ + struct Decoration + { + std::string alias; + std::string qualified_alias; + std::string user_semantic; + std::string user_type; + Bitset decoration_flags; + spv::BuiltIn builtin_type = spv::BuiltInMax; + uint32_t location = 0; + uint32_t component = 0; + uint32_t set = 0; + uint32_t binding = 0; + uint32_t offset = 0; + uint32_t offset_id = 0; + uint32_t xfb_buffer = 0; + uint32_t xfb_stride = 0; + uint32_t stream = 0; + uint32_t array_stride = 0; + uint32_t array_stride_id = 0; + uint32_t matrix_stride = 0; + uint32_t input_attachment = 0; + uint32_t spec_id = 0; + uint32_t index = 0; + spv::FPRoundingMode fp_rounding_mode = spv::FPRoundingModeMax; + spv::FPFastMathModeMask fp_fast_math_mode = spv::FPFastMathModeMaskNone; + bool builtin = false; + bool qualified_alias_explicit_override = false; + + struct Extended + { + Extended() + { + // MSVC 2013 workaround to init like this. + for (auto &v : values) + v = 0; + } + + Bitset flags; + uint32_t values[SPIRVCrossDecorationCount]; + } extended; + }; + + Decoration decoration; + + // Intentionally not a SmallVector. Decoration is large and somewhat rare. + Vector members; + + std::unordered_map decoration_word_offset; + + // For SPV_GOOGLE_hlsl_functionality1. + bool hlsl_is_magic_counter_buffer = false; + // ID for the sibling counter buffer. + uint32_t hlsl_magic_counter_buffer = 0; +}; + +// A user callback that remaps the type of any variable. +// var_name is the declared name of the variable. +// name_of_type is the textual name of the type which will be used in the code unless written to by the callback. +using VariableTypeRemapCallback = + std::function; + +class Hasher +{ +public: + inline void u32(uint32_t value) + { + h = (h * 0x100000001b3ull) ^ value; + } + + inline uint64_t get() const + { + return h; + } + +private: + uint64_t h = 0xcbf29ce484222325ull; +}; + +static inline bool type_is_floating_point(const SPIRType &type) +{ + return type.basetype == SPIRType::Half || type.basetype == SPIRType::Float || type.basetype == SPIRType::Double || + type.basetype == SPIRType::BFloat16 || type.basetype == SPIRType::FloatE5M2 || type.basetype == SPIRType::FloatE4M3; +} + +static inline bool type_is_integral(const SPIRType &type) +{ + return type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte || type.basetype == SPIRType::Short || + type.basetype == SPIRType::UShort || type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt || + type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64; +} + +static inline SPIRType::BaseType to_signed_basetype(uint32_t width) +{ + switch (width) + { + case 8: + return SPIRType::SByte; + case 16: + return SPIRType::Short; + case 32: + return SPIRType::Int; + case 64: + return SPIRType::Int64; + default: + SPIRV_CROSS_THROW("Invalid bit width."); + } +} + +static inline SPIRType::BaseType to_unsigned_basetype(uint32_t width) +{ + switch (width) + { + case 8: + return SPIRType::UByte; + case 16: + return SPIRType::UShort; + case 32: + return SPIRType::UInt; + case 64: + return SPIRType::UInt64; + default: + SPIRV_CROSS_THROW("Invalid bit width."); + } +} + +// Returns true if an arithmetic operation does not change behavior depending on signedness. +static inline bool opcode_is_sign_invariant(spv::Op opcode) +{ + switch (opcode) + { + case spv::OpIEqual: + case spv::OpINotEqual: + case spv::OpISub: + case spv::OpIAdd: + case spv::OpIMul: + case spv::OpShiftLeftLogical: + case spv::OpBitwiseOr: + case spv::OpBitwiseXor: + case spv::OpBitwiseAnd: + return true; + + default: + return false; + } +} + +static inline bool opcode_can_promote_integer_implicitly(spv::Op opcode) +{ + switch (opcode) + { + case spv::OpSNegate: + case spv::OpNot: + case spv::OpBitwiseAnd: + case spv::OpBitwiseOr: + case spv::OpBitwiseXor: + case spv::OpShiftLeftLogical: + case spv::OpShiftRightLogical: + case spv::OpShiftRightArithmetic: + case spv::OpIAdd: + case spv::OpISub: + case spv::OpIMul: + case spv::OpSDiv: + case spv::OpUDiv: + case spv::OpSRem: + case spv::OpUMod: + case spv::OpSMod: + return true; + + default: + return false; + } +} + +struct SetBindingPair +{ + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const SetBindingPair &other) const + { + return desc_set == other.desc_set && binding == other.binding; + } + + inline bool operator<(const SetBindingPair &other) const + { + return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding); + } +}; + +struct LocationComponentPair +{ + uint32_t location; + uint32_t component; + + inline bool operator==(const LocationComponentPair &other) const + { + return location == other.location && component == other.component; + } + + inline bool operator<(const LocationComponentPair &other) const + { + return location < other.location || (location == other.location && component < other.component); + } +}; + +struct StageSetBinding +{ + spv::ExecutionModel model; + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const StageSetBinding &other) const + { + return model == other.model && desc_set == other.desc_set && binding == other.binding; + } +}; + +struct InternalHasher +{ + inline size_t operator()(const SetBindingPair &value) const + { + // Quality of hash doesn't really matter here. + auto hash_set = std::hash()(value.desc_set); + auto hash_binding = std::hash()(value.binding); + return (hash_set * 0x10001b31) ^ hash_binding; + } + + inline size_t operator()(const LocationComponentPair &value) const + { + // Quality of hash doesn't really matter here. + auto hash_set = std::hash()(value.location); + auto hash_binding = std::hash()(value.component); + return (hash_set * 0x10001b31) ^ hash_binding; + } + + inline size_t operator()(const StageSetBinding &value) const + { + // Quality of hash doesn't really matter here. + auto hash_model = std::hash()(value.model); + auto hash_set = std::hash()(value.desc_set); + auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set; + return (tmp_hash * 0x10001b31) ^ value.binding; + } +}; + +// Special constant used in a {MSL,HLSL}ResourceBinding desc_set +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantDescriptorSet = ~(0u); + +// Special constant used in a {MSL,HLSL}ResourceBinding binding +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantBinding = 0; +} // namespace SPIRV_CROSS_NAMESPACE + +namespace std +{ +template +struct hash> +{ + size_t operator()(const SPIRV_CROSS_NAMESPACE::TypedID &value) const + { + return std::hash()(value); + } +}; +} // namespace std + +#ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE +#undef spv +#endif +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cpp.cpp b/thirdparty/SPIRV-Cross/spirv_cpp.cpp new file mode 100644 index 000000000..4bb6fc261 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cpp.cpp @@ -0,0 +1,553 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_cpp.hpp" + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +void CompilerCPP::emit_buffer_block(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + auto instance_name = to_name(var.self); + + uint32_t descriptor_set = ir.meta[var.self].decoration.set; + uint32_t binding = ir.meta[var.self].decoration.binding; + + emit_block_struct(type); + auto buffer_name = to_name(type.self); + + statement("internal::Resource<", buffer_name, type_to_array_glsl(type, var.self), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back( + join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");")); + statement(""); +} + +void CompilerCPP::emit_interface_block(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + + const char *qual = var.storage == StorageClassInput ? "StageInput" : "StageOutput"; + const char *lowerqual = var.storage == StorageClassInput ? "stage_input" : "stage_output"; + auto instance_name = to_name(var.self); + uint32_t location = ir.meta[var.self].decoration.location; + + string buffer_name; + auto flags = ir.meta[type.self].decoration.decoration_flags; + if (flags.get(DecorationBlock)) + { + emit_block_struct(type); + buffer_name = to_name(type.self); + } + else + buffer_name = type_to_glsl(type); + + statement("internal::", qual, "<", buffer_name, type_to_array_glsl(type, var.self), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back(join("s.register_", lowerqual, "(", instance_name, "__", ", ", location, ");")); + statement(""); +} + +void CompilerCPP::emit_shared(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto instance_name = to_name(var.self); + statement(CompilerGLSL::variable_decl(var), ";"); + statement_no_indent("#define ", instance_name, " __res->", instance_name); +} + +void CompilerCPP::emit_uniform(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + auto instance_name = to_name(var.self); + + uint32_t descriptor_set = ir.meta[var.self].decoration.set; + uint32_t binding = ir.meta[var.self].decoration.binding; + uint32_t location = ir.meta[var.self].decoration.location; + + string type_name = type_to_glsl(type); + remap_variable_type_name(type, instance_name, type_name); + + if (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage || + type.basetype == SPIRType::AtomicCounter) + { + statement("internal::Resource<", type_name, type_to_array_glsl(type, var.self), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back( + join("s.register_resource(", instance_name, "__", ", ", descriptor_set, ", ", binding, ");")); + } + else + { + statement("internal::UniformConstant<", type_name, type_to_array_glsl(type, var.self), "> ", instance_name, "__;"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, "__.get()"); + resource_registrations.push_back( + join("s.register_uniform_constant(", instance_name, "__", ", ", location, ");")); + } + + statement(""); +} + +void CompilerCPP::emit_push_constant_block(const SPIRVariable &var) +{ + add_resource_name(var.self); + + auto &type = get(var.basetype); + auto &flags = ir.meta[var.self].decoration.decoration_flags; + if (flags.get(DecorationBinding) || flags.get(DecorationDescriptorSet)) + SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. " + "Remap to location with reflection API first or disable these decorations."); + + emit_block_struct(type); + auto buffer_name = to_name(type.self); + auto instance_name = to_name(var.self); + + statement("internal::PushConstant<", buffer_name, type_to_array_glsl(type, var.self), "> ", instance_name, ";"); + statement_no_indent("#define ", instance_name, " __res->", instance_name, ".get()"); + resource_registrations.push_back(join("s.register_push_constant(", instance_name, "__", ");")); + statement(""); +} + +void CompilerCPP::emit_block_struct(SPIRType &type) +{ + // C++ can't do interface blocks, so we fake it by emitting a separate struct. + // However, these structs are not allowed to alias anything, so remove it before + // emitting the struct. + // + // The type we have here needs to be resolved to the non-pointer type so we can remove aliases. + auto &self = get(type.self); + self.type_alias = 0; + emit_struct(self); +} + +void CompilerCPP::emit_resources() +{ + for (auto &id : ir.ids) + { + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + bool needs_declaration = c.specialization || c.is_used_as_lut; + + if (needs_declaration) + { + if (!options.vulkan_semantics && c.specialization) + { + c.specialization_constant_macro_name = + constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); + } + emit_constant(c); + } + } + else if (id.get_type() == TypeConstantOp) + { + emit_specialization_constant_op(id.get()); + } + } + + // Output all basic struct types which are not Block or BufferBlock as these are declared inplace + // when such variables are instantiated. + for (auto &id : ir.ids) + { + if (id.get_type() == TypeType) + { + auto &type = id.get(); + if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer && + (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) && + !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock))) + { + emit_struct(type); + } + } + } + + statement("struct Resources : ", resource_type); + begin_scope(); + + // Output UBOs and SSBOs + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + + if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassUniform && + !is_hidden_variable(var) && + (ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock))) + { + emit_buffer_block(var); + } + } + } + + // Output push constant blocks + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + if (!is_hidden_variable(var) && var.storage != StorageClassFunction && type.pointer && + type.storage == StorageClassPushConstant) + { + emit_push_constant_block(var); + } + } + } + + // Output in/out interfaces. + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + + if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer && + (var.storage == StorageClassInput || var.storage == StorageClassOutput) && + interface_variable_exists_in_entry_point(var.self)) + { + emit_interface_block(var); + } + } + } + + // Output Uniform Constants (values, samplers, images, etc). + for (auto &id : ir.ids) + { + if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + auto &type = get(var.basetype); + + if (var.storage != StorageClassFunction && !is_hidden_variable(var) && type.pointer && + (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter)) + { + emit_uniform(var); + } + } + } + + // Global variables. + bool emitted = false; + for (auto global : global_variables) + { + auto &var = get(global); + if (var.storage == StorageClassWorkgroup) + { + emit_shared(var); + emitted = true; + } + } + + if (emitted) + statement(""); + + statement("inline void init(spirv_cross_shader& s)"); + begin_scope(); + statement(resource_type, "::init(s);"); + for (auto ® : resource_registrations) + statement(reg); + end_scope(); + resource_registrations.clear(); + + end_scope_decl(); + + statement(""); + statement("Resources* __res;"); + if (get_entry_point().model == ExecutionModelGLCompute) + statement("ComputePrivateResources __priv_res;"); + statement(""); + + // Emit regular globals which are allocated per invocation. + emitted = false; + for (auto global : global_variables) + { + auto &var = get(global); + if (var.storage == StorageClassPrivate) + { + if (var.storage == StorageClassWorkgroup) + emit_shared(var); + else + statement(CompilerGLSL::variable_decl(var), ";"); + emitted = true; + } + } + + if (emitted) + statement(""); +} + +string CompilerCPP::compile() +{ + ir.fixup_reserved_names(); + + // Do not deal with ES-isms like precision, older extensions and such. + options.es = false; + options.version = 450; + backend.float_literal_suffix = true; + backend.double_literal_suffix = false; + backend.long_long_literal_suffix = true; + backend.uint32_t_literal_suffix = true; + backend.basic_int_type = "int32_t"; + backend.basic_uint_type = "uint32_t"; + backend.swizzle_is_function = true; + backend.shared_is_implied = true; + backend.unsized_array_supported = false; + backend.explicit_struct_type = true; + backend.use_initializer_list = true; + + fixup_type_alias(); + reorder_type_alias(); + build_function_control_flow_graphs_and_analyze(); + update_active_builtins(); + + uint32_t pass_count = 0; + do + { + resource_registrations.clear(); + reset(pass_count); + + // Move constructor for this type is broken on GCC 4.9 ... + buffer.reset(); + + emit_header(); + emit_resources(); + + emit_function(get(ir.default_entry_point), Bitset()); + + pass_count++; + } while (is_forcing_recompilation()); + + // Match opening scope of emit_header(). + end_scope_decl(); + // namespace + end_scope(); + + // Emit C entry points + emit_c_linkage(); + + // Entry point in CPP is always main() for the time being. + get_entry_point().name = "main"; + + return buffer.str(); +} + +void CompilerCPP::emit_c_linkage() +{ + statement(""); + + statement("spirv_cross_shader_t *spirv_cross_construct(void)"); + begin_scope(); + statement("return new ", impl_type, "();"); + end_scope(); + + statement(""); + statement("void spirv_cross_destruct(spirv_cross_shader_t *shader)"); + begin_scope(); + statement("delete static_cast<", impl_type, "*>(shader);"); + end_scope(); + + statement(""); + statement("void spirv_cross_invoke(spirv_cross_shader_t *shader)"); + begin_scope(); + statement("static_cast<", impl_type, "*>(shader)->invoke();"); + end_scope(); + + statement(""); + statement("static const struct spirv_cross_interface vtable ="); + begin_scope(); + statement("spirv_cross_construct,"); + statement("spirv_cross_destruct,"); + statement("spirv_cross_invoke,"); + end_scope_decl(); + + statement(""); + statement("const struct spirv_cross_interface *", + interface_name.empty() ? string("spirv_cross_get_interface") : interface_name, "(void)"); + begin_scope(); + statement("return &vtable;"); + end_scope(); +} + +void CompilerCPP::emit_function_prototype(SPIRFunction &func, const Bitset &) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + local_variable_names = resource_names; + string decl; + + auto &type = get(func.return_type); + decl += "inline "; + decl += type_to_glsl(type); + decl += " "; + + if (func.self == ir.default_entry_point) + { + decl += "main"; + processing_entry_point = true; + } + else + decl += to_name(func.self); + + decl += "("; + for (auto &arg : func.arguments) + { + add_local_variable_name(arg.id); + + decl += argument_decl(arg); + if (&arg != &func.arguments.back()) + decl += ", "; + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + decl += ")"; + statement(decl); +} + +string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg) +{ + auto &type = expression_type(arg.id); + bool constref = !type.pointer || arg.write_count == 0; + + auto &var = get(arg.id); + + string base = type_to_glsl(type); + string variable_name = to_name(var.self); + remap_variable_type_name(type, variable_name, base); + + for (uint32_t i = 0; i < type.array.size(); i++) + base = join("std::array<", base, ", ", to_array_size(type, i), ">"); + + return join(constref ? "const " : "", base, " &", variable_name); +} + +string CompilerCPP::variable_decl(const SPIRType &type, const string &name, uint32_t /* id */) +{ + string base = type_to_glsl(type); + remap_variable_type_name(type, name, base); + bool runtime = false; + + for (uint32_t i = 0; i < type.array.size(); i++) + { + auto &array = type.array[i]; + if (!array && type.array_size_literal[i]) + { + // Avoid using runtime arrays with std::array since this is undefined. + // Runtime arrays cannot be passed around as values, so this is fine. + runtime = true; + } + else + base = join("std::array<", base, ", ", to_array_size(type, i), ">"); + } + base += ' '; + return base + name + (runtime ? "[1]" : ""); +} + +void CompilerCPP::emit_header() +{ + auto &execution = get_entry_point(); + + statement("// This C++ shader is autogenerated by spirv-cross."); + statement("#include \"spirv_cross/internal_interface.hpp\""); + statement("#include \"spirv_cross/external_interface.h\""); + // Needed to properly implement GLSL-style arrays. + statement("#include "); + statement("#include "); + statement(""); + statement("using namespace spirv_cross;"); + statement("using namespace glm;"); + statement(""); + + statement("namespace Impl"); + begin_scope(); + + switch (execution.model) + { + case ExecutionModelGeometry: + case ExecutionModelTessellationControl: + case ExecutionModelTessellationEvaluation: + case ExecutionModelGLCompute: + case ExecutionModelFragment: + case ExecutionModelVertex: + statement("struct Shader"); + begin_scope(); + break; + + default: + SPIRV_CROSS_THROW("Unsupported execution model."); + } + + switch (execution.model) + { + case ExecutionModelGeometry: + impl_type = "GeometryShader"; + resource_type = "GeometryResources"; + break; + + case ExecutionModelVertex: + impl_type = "VertexShader"; + resource_type = "VertexResources"; + break; + + case ExecutionModelFragment: + impl_type = "FragmentShader"; + resource_type = "FragmentResources"; + break; + + case ExecutionModelGLCompute: + impl_type = join("ComputeShader"); + resource_type = "ComputeResources"; + break; + + case ExecutionModelTessellationControl: + impl_type = "TessControlShader"; + resource_type = "TessControlResources"; + break; + + case ExecutionModelTessellationEvaluation: + impl_type = "TessEvaluationShader"; + resource_type = "TessEvaluationResources"; + break; + + default: + SPIRV_CROSS_THROW("Unsupported execution model."); + } +} diff --git a/thirdparty/SPIRV-Cross/spirv_cpp.hpp b/thirdparty/SPIRV-Cross/spirv_cpp.hpp new file mode 100644 index 000000000..c76629cdc --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cpp.hpp @@ -0,0 +1,93 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_CPP_HPP +#define SPIRV_CROSS_CPP_HPP + +#include "spirv_glsl.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +class CompilerCPP : public CompilerGLSL +{ +public: + explicit CompilerCPP(std::vector spirv_) + : CompilerGLSL(std::move(spirv_)) + { + } + + CompilerCPP(const uint32_t *ir_, size_t word_count) + : CompilerGLSL(ir_, word_count) + { + } + + explicit CompilerCPP(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + } + + explicit CompilerCPP(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + } + + std::string compile() override; + + // Sets a custom symbol name that can override + // spirv_cross_get_interface. + // + // Useful when several shader interfaces are linked + // statically into the same binary. + void set_interface_name(std::string name) + { + interface_name = std::move(name); + } + +private: + void emit_header() override; + void emit_c_linkage(); + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + + void emit_resources(); + void emit_buffer_block(const SPIRVariable &type) override; + void emit_push_constant_block(const SPIRVariable &var) override; + void emit_interface_block(const SPIRVariable &type); + void emit_block_chain(SPIRBlock &block); + void emit_uniform(const SPIRVariable &var) override; + void emit_shared(const SPIRVariable &var); + void emit_block_struct(SPIRType &type); + std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id) override; + + std::string argument_decl(const SPIRFunction::Parameter &arg); + + SmallVector resource_registrations; + std::string impl_type; + std::string resource_type; + uint32_t shared_counter = 0; + + std::string interface_name; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cross.cpp b/thirdparty/SPIRV-Cross/spirv_cross.cpp new file mode 100644 index 000000000..abddc8679 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross.cpp @@ -0,0 +1,6061 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_cross.hpp" +#include "GLSL.std.450.h" +#include "spirv_cfg.hpp" +#include "spirv_common.hpp" +#include "spirv_parser.hpp" +#include +#include +#include + +using namespace std; +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; + +Compiler::Compiler(vector ir_) +{ + Parser parser(std::move(ir_)); + parser.parse(); + set_ir(std::move(parser.get_parsed_ir())); +} + +Compiler::Compiler(const uint32_t *ir_, size_t word_count) +{ + Parser parser(ir_, word_count); + parser.parse(); + set_ir(std::move(parser.get_parsed_ir())); +} + +Compiler::Compiler(const ParsedIR &ir_) +{ + set_ir(ir_); +} + +Compiler::Compiler(ParsedIR &&ir_) +{ + set_ir(std::move(ir_)); +} + +void Compiler::set_ir(ParsedIR &&ir_) +{ + ir = std::move(ir_); + parse_fixup(); +} + +void Compiler::set_ir(const ParsedIR &ir_) +{ + ir = ir_; + parse_fixup(); +} + +string Compiler::compile() +{ + return ""; +} + +bool Compiler::variable_storage_is_aliased(const SPIRVariable &v) +{ + auto &type = get(v.basetype); + + // Untyped pointer, assume full aliasing. + if (type.basetype == SPIRType::Void) + return true; + + bool ssbo = v.storage == StorageClassStorageBuffer || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + bool image = type.basetype == SPIRType::Image; + bool counter = type.basetype == SPIRType::AtomicCounter; + bool buffer_reference = type.storage == StorageClassPhysicalStorageBuffer; + + bool is_restrict; + if (ssbo) + is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict); + else + is_restrict = has_decoration(v.self, DecorationRestrict); + + return !is_restrict && (ssbo || image || counter || buffer_reference); +} + +bool Compiler::block_is_control_dependent(const SPIRBlock &block) +{ + for (auto &i : block.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpFunctionCall: + { + uint32_t func = ops[2]; + if (function_is_control_dependent(get(func))) + return true; + break; + } + + // Derivatives + case OpDPdx: + case OpDPdxCoarse: + case OpDPdxFine: + case OpDPdy: + case OpDPdyCoarse: + case OpDPdyFine: + case OpFwidth: + case OpFwidthCoarse: + case OpFwidthFine: + + // Anything implicit LOD + case OpImageSampleImplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageQueryLod: + case OpImageDrefGather: + case OpImageGather: + case OpImageSparseDrefGather: + case OpImageSparseGather: + + // Anything subgroups + case OpGroupNonUniformElect: + case OpGroupNonUniformAll: + case OpGroupNonUniformAny: + case OpGroupNonUniformAllEqual: + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformBroadcastFirst: + case OpGroupNonUniformBallot: + case OpGroupNonUniformInverseBallot: + case OpGroupNonUniformBallotBitExtract: + case OpGroupNonUniformBallotBitCount: + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + case OpGroupNonUniformIAdd: + case OpGroupNonUniformFAdd: + case OpGroupNonUniformIMul: + case OpGroupNonUniformFMul: + case OpGroupNonUniformSMin: + case OpGroupNonUniformUMin: + case OpGroupNonUniformFMin: + case OpGroupNonUniformSMax: + case OpGroupNonUniformUMax: + case OpGroupNonUniformFMax: + case OpGroupNonUniformBitwiseAnd: + case OpGroupNonUniformBitwiseOr: + case OpGroupNonUniformBitwiseXor: + case OpGroupNonUniformLogicalAnd: + case OpGroupNonUniformLogicalOr: + case OpGroupNonUniformLogicalXor: + case OpGroupNonUniformQuadBroadcast: + case OpGroupNonUniformQuadSwap: + case OpGroupNonUniformRotateKHR: + + // Control barriers + case OpControlBarrier: + return true; + + default: + break; + } + } + + return false; +} + +bool Compiler::block_is_pure(const SPIRBlock &block) +{ + // This is a global side effect of the function. + if (block.terminator == SPIRBlock::Kill || + block.terminator == SPIRBlock::TerminateRay || + block.terminator == SPIRBlock::IgnoreIntersection || + block.terminator == SPIRBlock::EmitMeshTasks) + return false; + + for (auto &i : block.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpFunctionCall: + { + uint32_t func = ops[2]; + if (!function_is_pure(get(func))) + return false; + break; + } + + case OpCopyMemory: + case OpStore: + case OpCooperativeMatrixStoreKHR: + { + auto &type = expression_type(ops[0]); + if (type.storage != StorageClassFunction) + return false; + break; + } + + case OpImageWrite: + return false; + + // Atomics are impure. + case OpAtomicLoad: + case OpAtomicStore: + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + return false; + + // Geometry shader builtins modify global state. + case OpEndPrimitive: + case OpEmitStreamVertex: + case OpEndStreamPrimitive: + case OpEmitVertex: + return false; + + // Mesh shader functions modify global state. + // (EmitMeshTasks is a terminator). + case OpSetMeshOutputsEXT: + return false; + + // Barriers disallow any reordering, so we should treat blocks with barrier as writing. + case OpControlBarrier: + case OpMemoryBarrier: + return false; + + // Ray tracing builtins are impure. + case OpReportIntersectionKHR: + case OpIgnoreIntersectionNV: + case OpTerminateRayNV: + case OpTraceNV: + case OpTraceRayKHR: + case OpExecuteCallableNV: + case OpExecuteCallableKHR: + case OpRayQueryInitializeKHR: + case OpRayQueryTerminateKHR: + case OpRayQueryGenerateIntersectionKHR: + case OpRayQueryConfirmIntersectionKHR: + case OpRayQueryProceedKHR: + // There are various getters in ray query, but they are considered pure. + return false; + + // OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure. + + case OpDemoteToHelperInvocationEXT: + // This is a global side effect of the function. + return false; + + case OpTensorReadARM: + return false; + + case OpExtInst: + { + uint32_t extension_set = ops[2]; + if (get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(ops[3]); + switch (op_450) + { + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + auto &type = expression_type(ops[5]); + if (type.storage != StorageClassFunction) + return false; + break; + } + + default: + break; + } + } + break; + } + + default: + break; + } + } + + return true; +} + +string Compiler::to_name(uint32_t id, bool allow_alias) const +{ + if (allow_alias && ir.ids[id].get_type() == TypeType) + { + // If this type is a simple alias, emit the + // name of the original type instead. + // We don't want to override the meta alias + // as that can be overridden by the reflection APIs after parse. + auto &type = get(id); + if (type.type_alias) + { + // If the alias master has been specially packed, we will have emitted a clean variant as well, + // so skip the name aliasing here. + if (!has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + return to_name(type.type_alias); + } + } + + auto &alias = ir.get_name(id); + if (alias.empty()) + return join("_", id); + else + return alias; +} + +bool Compiler::function_is_pure(const SPIRFunction &func) +{ + for (auto block : func.blocks) + if (!block_is_pure(get(block))) + return false; + + return true; +} + +bool Compiler::function_is_control_dependent(const SPIRFunction &func) +{ + for (auto block : func.blocks) + if (block_is_control_dependent(get(block))) + return true; + + return false; +} + +void Compiler::register_global_read_dependencies(const SPIRBlock &block, uint32_t id) +{ + for (auto &i : block.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpFunctionCall: + { + uint32_t func = ops[2]; + register_global_read_dependencies(get(func), id); + break; + } + + case OpLoad: + case OpCooperativeMatrixLoadKHR: + case OpCooperativeVectorLoadNV: + case OpImageRead: + { + // If we're in a storage class which does not get invalidated, adding dependencies here is no big deal. + auto *var = maybe_get_backing_variable(ops[2]); + if (var && var->storage != StorageClassFunction) + { + auto &type = get(var->basetype); + + // InputTargets are immutable. + if (type.basetype != SPIRType::Image && type.image.dim != DimSubpassData) + var->dependees.push_back(id); + } + break; + } + + default: + break; + } + } +} + +void Compiler::register_global_read_dependencies(const SPIRFunction &func, uint32_t id) +{ + for (auto block : func.blocks) + register_global_read_dependencies(get(block), id); +} + +SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain) +{ + auto *var = maybe_get(chain); + if (!var) + { + auto *cexpr = maybe_get(chain); + if (cexpr) + { + var = maybe_get(cexpr->loaded_from); + if (!var && cexpr->loaded_from != chain) + var = maybe_get_backing_variable(cexpr->loaded_from); + } + + auto *access_chain = maybe_get(chain); + if (access_chain) + { + var = maybe_get(access_chain->loaded_from); + if (!var && access_chain->loaded_from != chain) + var = maybe_get_backing_variable(access_chain->loaded_from); + } + } + + return var; +} + +SPIRExpression *Compiler::maybe_get_backing_buffer_pointer(uint32_t chain) +{ + auto *expr = maybe_get(chain); + while (expr && !expr->buffer_pointer && expr->loaded_from) + expr = maybe_get(expr->loaded_from); + return expr && expr->buffer_pointer ? expr : nullptr; +} + +void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded) +{ + auto &e = get(expr); + auto *var = maybe_get_backing_variable(chain); + auto *buffer_pointer = maybe_get_backing_buffer_pointer(chain); + + if (var) + { + e.loaded_from = var->self; + + // If the backing variable is immutable, we do not need to depend on the variable. + if (forwarded && !is_immutable(var->self)) + var->dependees.push_back(e.self); + + // If we load from a parameter, make sure we create "inout" if we also write to the parameter. + // The default is "in" however, so we never invalidate our compilation by reading. + if (var && var->parameter) + var->parameter->read_count++; + } + else if (buffer_pointer) + { + e.loaded_from = buffer_pointer->self; + // If the backing variable is immutable, we do not need to depend on the variable. + if (forwarded && !is_immutable(buffer_pointer->self)) + buffer_pointer->buffer_pointer_dependees.push_back(e.self); + } +} + +void Compiler::register_write(uint32_t chain) +{ + auto *var = maybe_get(chain); + if (!var) + { + // If we're storing through an access chain, invalidate the backing variable instead. + auto *expr = maybe_get(chain); + if (expr && expr->loaded_from) + var = maybe_get(expr->loaded_from); + + auto *access_chain = maybe_get(chain); + if (access_chain && access_chain->loaded_from) + var = maybe_get(access_chain->loaded_from); + } + + auto *buffer_pointer = maybe_get_backing_buffer_pointer(chain); + + auto &chain_type = expression_type(chain); + + if (var) + { + bool check_argument_storage_qualifier = true; + auto &type = expression_type(chain); + + // If our variable is in a storage class which can alias with other buffers, + // invalidate all variables which depend on aliased variables. And if this is a + // variable pointer, then invalidate all variables regardless. + if (get_variable_data_type(*var).pointer) + { + flush_all_active_variables(); + + if (type.pointer_depth == 1) + { + // We have a backing variable which is a pointer-to-pointer type. + // We are storing some data through a pointer acquired through that variable, + // but we are not writing to the value of the variable itself, + // i.e., we are not modifying the pointer directly. + // If we are storing a non-pointer type (pointer_depth == 1), + // we know that we are storing some unrelated data. + // A case here would be + // void foo(Foo * const *arg) { + // Foo *bar = *arg; + // bar->unrelated = 42; + // } + // arg, the argument is constant. + check_argument_storage_qualifier = false; + } + } + + if (type.storage == StorageClassPhysicalStorageBuffer || variable_storage_is_aliased(*var)) + flush_all_aliased_variables(); + else if (var) + flush_dependees(*var); + + // We tried to write to a parameter which is not marked with out qualifier, force a recompile. + if (check_argument_storage_qualifier && var->parameter && var->parameter->write_count == 0) + { + var->parameter->write_count++; + force_recompile(); + } + } + else if (buffer_pointer) + { + flush_dependees(*buffer_pointer); + } + else if (chain_type.pointer) + { + // If we stored through a variable pointer, then we don't know which + // variable we stored to. So *all* expressions after this point need to + // be invalidated. + // FIXME: If we can prove that the variable pointer will point to + // only certain variables, we can invalidate only those. + flush_all_active_variables(); + } + + // If chain_type.pointer is false, we're not writing to memory backed variables, but temporaries instead. + // This can happen in copy_logical_type where we unroll complex reads and writes to temporaries. +} + +void Compiler::flush_dependees(SPIRVariable &var) +{ + for (auto expr : var.dependees) + invalid_expressions.insert(expr); + var.dependees.clear(); +} + +void Compiler::flush_dependees(SPIRExpression &expr) +{ + // A little ugly to split things up like this since BufferPointerEXT is a weird case + // where it's both an expression (chain into global heap) and a memory declaration at the same time ... + assert(expr.buffer_pointer); + for (auto dep : expr.buffer_pointer_dependees) + invalid_expressions.insert(dep); + expr.buffer_pointer_dependees.clear(); +} + +void Compiler::flush_all_aliased_variables() +{ + for (auto aliased : aliased_variables) + flush_dependees(get(aliased)); +} + +void Compiler::flush_all_atomic_capable_variables() +{ + for (auto global : global_variables) + flush_dependees(get(global)); + for (auto global : buffer_pointer_variables) + flush_dependees(get(global)); + flush_all_aliased_variables(); +} + +void Compiler::flush_control_dependent_expressions(uint32_t block_id) +{ + auto &block = get(block_id); + for (auto &expr : block.invalidate_expressions) + invalid_expressions.insert(expr); + block.invalidate_expressions.clear(); +} + +void Compiler::flush_all_active_variables() +{ + // Invalidate all temporaries we read from variables in this block since they were forwarded. + // Invalidate all temporaries we read from globals. + for (auto &v : current_function->local_variables) + flush_dependees(get(v)); + for (auto &arg : current_function->arguments) + flush_dependees(get(arg.id)); + for (auto global : global_variables) + flush_dependees(get(global)); + for (auto global : buffer_pointer_variables) + flush_dependees(get(global)); + + flush_all_aliased_variables(); +} + +uint32_t Compiler::expression_type_id(uint32_t id) const +{ + switch (ir.ids[id].get_type()) + { + case TypeVariable: + return get(id).basetype; + + case TypeExpression: + return get(id).expression_type; + + case TypeConstant: + return get(id).constant_type; + + case TypeConstantOp: + return get(id).basetype; + + case TypeUndef: + return get(id).basetype; + + case TypeCombinedImageSampler: + return get(id).combined_type; + + case TypeAccessChain: + return get(id).basetype; + + default: + SPIRV_CROSS_THROW("Cannot resolve expression type."); + } +} + +const SPIRType &Compiler::expression_type(uint32_t id) const +{ + return get(expression_type_id(id)); +} + +bool Compiler::expression_is_lvalue(uint32_t id) const +{ + auto &type = expression_type(id); + + switch (type.basetype) + { + case SPIRType::SampledImage: + case SPIRType::Image: + case SPIRType::Sampler: + return false; + + default: + return true; + } +} + +bool Compiler::is_immutable(uint32_t id) const +{ + if (ir.ids[id].get_type() == TypeVariable) + { + auto &var = get(id); + + // Anything we load from the UniformConstant address space is guaranteed to be immutable. + bool pointer_to_const = var.storage == StorageClassUniformConstant; + return pointer_to_const || var.phi_variable || !expression_is_lvalue(id); + } + else if (ir.ids[id].get_type() == TypeAccessChain) + return get(id).immutable; + else if (ir.ids[id].get_type() == TypeExpression) + return get(id).immutable; + else if (ir.ids[id].get_type() == TypeConstant || ir.ids[id].get_type() == TypeConstantOp || + ir.ids[id].get_type() == TypeUndef) + return true; + else + return false; +} + +static inline bool storage_class_is_interface(StorageClass storage) +{ + switch (storage) + { + case StorageClassInput: + case StorageClassOutput: + case StorageClassUniform: + case StorageClassUniformConstant: + case StorageClassAtomicCounter: + case StorageClassPushConstant: + case StorageClassStorageBuffer: + return true; + + default: + return false; + } +} + +bool Compiler::is_hidden_variable(const SPIRVariable &var, bool include_builtins) const +{ + if ((is_builtin_variable(var) && !include_builtins) || var.remapped_variable) + return true; + + // Combined image samplers are always considered active as they are "magic" variables. + if (find_if(begin(combined_image_samplers), end(combined_image_samplers), [&var](const CombinedImageSampler &samp) { + return samp.combined_id == var.self; + }) != end(combined_image_samplers)) + { + return false; + } + + // In SPIR-V 1.4 and up we must also use the active variable interface to disable global variables + // which are not part of the entry point. + if (ir.get_spirv_version() >= 0x10400 && var.storage != StorageClassGeneric && + var.storage != StorageClassFunction && !interface_variable_exists_in_entry_point(var.self)) + { + return true; + } + + return check_active_interface_variables && storage_class_is_interface(var.storage) && + active_interface_variables.find(var.self) == end(active_interface_variables); +} + +bool Compiler::is_builtin_type(const SPIRType &type) const +{ + auto *type_meta = ir.find_meta(type.self); + + // We can have builtin structs as well. If one member of a struct is builtin, the struct must also be builtin. + if (type_meta) + for (auto &m : type_meta->members) + if (m.builtin) + return true; + + return false; +} + +bool Compiler::is_builtin_variable(const SPIRVariable &var) const +{ + auto *m = ir.find_meta(var.self); + + if (var.compat_builtin || (m && m->decoration.builtin)) + return true; + else + return is_builtin_type(get(var.basetype)); +} + +bool Compiler::is_member_builtin(const SPIRType &type, uint32_t index, BuiltIn *builtin) const +{ + auto *type_meta = ir.find_meta(type.self); + + if (type_meta) + { + auto &memb = type_meta->members; + if (index < memb.size() && memb[index].builtin) + { + if (builtin) + *builtin = memb[index].builtin_type; + return true; + } + } + + return false; +} + +bool Compiler::is_scalar(const SPIRType &type) const +{ + return type.basetype != SPIRType::Struct && type.vecsize == 1 && type.columns == 1; +} + +bool Compiler::is_vector(const SPIRType &type) const +{ + return type.vecsize > 1 && type.columns == 1; +} + +bool Compiler::is_matrix(const SPIRType &type) const +{ + return type.vecsize > 1 && type.columns > 1; +} + +bool Compiler::is_array(const SPIRType &type) const +{ + return type.op == OpTypeArray || type.op == OpTypeRuntimeArray; +} + +bool Compiler::is_pointer(const SPIRType &type) const +{ + return (type.op == OpTypePointer || type.op == OpTypeUntypedPointerKHR) && type.basetype != SPIRType::Unknown; // Ignore function pointers. +} + +bool Compiler::is_physical_pointer(const SPIRType &type) const +{ + return (type.op == OpTypePointer || type.op == OpTypeUntypedPointerKHR) && type.storage == StorageClassPhysicalStorageBuffer; +} + +bool Compiler::is_physical_or_buffer_pointer(const SPIRType &type) const +{ + return (type.op == OpTypePointer || type.op == OpTypeUntypedPointerKHR) && + (type.storage == StorageClassPhysicalStorageBuffer || type.storage == StorageClassUniform || + type.storage == StorageClassStorageBuffer || type.storage == StorageClassWorkgroup || + type.storage == StorageClassPushConstant); +} + +bool Compiler::is_physical_pointer_to_buffer_block(const SPIRType &type) const +{ + return is_physical_pointer(type) && get_pointee_type(type).self == type.parent_type && + (has_decoration(type.self, DecorationBlock) || + has_decoration(type.self, DecorationBufferBlock)); +} + +bool Compiler::is_runtime_size_array(const SPIRType &type) +{ + return type.op == OpTypeRuntimeArray; +} + +ShaderResources Compiler::get_shader_resources() const +{ + return get_shader_resources(nullptr); +} + +ShaderResources Compiler::get_shader_resources(const unordered_set &active_variables) const +{ + return get_shader_resources(&active_variables); +} + +bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + uint32_t variable = 0; + switch (opcode) + { + // Need this first, otherwise, GCC complains about unhandled switch statements. + default: + break; + + case OpFunctionCall: + { + // Invalid SPIR-V. + if (length < 3) + return false; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + { + auto *var = compiler.maybe_get(args[i]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[i]); + } + break; + } + + case OpSelect: + { + // Invalid SPIR-V. + if (length < 5) + return false; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + { + auto *var = compiler.maybe_get(args[i]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[i]); + } + break; + } + + case OpPhi: + { + // Invalid SPIR-V. + if (length < 2) + return false; + + uint32_t count = length - 2; + args += 2; + for (uint32_t i = 0; i < count; i += 2) + { + auto *var = compiler.maybe_get(args[i]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[i]); + } + break; + } + + case OpAtomicStore: + case OpStore: + case OpCooperativeMatrixStoreKHR: + // Invalid SPIR-V. + if (length < 1) + return false; + variable = args[0]; + break; + + case OpCopyMemory: + { + if (length < 2) + return false; + + auto *var = compiler.maybe_get(args[0]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[0]); + + var = compiler.maybe_get(args[1]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[1]); + break; + } + + case OpExtInst: + { + if (length < 3) + return false; + auto &extension_set = compiler.get(args[2]); + switch (extension_set.ext) + { + case SPIRExtension::GLSL: + { + auto op = static_cast(args[3]); + + switch (op) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + auto *var = compiler.maybe_get(args[4]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[4]); + break; + } + + case GLSLstd450Modf: + case GLSLstd450Fract: + { + auto *var = compiler.maybe_get(args[5]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[5]); + break; + } + + default: + break; + } + break; + } + case SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter: + { + enum AMDShaderExplicitVertexParameter + { + InterpolateAtVertexAMD = 1 + }; + + auto op = static_cast(args[3]); + + switch (op) + { + case InterpolateAtVertexAMD: + { + auto *var = compiler.maybe_get(args[4]); + if (var && storage_class_is_interface(var->storage)) + variables.insert(args[4]); + break; + } + + default: + break; + } + break; + } + default: + break; + } + break; + } + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + case OpLoad: + case OpCooperativeMatrixLoadKHR: + case OpCopyObject: + case OpImageTexelPointer: + case OpAtomicLoad: + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + case OpArrayLength: + // Invalid SPIR-V. + if (length < 3) + return false; + variable = args[2]; + break; + } + + if (variable) + { + auto *var = compiler.maybe_get(variable); + if (var && storage_class_is_interface(var->storage)) + variables.insert(variable); + } + return true; +} + +unordered_set Compiler::get_active_interface_variables() const +{ + // Traverse the call graph and find all interface variables which are in use. + unordered_set variables; + InterfaceVariableAccessHandler handler(*this, variables); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (var.storage != StorageClassOutput) + return; + if (!interface_variable_exists_in_entry_point(var.self)) + return; + + // An output variable which is just declared (but uninitialized) might be read by subsequent stages + // so we should force-enable these outputs, + // since compilation will fail if a subsequent stage attempts to read from the variable in question. + // Also, make sure we preserve output variables which are only initialized, but never accessed by any code. + if (var.initializer != ID(0) || get_execution_model() != ExecutionModelFragment) + variables.insert(var.self); + }); + + // If we needed to create one, we'll need it. + if (dummy_sampler_id) + variables.insert(dummy_sampler_id); + + return variables; +} + +void Compiler::set_enabled_interface_variables(std::unordered_set active_variables) +{ + active_interface_variables = std::move(active_variables); + check_active_interface_variables = true; +} + +ShaderResources Compiler::get_shader_resources(const unordered_set *active_variables) const +{ + ShaderResources res; + + bool ssbo_instance_name = reflection_ssbo_instance_name_is_significant(); + + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + auto &type = this->get(var.basetype); + + // It is possible for uniform storage classes to be passed as function parameters, so detect + // that. To detect function parameters, check of StorageClass of variable is function scope. + if (var.storage == StorageClassFunction || !type.pointer) + return; + + if (active_variables && active_variables->find(var.self) == end(*active_variables)) + return; + + // In SPIR-V 1.4 and up, every global must be present in the entry point interface list, + // not just IO variables. + bool active_in_entry_point = true; + if (ir.get_spirv_version() < 0x10400) + { + if (var.storage == StorageClassInput || var.storage == StorageClassOutput) + active_in_entry_point = interface_variable_exists_in_entry_point(var.self); + } + else + active_in_entry_point = interface_variable_exists_in_entry_point(var.self); + + if (!active_in_entry_point) + return; + + bool is_builtin = is_builtin_variable(var); + + if (is_builtin) + { + if (var.storage != StorageClassInput && var.storage != StorageClassOutput) + return; + + auto &list = var.storage == StorageClassInput ? res.builtin_inputs : res.builtin_outputs; + BuiltInResource resource; + + if (has_decoration(type.self, DecorationBlock)) + { + resource.resource = { var.self, var.basetype, type.self, + get_remapped_declared_block_name(var.self, false) }; + + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + resource.value_type_id = type.member_types[i]; + resource.builtin = BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn)); + list.push_back(resource); + } + } + else + { + bool strip_array = + !has_decoration(var.self, DecorationPatch) && ( + get_execution_model() == ExecutionModelTessellationControl || + (get_execution_model() == ExecutionModelTessellationEvaluation && + var.storage == StorageClassInput)); + + resource.resource = { var.self, var.basetype, type.self, get_name(var.self) }; + + if (strip_array && !type.array.empty()) + resource.value_type_id = get_variable_data_type(var).parent_type; + else + resource.value_type_id = get_variable_data_type_id(var); + + assert(resource.value_type_id); + + resource.builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + list.push_back(std::move(resource)); + } + return; + } + + // Input + if (var.storage == StorageClassInput) + { + if (has_decoration(type.self, DecorationBlock)) + { + res.stage_inputs.push_back( + { var.self, var.basetype, type.self, + get_remapped_declared_block_name(var.self, false) }); + } + else + res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Subpass inputs + else if (var.storage == StorageClassUniformConstant && type.image.dim == DimSubpassData) + { + res.subpass_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Outputs + else if (var.storage == StorageClassOutput) + { + if (has_decoration(type.self, DecorationBlock)) + { + res.stage_outputs.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) }); + } + else + res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // UBOs + else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBlock)) + { + res.uniform_buffers.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) }); + } + // Old way to declare SSBOs. + else if (type.storage == StorageClassUniform && has_decoration(type.self, DecorationBufferBlock)) + { + res.storage_buffers.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, ssbo_instance_name) }); + } + // Modern way to declare SSBOs. + else if (type.storage == StorageClassStorageBuffer) + { + res.storage_buffers.push_back( + { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, ssbo_instance_name) }); + } + // Push constant blocks + else if (type.storage == StorageClassPushConstant) + { + // There can only be one push constant block, but keep the vector in case this restriction is lifted + // in the future. + res.push_constant_buffers.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + else if (type.storage == StorageClassShaderRecordBufferKHR) + { + res.shader_record_buffers.push_back({ var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, ssbo_instance_name) }); + } + // Atomic counters + else if (type.storage == StorageClassAtomicCounter) + { + res.atomic_counters.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + else if (type.storage == StorageClassUniformConstant) + { + if (type.basetype == SPIRType::Image) + { + // Images + if (type.image.sampled == 2) + { + res.storage_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Separate images + else if (type.image.sampled == 1) + { + res.separate_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + } + // Separate samplers + else if (type.basetype == SPIRType::Sampler) + { + res.separate_samplers.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Textures + else if (type.basetype == SPIRType::SampledImage) + { + res.sampled_images.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Acceleration structures + else if (type.basetype == SPIRType::AccelerationStructure) + { + res.acceleration_structures.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + // Tensors + else if (type.basetype == SPIRType::Tensor) + { + res.tensors.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + else + { + res.gl_plain_uniforms.push_back({ var.self, var.basetype, type.self, get_name(var.self) }); + } + } + }); + + return res; +} + +bool Compiler::type_is_top_level_block(const SPIRType &type) const +{ + if (type.basetype != SPIRType::Struct) + return false; + return has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); +} + +bool Compiler::type_is_explicit_layout(const SPIRType &type) const +{ + if (type.basetype == SPIRType::Struct) + { + // Block-like types may have Offset decorations. + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + if (has_member_decoration(type.self, i, DecorationOffset)) + return true; + } + + return false; +} + +bool Compiler::type_is_block_like(const SPIRType &type) const +{ + if (type_is_top_level_block(type)) + return true; + else + return type_is_explicit_layout(type); +} + +void Compiler::parse_fixup() +{ + // Figure out specialization constants for work group sizes. + for (auto id_ : ir.ids_for_constant_or_variable) + { + auto &id = ir.ids[id_]; + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + if (has_decoration(c.self, DecorationBuiltIn) && + BuiltIn(get_decoration(c.self, DecorationBuiltIn)) == BuiltInWorkgroupSize) + { + // In current SPIR-V, there can be just one constant like this. + // All entry points will receive the constant value. + // WorkgroupSize take precedence over LocalSizeId. + for (auto &entry : ir.entry_points) + { + entry.second.workgroup_size.constant = c.self; + entry.second.workgroup_size.x = c.scalar(0, 0); + entry.second.workgroup_size.y = c.scalar(0, 1); + entry.second.workgroup_size.z = c.scalar(0, 2); + } + } + } + else if (id.get_type() == TypeVariable) + { + auto &var = id.get(); + if (var.storage == StorageClassPrivate || var.storage == StorageClassWorkgroup || + var.storage == StorageClassTaskPayloadWorkgroupEXT || + var.storage == StorageClassOutput) + { + global_variables.push_back(var.self); + } + if (variable_storage_is_aliased(var)) + aliased_variables.push_back(var.self); + } + } +} + +void Compiler::update_name_cache(unordered_set &cache_primary, const unordered_set &cache_secondary, + string &name) +{ + if (name.empty()) + return; + + const auto find_name = [&](const string &n) -> bool { + if (cache_primary.find(n) != end(cache_primary)) + return true; + + if (&cache_primary != &cache_secondary) + if (cache_secondary.find(n) != end(cache_secondary)) + return true; + + return false; + }; + + const auto insert_name = [&](const string &n) { cache_primary.insert(n); }; + + if (!find_name(name)) + { + insert_name(name); + return; + } + + uint32_t counter = 0; + auto tmpname = name; + + bool use_linked_underscore = true; + + if (tmpname == "_") + { + // We cannot just append numbers, as we will end up creating internally reserved names. + // Make it like _0_ instead. + tmpname += "0"; + } + else if (tmpname.back() == '_') + { + // The last_character is an underscore, so we don't need to link in underscore. + // This would violate double underscore rules. + use_linked_underscore = false; + } + + // If there is a collision (very rare), + // keep tacking on extra identifier until it's unique. + do + { + counter++; + name = tmpname + (use_linked_underscore ? "_" : "") + convert_to_string(counter); + } while (find_name(name)); + insert_name(name); +} + +void Compiler::update_name_cache(unordered_set &cache, string &name) +{ + update_name_cache(cache, cache, name); +} + +void Compiler::set_name(ID id, const std::string &name) +{ + ir.set_name(id, name); +} + +const SPIRType &Compiler::get_type(TypeID id) const +{ + return get(id); +} + +const SPIRType &Compiler::get_type_from_variable(VariableID id) const +{ + return get(get(id).basetype); +} + +uint32_t Compiler::get_pointee_type_id(uint32_t type_id) const +{ + auto *p_type = &get(type_id); + if (p_type->pointer) + { + assert(p_type->parent_type); + type_id = p_type->parent_type; + } + return type_id; +} + +const SPIRType &Compiler::get_pointee_type(const SPIRType &type) const +{ + auto *p_type = &type; + if (p_type->pointer) + { + assert(p_type->parent_type); + p_type = &get(p_type->parent_type); + } + return *p_type; +} + +const SPIRType &Compiler::get_pointee_type(uint32_t type_id) const +{ + return get_pointee_type(get(type_id)); +} + +uint32_t Compiler::get_variable_data_type_id(const SPIRVariable &var) const +{ + if (var.phi_variable || var.storage == StorageClassAtomicCounter) + return var.basetype; + return get_pointee_type_id(var.basetype); +} + +SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var) +{ + return get(get_variable_data_type_id(var)); +} + +const SPIRType &Compiler::get_variable_data_type(const SPIRVariable &var) const +{ + return get(get_variable_data_type_id(var)); +} + +SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var) +{ + SPIRType *type = &get_variable_data_type(var); + if (is_array(*type)) + type = &get(type->parent_type); + return *type; +} + +const SPIRType &Compiler::get_variable_element_type(const SPIRVariable &var) const +{ + const SPIRType *type = &get_variable_data_type(var); + if (is_array(*type)) + type = &get(type->parent_type); + return *type; +} + +bool Compiler::is_sampled_image_type(const SPIRType &type) +{ + return (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage) && type.image.sampled == 1 && + type.image.dim != DimBuffer; +} + +void Compiler::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, + const std::string &argument) +{ + ir.set_member_decoration_string(id, index, decoration, argument); +} + +void Compiler::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument) +{ + ir.set_member_decoration(id, index, decoration, argument); +} + +void Compiler::set_member_name(TypeID id, uint32_t index, const std::string &name) +{ + ir.set_member_name(id, index, name); +} + +const std::string &Compiler::get_member_name(TypeID id, uint32_t index) const +{ + return ir.get_member_name(id, index); +} + +void Compiler::set_qualified_name(uint32_t id, const string &name) +{ + ir.meta[id].decoration.qualified_alias = name; +} + +void Compiler::set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name) +{ + ir.meta[type_id].members.resize(max(ir.meta[type_id].members.size(), size_t(index) + 1)); + ir.meta[type_id].members[index].qualified_alias = name; +} + +const string &Compiler::get_member_qualified_name(TypeID type_id, uint32_t index) const +{ + auto *m = ir.find_meta(type_id); + if (m && index < m->members.size()) + return m->members[index].qualified_alias; + else + return ir.get_empty_string(); +} + +uint32_t Compiler::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + return ir.get_member_decoration(id, index, decoration); +} + +const Bitset &Compiler::get_member_decoration_bitset(TypeID id, uint32_t index) const +{ + return ir.get_member_decoration_bitset(id, index); +} + +bool Compiler::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + return ir.has_member_decoration(id, index, decoration); +} + +void Compiler::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration) +{ + ir.unset_member_decoration(id, index, decoration); +} + +void Compiler::set_decoration_string(ID id, Decoration decoration, const std::string &argument) +{ + ir.set_decoration_string(id, decoration, argument); +} + +void Compiler::set_decoration(ID id, Decoration decoration, uint32_t argument) +{ + ir.set_decoration(id, decoration, argument); +} + +void Compiler::set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value) +{ + auto &dec = ir.meta[id].decoration; + dec.extended.flags.set(decoration); + dec.extended.values[decoration] = value; +} + +void Compiler::set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration, + uint32_t value) +{ + ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1)); + auto &dec = ir.meta[type].members[index]; + dec.extended.flags.set(decoration); + dec.extended.values[decoration] = value; +} + +static uint32_t get_default_extended_decoration(ExtendedDecorations decoration) +{ + switch (decoration) + { + case SPIRVCrossDecorationResourceIndexPrimary: + case SPIRVCrossDecorationResourceIndexSecondary: + case SPIRVCrossDecorationResourceIndexTertiary: + case SPIRVCrossDecorationResourceIndexQuaternary: + case SPIRVCrossDecorationInterfaceMemberIndex: + return ~(0u); + + default: + return 0; + } +} + +uint32_t Compiler::get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(id); + if (!m) + return 0; + + auto &dec = m->decoration; + + if (!dec.extended.flags.get(decoration)) + return get_default_extended_decoration(decoration); + + return dec.extended.values[decoration]; +} + +uint32_t Compiler::get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(type); + if (!m) + return 0; + + if (index >= m->members.size()) + return 0; + + auto &dec = m->members[index]; + if (!dec.extended.flags.get(decoration)) + return get_default_extended_decoration(decoration); + return dec.extended.values[decoration]; +} + +bool Compiler::has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(id); + if (!m) + return false; + + auto &dec = m->decoration; + return dec.extended.flags.get(decoration); +} + +bool Compiler::has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const +{ + auto *m = ir.find_meta(type); + if (!m) + return false; + + if (index >= m->members.size()) + return false; + + auto &dec = m->members[index]; + return dec.extended.flags.get(decoration); +} + +void Compiler::unset_extended_decoration(uint32_t id, ExtendedDecorations decoration) +{ + auto &dec = ir.meta[id].decoration; + dec.extended.flags.clear(decoration); + dec.extended.values[decoration] = 0; +} + +void Compiler::unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) +{ + ir.meta[type].members.resize(max(ir.meta[type].members.size(), size_t(index) + 1)); + auto &dec = ir.meta[type].members[index]; + dec.extended.flags.clear(decoration); + dec.extended.values[decoration] = 0; +} + +StorageClass Compiler::get_storage_class(VariableID id) const +{ + return get(id).storage; +} + +const std::string &Compiler::get_name(ID id) const +{ + return ir.get_name(id); +} + +const std::string Compiler::get_fallback_name(ID id) const +{ + return join("_", id); +} + +const std::string Compiler::get_block_fallback_name(VariableID id) const +{ + auto &var = get(id); + if (get_name(id).empty()) + return join("_", get(var.basetype).self, "_", id); + else + return get_name(id); +} + +const Bitset &Compiler::get_decoration_bitset(ID id) const +{ + return ir.get_decoration_bitset(id); +} + +bool Compiler::has_decoration(ID id, Decoration decoration) const +{ + return ir.has_decoration(id, decoration); +} + +const string &Compiler::get_decoration_string(ID id, Decoration decoration) const +{ + return ir.get_decoration_string(id, decoration); +} + +const string &Compiler::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const +{ + return ir.get_member_decoration_string(id, index, decoration); +} + +uint32_t Compiler::get_decoration(ID id, Decoration decoration) const +{ + return ir.get_decoration(id, decoration); +} + +void Compiler::unset_decoration(ID id, Decoration decoration) +{ + ir.unset_decoration(id, decoration); +} + +bool Compiler::get_binary_offset_for_decoration(VariableID id, Decoration decoration, uint32_t &word_offset) const +{ + auto *m = ir.find_meta(id); + if (!m) + return false; + + auto &word_offsets = m->decoration_word_offset; + auto itr = word_offsets.find(decoration); + if (itr == end(word_offsets)) + return false; + + word_offset = itr->second; + return true; +} + +bool Compiler::block_is_noop(const SPIRBlock &block) const +{ + if (block.terminator != SPIRBlock::Direct) + return false; + + auto &child = get(block.next_block); + + // If this block participates in PHI, the block isn't really noop. + for (auto &phi : block.phi_variables) + if (phi.parent == block.self || phi.parent == child.self) + return false; + + for (auto &phi : child.phi_variables) + if (phi.parent == block.self) + return false; + + // Verify all instructions have no semantic impact. + for (auto &i : block.ops) + { + auto op = static_cast(i.op); + + switch (op) + { + // Non-Semantic instructions. + case OpLine: + case OpNoLine: + break; + + case OpExtInst: + { + auto *ops = stream(i); + auto ext = get(ops[2]).ext; + + bool ext_is_nonsemantic_only = + ext == SPIRExtension::NonSemanticShaderDebugInfo || + ext == SPIRExtension::SPV_debug_info || + ext == SPIRExtension::NonSemanticGeneric; + + if (!ext_is_nonsemantic_only) + return false; + + break; + } + + default: + return false; + } + } + + return true; +} + +bool Compiler::block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const +{ + // Tried and failed. + if (block.disable_block_optimization || block.complex_continue) + return false; + + if (method == SPIRBlock::MergeToSelectForLoop || method == SPIRBlock::MergeToSelectContinueForLoop) + { + // Try to detect common for loop pattern + // which the code backend can use to create cleaner code. + // for(;;) { if (cond) { some_body; } else { break; } } + // is the pattern we're looking for. + const auto *false_block = maybe_get(block.false_block); + const auto *true_block = maybe_get(block.true_block); + const auto *merge_block = maybe_get(block.merge_block); + + bool false_block_is_merge = block.false_block == block.merge_block || + (false_block && merge_block && execution_is_noop(*false_block, *merge_block)); + + bool true_block_is_merge = block.true_block == block.merge_block || + (true_block && merge_block && execution_is_noop(*true_block, *merge_block)); + + bool positive_candidate = + block.true_block != block.merge_block && block.true_block != block.self && false_block_is_merge; + + bool negative_candidate = + block.false_block != block.merge_block && block.false_block != block.self && true_block_is_merge; + + bool ret = block.terminator == SPIRBlock::Select && block.merge == SPIRBlock::MergeLoop && + (positive_candidate || negative_candidate); + + if (ret && positive_candidate && method == SPIRBlock::MergeToSelectContinueForLoop) + ret = block.true_block == block.continue_block; + else if (ret && negative_candidate && method == SPIRBlock::MergeToSelectContinueForLoop) + ret = block.false_block == block.continue_block; + + // If we have OpPhi which depends on branches which came from our own block, + // we need to flush phi variables in else block instead of a trivial break, + // so we cannot assume this is a for loop candidate. + if (ret) + { + for (auto &phi : block.phi_variables) + if (phi.parent == block.self) + return false; + + auto *merge = maybe_get(block.merge_block); + if (merge) + for (auto &phi : merge->phi_variables) + if (phi.parent == block.self) + return false; + } + return ret; + } + else if (method == SPIRBlock::MergeToDirectForLoop) + { + // Empty loop header that just sets up merge target + // and branches to loop body. + bool ret = block.terminator == SPIRBlock::Direct && block.merge == SPIRBlock::MergeLoop && block_is_noop(block); + + if (!ret) + return false; + + auto &child = get(block.next_block); + + const auto *false_block = maybe_get(child.false_block); + const auto *true_block = maybe_get(child.true_block); + const auto *merge_block = maybe_get(block.merge_block); + + bool false_block_is_merge = child.false_block == block.merge_block || + (false_block && merge_block && execution_is_noop(*false_block, *merge_block)); + + bool true_block_is_merge = child.true_block == block.merge_block || + (true_block && merge_block && execution_is_noop(*true_block, *merge_block)); + + bool positive_candidate = + child.true_block != block.merge_block && child.true_block != block.self && false_block_is_merge; + + bool negative_candidate = + child.false_block != block.merge_block && child.false_block != block.self && true_block_is_merge; + + ret = child.terminator == SPIRBlock::Select && child.merge == SPIRBlock::MergeNone && + (positive_candidate || negative_candidate); + + if (ret) + { + auto *merge = maybe_get(block.merge_block); + if (merge) + for (auto &phi : merge->phi_variables) + if (phi.parent == block.self || phi.parent == child.false_block) + return false; + } + + return ret; + } + else + return false; +} + +bool Compiler::execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const +{ + if (!execution_is_branchless(from, to)) + return false; + + auto *start = &from; + for (;;) + { + if (start->self == to.self) + return true; + + if (!block_is_noop(*start)) + return false; + + auto &next = get(start->next_block); + start = &next; + } +} + +bool Compiler::execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const +{ + auto *start = &from; + for (;;) + { + if (start->self == to.self) + return true; + + if (start->terminator == SPIRBlock::Direct && start->merge == SPIRBlock::MergeNone) + start = &get(start->next_block); + else + return false; + } +} + +bool Compiler::execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const +{ + return from.terminator == SPIRBlock::Direct && from.merge == SPIRBlock::MergeNone && from.next_block == to.self; +} + +SPIRBlock::ContinueBlockType Compiler::continue_block_type(const SPIRBlock &block) const +{ + // The block was deemed too complex during code emit, pick conservative fallback paths. + if (block.complex_continue) + return SPIRBlock::ComplexLoop; + + // In older glslang output continue block can be equal to the loop header. + // In this case, execution is clearly branchless, so just assume a while loop header here. + if (block.merge == SPIRBlock::MergeLoop) + return SPIRBlock::WhileLoop; + + if (block.loop_dominator == BlockID(SPIRBlock::NoDominator)) + { + // Continue block is never reached from CFG. + return SPIRBlock::ComplexLoop; + } + + auto &dominator = get(block.loop_dominator); + + if (execution_is_noop(block, dominator)) + return SPIRBlock::WhileLoop; + else if (execution_is_branchless(block, dominator)) + return SPIRBlock::ForLoop; + else + { + const auto *false_block = maybe_get(block.false_block); + const auto *true_block = maybe_get(block.true_block); + const auto *merge_block = maybe_get(dominator.merge_block); + + // If we need to flush Phi in this block, we cannot have a DoWhile loop. + bool flush_phi_to_false = false_block && flush_phi_required(block.self, block.false_block); + bool flush_phi_to_true = true_block && flush_phi_required(block.self, block.true_block); + if (flush_phi_to_false || flush_phi_to_true) + return SPIRBlock::ComplexLoop; + + bool positive_do_while = block.true_block == dominator.self && + (block.false_block == dominator.merge_block || + (false_block && merge_block && execution_is_noop(*false_block, *merge_block))); + + bool negative_do_while = block.false_block == dominator.self && + (block.true_block == dominator.merge_block || + (true_block && merge_block && execution_is_noop(*true_block, *merge_block))); + + if (block.merge == SPIRBlock::MergeNone && block.terminator == SPIRBlock::Select && + (positive_do_while || negative_do_while)) + { + return SPIRBlock::DoWhileLoop; + } + else + return SPIRBlock::ComplexLoop; + } +} + +const SmallVector &Compiler::get_case_list(const SPIRBlock &block) const +{ + uint32_t width = 0; + + // First we check if we can get the type directly from the block.condition + // since it can be a SPIRConstant or a SPIRVariable. + if (const auto *constant = maybe_get(block.condition)) + { + const auto &type = get(constant->constant_type); + width = type.width; + } + else if (const auto *op = maybe_get(block.condition)) + { + const auto &type = get(op->basetype); + width = type.width; + } + else if (const auto *var = maybe_get(block.condition)) + { + const auto &type = get(var->basetype); + width = type.width; + } + else if (const auto *undef = maybe_get(block.condition)) + { + const auto &type = get(undef->basetype); + width = type.width; + } + else + { + auto search = ir.load_type_width.find(block.condition); + if (search == ir.load_type_width.end()) + { + SPIRV_CROSS_THROW("Use of undeclared variable on a switch statement."); + } + + width = search->second; + } + + if (width > 32) + return block.cases_64bit; + + return block.cases_32bit; +} + +bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const +{ + handler.set_current_block(block); + handler.rearm_current_block(block); + + if (handler.enable_result_types) + { + for (auto &phi: block.phi_variables) + { + auto &v = get(phi.function_variable); + handler.result_types[phi.function_variable] = v.basetype; + } + } + + // Ideally, perhaps traverse the CFG instead of all blocks in order to eliminate dead blocks, + // but this shouldn't be a problem in practice unless the SPIR-V is doing insane things like recursing + // inside dead blocks ... + for (auto &i : block.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + if (!handler.handle(op, ops, i.length)) + return false; + + if (handler.enable_result_types) + { + // If it has one, keep track of the instruction's result type, mapped by ID + uint32_t result_type, result_id; + if (instruction_to_result_type(result_type, result_id, op, ops, i.length)) + handler.result_types[result_id] = result_type; + } + + if (op == OpFunctionCall) + { + auto &func = get(ops[2]); + if (handler.follow_function_call(func)) + { + if (handler.enable_result_types) + for (auto &arg : func.arguments) + if (!arg.alias_global_variable) + handler.result_types[arg.id] = arg.type; + + if (!handler.begin_function_scope(ops, i.length)) + return false; + if (!traverse_all_reachable_opcodes(get(ops[2]), handler)) + return false; + if (!handler.end_function_scope(ops, i.length)) + return false; + + handler.rearm_current_block(block); + } + } + } + + if (!handler.handle_terminator(block)) + return false; + + return true; +} + +bool Compiler::traverse_all_reachable_opcodes(const SPIRFunction &func, OpcodeHandler &handler) const +{ + for (auto block : func.blocks) + if (!traverse_all_reachable_opcodes(get(block), handler)) + return false; + + return true; +} + +uint32_t Compiler::type_struct_member_offset(const SPIRType &type, uint32_t index) const +{ + auto *type_meta = ir.find_meta(type.self); + if (type_meta) + { + // Decoration must be set in valid SPIR-V, otherwise throw. + auto &dec = type_meta->members[index]; + if (dec.decoration_flags.get(DecorationOffset)) + return dec.offset; + else + SPIRV_CROSS_THROW("Struct member does not have Offset set."); + } + else + SPIRV_CROSS_THROW("Struct member does not have Offset set."); +} + +uint32_t Compiler::type_struct_member_array_stride(const SPIRType &type, uint32_t index) const +{ + auto *type_meta = ir.find_meta(type.member_types[index]); + if (type_meta) + { + // Decoration must be set in valid SPIR-V, otherwise throw. + // ArrayStride is part of the array type not OpMemberDecorate. + auto &dec = type_meta->decoration; + if (dec.decoration_flags.get(DecorationArrayStride)) + return dec.array_stride; + else + SPIRV_CROSS_THROW("Struct member does not have ArrayStride set."); + } + else + SPIRV_CROSS_THROW("Struct member does not have ArrayStride set."); +} + +uint32_t Compiler::type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const +{ + auto *type_meta = ir.find_meta(type.self); + if (type_meta) + { + // Decoration must be set in valid SPIR-V, otherwise throw. + // MatrixStride is part of OpMemberDecorate. + auto &dec = type_meta->members[index]; + if (dec.decoration_flags.get(DecorationMatrixStride)) + return dec.matrix_stride; + else + SPIRV_CROSS_THROW("Struct member does not have MatrixStride set."); + } + else + SPIRV_CROSS_THROW("Struct member does not have MatrixStride set."); +} + +size_t Compiler::get_declared_struct_size(const SPIRType &type) const +{ + if (type.member_types.empty()) + SPIRV_CROSS_THROW("Declared struct in block cannot be empty."); + + // Offsets can be declared out of order, so we need to deduce the actual size + // based on last member instead. + uint32_t member_index = 0; + size_t highest_offset = 0; + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + size_t offset = type_struct_member_offset(type, i); + if (offset > highest_offset) + { + highest_offset = offset; + member_index = i; + } + } + + size_t size = get_declared_struct_member_size(type, member_index); + return highest_offset + size; +} + +size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const +{ + if (type.member_types.empty()) + SPIRV_CROSS_THROW("Declared struct in block cannot be empty."); + + size_t size = get_declared_struct_size(type); + auto &last_type = get(type.member_types.back()); + if (!last_type.array.empty() && last_type.array_size_literal.back() && last_type.array.back() == 0) // Runtime array + size += array_size * type_struct_member_array_stride(type, uint32_t(type.member_types.size() - 1)); + + return size; +} + +uint32_t Compiler::evaluate_spec_constant_u32(const SPIRConstantOp &spec) const +{ + auto &result_type = get(spec.basetype); + if (result_type.basetype != SPIRType::UInt && result_type.basetype != SPIRType::Int && + result_type.basetype != SPIRType::Boolean) + { + SPIRV_CROSS_THROW( + "Only 32-bit integers and booleans are currently supported when evaluating specialization constants.\n"); + } + + if (!is_scalar(result_type)) + SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n"); + + uint32_t value = 0; + + const auto eval_u32 = [&](uint32_t id) -> uint32_t { + auto &type = expression_type(id); + if (type.basetype != SPIRType::UInt && type.basetype != SPIRType::Int && type.basetype != SPIRType::Boolean) + { + SPIRV_CROSS_THROW("Only 32-bit integers and booleans are currently supported when evaluating " + "specialization constants.\n"); + } + + if (!is_scalar(type)) + SPIRV_CROSS_THROW("Spec constant evaluation must be a scalar.\n"); + if (const auto *c = this->maybe_get(id)) + return c->scalar(); + else + return evaluate_spec_constant_u32(this->get(id)); + }; + +#define binary_spec_op(op, binary_op) \ + case Op##op: \ + value = eval_u32(spec.arguments[0]) binary_op eval_u32(spec.arguments[1]); \ + break +#define binary_spec_op_cast(op, binary_op, type) \ + case Op##op: \ + value = uint32_t(type(eval_u32(spec.arguments[0])) binary_op type(eval_u32(spec.arguments[1]))); \ + break + + // Support the basic opcodes which are typically used when computing array sizes. + switch (spec.opcode) + { + binary_spec_op(IAdd, +); + binary_spec_op(ISub, -); + binary_spec_op(IMul, *); + binary_spec_op(BitwiseAnd, &); + binary_spec_op(BitwiseOr, |); + binary_spec_op(BitwiseXor, ^); + binary_spec_op(LogicalAnd, &); + binary_spec_op(LogicalOr, |); + binary_spec_op(ShiftLeftLogical, <<); + binary_spec_op(ShiftRightLogical, >>); + binary_spec_op_cast(ShiftRightArithmetic, >>, int32_t); + binary_spec_op(LogicalEqual, ==); + binary_spec_op(LogicalNotEqual, !=); + binary_spec_op(IEqual, ==); + binary_spec_op(INotEqual, !=); + binary_spec_op(ULessThan, <); + binary_spec_op(ULessThanEqual, <=); + binary_spec_op(UGreaterThan, >); + binary_spec_op(UGreaterThanEqual, >=); + binary_spec_op_cast(SLessThan, <, int32_t); + binary_spec_op_cast(SLessThanEqual, <=, int32_t); + binary_spec_op_cast(SGreaterThan, >, int32_t); + binary_spec_op_cast(SGreaterThanEqual, >=, int32_t); +#undef binary_spec_op +#undef binary_spec_op_cast + + case OpLogicalNot: + value = uint32_t(!eval_u32(spec.arguments[0])); + break; + + case OpNot: + value = ~eval_u32(spec.arguments[0]); + break; + + case OpSNegate: + value = uint32_t(-int32_t(eval_u32(spec.arguments[0]))); + break; + + case OpSelect: + value = eval_u32(spec.arguments[0]) ? eval_u32(spec.arguments[1]) : eval_u32(spec.arguments[2]); + break; + + case OpUMod: + { + uint32_t a = eval_u32(spec.arguments[0]); + uint32_t b = eval_u32(spec.arguments[1]); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in UMod, b == 0.\n"); + value = a % b; + break; + } + + case OpSRem: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SRem, b == 0.\n"); + value = a % b; + break; + } + + case OpSMod: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SMod, b == 0.\n"); + auto v = a % b; + + // Makes sure we match the sign of b, not a. + if ((b < 0 && v > 0) || (b > 0 && v < 0)) + v += b; + value = v; + break; + } + + case OpUDiv: + { + uint32_t a = eval_u32(spec.arguments[0]); + uint32_t b = eval_u32(spec.arguments[1]); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in UDiv, b == 0.\n"); + value = a / b; + break; + } + + case OpSDiv: + { + auto a = int32_t(eval_u32(spec.arguments[0])); + auto b = int32_t(eval_u32(spec.arguments[1])); + if (b == 0) + SPIRV_CROSS_THROW("Undefined behavior in SDiv, b == 0.\n"); + value = a / b; + break; + } + + default: + SPIRV_CROSS_THROW("Unsupported spec constant opcode for evaluation.\n"); + } + + return value; +} + +uint32_t Compiler::evaluate_constant_u32(uint32_t id) const +{ + if (const auto *c = maybe_get(id)) + return c->scalar(); + else + return evaluate_spec_constant_u32(get(id)); +} + +size_t Compiler::get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const +{ + if (struct_type.member_types.empty()) + SPIRV_CROSS_THROW("Declared struct in block cannot be empty."); + + auto &flags = get_member_decoration_bitset(struct_type.self, index); + auto &type = get(struct_type.member_types[index]); + + switch (type.basetype) + { + case SPIRType::Unknown: + case SPIRType::Void: + case SPIRType::Boolean: // Bools are purely logical, and cannot be used for externally visible types. + case SPIRType::AtomicCounter: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + SPIRV_CROSS_THROW("Querying size for object with opaque size."); + + default: + break; + } + + if (type.pointer && type.storage == StorageClassPhysicalStorageBuffer) + { + // Check if this is a top-level pointer type, and not an array of pointers. + if (type.pointer_depth > get(type.parent_type).pointer_depth) + return 8; + } + + if (!type.array.empty()) + { + // For arrays, we can use ArrayStride to get an easy check. + bool array_size_literal = type.array_size_literal.back(); + uint32_t array_size = array_size_literal ? type.array.back() : evaluate_constant_u32(type.array.back()); + return type_struct_member_array_stride(struct_type, index) * array_size; + } + else if (type.basetype == SPIRType::Struct) + { + return get_declared_struct_size(type); + } + else + { + unsigned vecsize = type.vecsize; + unsigned columns = type.columns; + + // Vectors. + if (columns == 1) + { + size_t component_size = type.width / 8; + return vecsize * component_size; + } + else + { + uint32_t matrix_stride = type_struct_member_matrix_stride(struct_type, index); + + // Per SPIR-V spec, matrices must be tightly packed and aligned up for vec3 accesses. + if (flags.get(DecorationRowMajor)) + return matrix_stride * vecsize; + else if (flags.get(DecorationColMajor)) + return matrix_stride * columns; + else + SPIRV_CROSS_THROW("Either row-major or column-major must be declared for matrices."); + } + } +} + +bool Compiler::BufferAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + if (opcode != OpAccessChain && opcode != OpInBoundsAccessChain && opcode != OpPtrAccessChain) + return true; + + bool ptr_chain = (opcode == OpPtrAccessChain); + + // Invalid SPIR-V. + if (length < (ptr_chain ? 5u : 4u)) + return false; + + if (args[2] != id) + return true; + + // Don't bother traversing the entire access chain tree yet. + // If we access a struct member, assume we access the entire member. + uint32_t index = compiler.get(args[ptr_chain ? 4 : 3]).scalar(); + + // Seen this index already. + if (seen.find(index) != end(seen)) + return true; + seen.insert(index); + + auto &type = compiler.expression_type(id); + uint32_t offset = compiler.type_struct_member_offset(type, index); + + size_t range; + // If we have another member in the struct, deduce the range by looking at the next member. + // This is okay since structs in SPIR-V can have padding, but Offset decoration must be + // monotonically increasing. + // Of course, this doesn't take into account if the SPIR-V for some reason decided to add + // very large amounts of padding, but that's not really a big deal. + if (index + 1 < type.member_types.size()) + { + range = compiler.type_struct_member_offset(type, index + 1) - offset; + } + else + { + // No padding, so just deduce it from the size of the member directly. + range = compiler.get_declared_struct_member_size(type, index); + } + + ranges.push_back({ index, offset, range }); + return true; +} + +SmallVector Compiler::get_active_buffer_ranges(VariableID id) const +{ + SmallVector ranges; + BufferAccessHandler handler(*this, ranges, id); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + return ranges; +} + +bool Compiler::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const +{ + if (a.basetype != b.basetype) + return false; + if (a.width != b.width) + return false; + if (a.vecsize != b.vecsize) + return false; + if (a.columns != b.columns) + return false; + if (a.array.size() != b.array.size()) + return false; + + size_t array_count = a.array.size(); + if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0) + return false; + + if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage) + { + if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0) + return false; + } + + if (a.member_types.size() != b.member_types.size()) + return false; + + size_t member_types = a.member_types.size(); + for (size_t i = 0; i < member_types; i++) + { + if (!types_are_logically_equivalent(get(a.member_types[i]), get(b.member_types[i]))) + return false; + } + + return true; +} + +const Bitset &Compiler::get_execution_mode_bitset() const +{ + return get_entry_point().flags; +} + +void Compiler::set_execution_mode(ExecutionMode mode, uint32_t arg0, uint32_t arg1, uint32_t arg2) +{ + auto &execution = get_entry_point(); + + execution.flags.set(mode); + switch (mode) + { + case ExecutionModeLocalSize: + execution.workgroup_size.x = arg0; + execution.workgroup_size.y = arg1; + execution.workgroup_size.z = arg2; + break; + + case ExecutionModeLocalSizeId: + execution.workgroup_size.id_x = arg0; + execution.workgroup_size.id_y = arg1; + execution.workgroup_size.id_z = arg2; + break; + + case ExecutionModeInvocations: + execution.invocations = arg0; + break; + + case ExecutionModeOutputVertices: + execution.output_vertices = arg0; + break; + + case ExecutionModeOutputPrimitivesEXT: + execution.output_primitives = arg0; + break; + + case ExecutionModeFPFastMathDefault: + execution.fp_fast_math_defaults[arg0] = arg1; + break; + + default: + break; + } +} + +void Compiler::unset_execution_mode(ExecutionMode mode) +{ + auto &execution = get_entry_point(); + execution.flags.clear(mode); +} + +uint32_t Compiler::get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, + SpecializationConstant &z) const +{ + auto &execution = get_entry_point(); + x = { 0, 0 }; + y = { 0, 0 }; + z = { 0, 0 }; + + // WorkgroupSize builtin takes precedence over LocalSize / LocalSizeId. + if (execution.workgroup_size.constant != 0) + { + auto &c = get(execution.workgroup_size.constant); + + if (c.m.c[0].id[0] != ID(0)) + { + x.id = c.m.c[0].id[0]; + x.constant_id = get_decoration(c.m.c[0].id[0], DecorationSpecId); + } + + if (c.m.c[0].id[1] != ID(0)) + { + y.id = c.m.c[0].id[1]; + y.constant_id = get_decoration(c.m.c[0].id[1], DecorationSpecId); + } + + if (c.m.c[0].id[2] != ID(0)) + { + z.id = c.m.c[0].id[2]; + z.constant_id = get_decoration(c.m.c[0].id[2], DecorationSpecId); + } + } + else if (execution.flags.get(ExecutionModeLocalSizeId)) + { + auto &cx = get(execution.workgroup_size.id_x); + if (cx.specialization) + { + x.id = execution.workgroup_size.id_x; + x.constant_id = get_decoration(execution.workgroup_size.id_x, DecorationSpecId); + } + + auto &cy = get(execution.workgroup_size.id_y); + if (cy.specialization) + { + y.id = execution.workgroup_size.id_y; + y.constant_id = get_decoration(execution.workgroup_size.id_y, DecorationSpecId); + } + + auto &cz = get(execution.workgroup_size.id_z); + if (cz.specialization) + { + z.id = execution.workgroup_size.id_z; + z.constant_id = get_decoration(execution.workgroup_size.id_z, DecorationSpecId); + } + } + + return execution.workgroup_size.constant; +} + +uint32_t Compiler::get_execution_mode_argument(ExecutionMode mode, uint32_t index) const +{ + auto &execution = get_entry_point(); + switch (mode) + { + case ExecutionModeLocalSizeId: + if (execution.flags.get(ExecutionModeLocalSizeId)) + { + switch (index) + { + case 0: + return execution.workgroup_size.id_x; + case 1: + return execution.workgroup_size.id_y; + case 2: + return execution.workgroup_size.id_z; + default: + return 0; + } + } + else + return 0; + + case ExecutionModeLocalSize: + switch (index) + { + case 0: + if (execution.flags.get(ExecutionModeLocalSizeId) && execution.workgroup_size.id_x != 0) + return get(execution.workgroup_size.id_x).scalar(); + else + return execution.workgroup_size.x; + case 1: + if (execution.flags.get(ExecutionModeLocalSizeId) && execution.workgroup_size.id_y != 0) + return get(execution.workgroup_size.id_y).scalar(); + else + return execution.workgroup_size.y; + case 2: + if (execution.flags.get(ExecutionModeLocalSizeId) && execution.workgroup_size.id_z != 0) + return get(execution.workgroup_size.id_z).scalar(); + else + return execution.workgroup_size.z; + default: + return 0; + } + + case ExecutionModeInvocations: + return execution.invocations; + + case ExecutionModeOutputVertices: + return execution.output_vertices; + + case ExecutionModeOutputPrimitivesEXT: + return execution.output_primitives; + + default: + return 0; + } +} + +ExecutionModel Compiler::get_execution_model() const +{ + auto &execution = get_entry_point(); + return execution.model; +} + +bool Compiler::is_tessellation_shader(ExecutionModel model) +{ + return model == ExecutionModelTessellationControl || model == ExecutionModelTessellationEvaluation; +} + +bool Compiler::is_vertex_like_shader() const +{ + auto model = get_execution_model(); + return model == ExecutionModelVertex || model == ExecutionModelGeometry || + model == ExecutionModelTessellationControl || model == ExecutionModelTessellationEvaluation; +} + +bool Compiler::is_tessellation_shader() const +{ + return is_tessellation_shader(get_execution_model()); +} + +bool Compiler::is_tessellating_triangles() const +{ + return get_execution_mode_bitset().get(ExecutionModeTriangles); +} + +void Compiler::set_remapped_variable_state(VariableID id, bool remap_enable) +{ + get(id).remapped_variable = remap_enable; +} + +bool Compiler::get_remapped_variable_state(VariableID id) const +{ + return get(id).remapped_variable; +} + +void Compiler::set_subpass_input_remapped_components(VariableID id, uint32_t components) +{ + get(id).remapped_components = components; +} + +uint32_t Compiler::get_subpass_input_remapped_components(VariableID id) const +{ + return get(id).remapped_components; +} + +void Compiler::add_implied_read_expression(SPIRExpression &e, uint32_t source) +{ + auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), ID(source)); + if (itr == end(e.implied_read_expressions)) + e.implied_read_expressions.push_back(source); +} + +void Compiler::add_implied_read_expression(SPIRAccessChain &e, uint32_t source) +{ + auto itr = find(begin(e.implied_read_expressions), end(e.implied_read_expressions), ID(source)); + if (itr == end(e.implied_read_expressions)) + e.implied_read_expressions.push_back(source); +} + +void Compiler::add_active_interface_variable(uint32_t var_id) +{ + active_interface_variables.insert(var_id); + + // In SPIR-V 1.4 and up we must also track the interface variable in the entry point. + if (ir.get_spirv_version() >= 0x10400) + { + auto &vars = get_entry_point().interface_variables; + if (find(begin(vars), end(vars), VariableID(var_id)) == end(vars)) + vars.push_back(var_id); + } +} + +void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_expression) +{ + auto *ptr_e = maybe_get(dst); + + if (is_position_invariant() && ptr_e && maybe_get(source_expression)) + { + auto &deps = ptr_e->invariance_dependencies; + if (std::find(deps.begin(), deps.end(), source_expression) == deps.end()) + deps.push_back(source_expression); + } + + // Don't inherit any expression dependencies if the expression in dst + // is not a forwarded temporary. + if (forwarded_temporaries.find(dst) == end(forwarded_temporaries) || + forced_temporaries.find(dst) != end(forced_temporaries)) + { + return; + } + + auto &e = *ptr_e; + auto *phi = maybe_get(source_expression); + if (phi && phi->phi_variable) + { + // We have used a phi variable, which can change at the end of the block, + // so make sure we take a dependency on this phi variable. + phi->dependees.push_back(dst); + } + + auto *s = maybe_get(source_expression); + if (!s) + return; + + auto &e_deps = e.expression_dependencies; + auto &s_deps = s->expression_dependencies; + + // If we depend on a expression, we also depend on all sub-dependencies from source. + e_deps.push_back(source_expression); + e_deps.insert(end(e_deps), begin(s_deps), end(s_deps)); + + // Eliminate duplicated dependencies. + sort(begin(e_deps), end(e_deps)); + e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps)); +} + +SmallVector Compiler::get_entry_points_and_stages() const +{ + SmallVector entries; + for (auto &entry : ir.entry_points) + entries.push_back({ entry.second.orig_name, entry.second.model }); + return entries; +} + +void Compiler::rename_entry_point(const std::string &old_name, const std::string &new_name, ExecutionModel model) +{ + auto &entry = get_entry_point(old_name, model); + entry.orig_name = new_name; + entry.name = new_name; +} + +void Compiler::set_entry_point(const std::string &name, ExecutionModel model) +{ + auto &entry = get_entry_point(name, model); + ir.default_entry_point = entry.self; +} + +SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) +{ + auto itr = find_if( + begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { return entry.second.orig_name == name; }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +const SPIREntryPoint &Compiler::get_first_entry_point(const std::string &name) const +{ + auto itr = find_if( + begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { return entry.second.orig_name == name; }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) +{ + auto itr = find_if(begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { + return entry.second.orig_name == name && entry.second.model == model; + }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +const SPIREntryPoint &Compiler::get_entry_point(const std::string &name, ExecutionModel model) const +{ + auto itr = find_if(begin(ir.entry_points), end(ir.entry_points), + [&](const std::pair &entry) -> bool { + return entry.second.orig_name == name && entry.second.model == model; + }); + + if (itr == end(ir.entry_points)) + SPIRV_CROSS_THROW("Entry point does not exist."); + + return itr->second; +} + +const string &Compiler::get_cleansed_entry_point_name(const std::string &name, ExecutionModel model) const +{ + return get_entry_point(name, model).name; +} + +const SPIREntryPoint &Compiler::get_entry_point() const +{ + return ir.entry_points.find(ir.default_entry_point)->second; +} + +SPIREntryPoint &Compiler::get_entry_point() +{ + return ir.entry_points.find(ir.default_entry_point)->second; +} + +bool Compiler::interface_variable_exists_in_entry_point(uint32_t id) const +{ + auto &var = get(id); + + if (ir.get_spirv_version() < 0x10400) + { + if (var.storage != StorageClassInput && var.storage != StorageClassOutput && + var.storage != StorageClassUniformConstant) + SPIRV_CROSS_THROW("Only Input, Output variables and Uniform constants are part of a shader linking interface."); + + // This is to avoid potential problems with very old glslang versions which did + // not emit input/output interfaces properly. + // We can assume they only had a single entry point, and single entry point + // shaders could easily be assumed to use every interface variable anyways. + if (ir.entry_points.size() <= 1) + return true; + } + + // In SPIR-V 1.4 and later, all global resource variables must be present. + + auto &execution = get_entry_point(); + return find(begin(execution.interface_variables), end(execution.interface_variables), VariableID(id)) != + end(execution.interface_variables); +} + +void Compiler::CombinedImageSamplerHandler::push_remap_parameters(const SPIRFunction &func, const uint32_t *args, + uint32_t length) +{ + // If possible, pipe through a remapping table so that parameters know + // which variables they actually bind to in this scope. + unordered_map remapping; + for (uint32_t i = 0; i < length; i++) + remapping[func.arguments[i].id] = remap_parameter(args[i]); + parameter_remapping.push(std::move(remapping)); +} + +void Compiler::CombinedImageSamplerHandler::pop_remap_parameters() +{ + parameter_remapping.pop(); +} + +uint32_t Compiler::CombinedImageSamplerHandler::remap_parameter(uint32_t id) +{ + auto *var = compiler.maybe_get_backing_variable(id); + if (var) + id = var->self; + + if (parameter_remapping.empty()) + return id; + + auto &remapping = parameter_remapping.top(); + auto itr = remapping.find(id); + if (itr != end(remapping)) + return itr->second; + else + return id; +} + +bool Compiler::CombinedImageSamplerHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + auto &callee = compiler.get(args[2]); + args += 3; + length -= 3; + push_remap_parameters(callee, args, length); + functions.push(&callee); + return true; +} + +bool Compiler::CombinedImageSamplerHandler::end_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + auto &callee = compiler.get(args[2]); + args += 3; + + // There are two types of cases we have to handle, + // a callee might call sampler2D(texture2D, sampler) directly where + // one or more parameters originate from parameters. + // Alternatively, we need to provide combined image samplers to our callees, + // and in this case we need to add those as well. + + pop_remap_parameters(); + + // Our callee has now been processed at least once. + // No point in doing it again. + callee.do_combined_parameters = false; + + auto ¶ms = functions.top()->combined_parameters; + functions.pop(); + if (functions.empty()) + return true; + + auto &caller = *functions.top(); + if (caller.do_combined_parameters) + { + for (auto ¶m : params) + { + VariableID image_id = param.global_image ? param.image_id : VariableID(args[param.image_id]); + VariableID sampler_id = param.global_sampler ? param.sampler_id : VariableID(args[param.sampler_id]); + + auto *i = compiler.maybe_get_backing_variable(image_id); + auto *s = compiler.maybe_get_backing_variable(sampler_id); + if (i) + image_id = i->self; + if (s) + sampler_id = s->self; + + register_combined_image_sampler(caller, 0, image_id, sampler_id, param.depth); + } + } + + return true; +} + +void Compiler::CombinedImageSamplerHandler::register_combined_image_sampler(SPIRFunction &caller, + VariableID combined_module_id, + VariableID image_id, VariableID sampler_id, + bool depth) +{ + // We now have a texture ID and a sampler ID which will either be found as a global + // or a parameter in our own function. If both are global, they will not need a parameter, + // otherwise, add it to our list. + SPIRFunction::CombinedImageSamplerParameter param = { + 0u, image_id, sampler_id, true, true, depth, + }; + + auto texture_itr = find_if(begin(caller.arguments), end(caller.arguments), + [image_id](const SPIRFunction::Parameter &p) { return p.id == image_id; }); + auto sampler_itr = find_if(begin(caller.arguments), end(caller.arguments), + [sampler_id](const SPIRFunction::Parameter &p) { return p.id == sampler_id; }); + + if (texture_itr != end(caller.arguments)) + { + param.global_image = false; + param.image_id = uint32_t(texture_itr - begin(caller.arguments)); + } + + if (sampler_itr != end(caller.arguments)) + { + param.global_sampler = false; + param.sampler_id = uint32_t(sampler_itr - begin(caller.arguments)); + } + + if (param.global_image && param.global_sampler) + return; + + auto itr = find_if(begin(caller.combined_parameters), end(caller.combined_parameters), + [¶m](const SPIRFunction::CombinedImageSamplerParameter &p) { + return param.image_id == p.image_id && param.sampler_id == p.sampler_id && + param.global_image == p.global_image && param.global_sampler == p.global_sampler; + }); + + if (itr == end(caller.combined_parameters)) + { + uint32_t id = compiler.ir.increase_bound_by(3); + auto type_id = id + 0; + auto ptr_type_id = id + 1; + auto combined_id = id + 2; + auto &base = compiler.expression_type(image_id); + auto &type = compiler.set(type_id, OpTypeSampledImage); + auto &ptr_type = compiler.set(ptr_type_id, OpTypePointer); + + type = base; + type.self = type_id; + type.basetype = SPIRType::SampledImage; + type.pointer = false; + type.storage = StorageClassGeneric; + type.image.depth = depth; + + ptr_type = type; + ptr_type.pointer = true; + ptr_type.storage = StorageClassUniformConstant; + ptr_type.parent_type = type_id; + + // Build new variable. + compiler.set(combined_id, ptr_type_id, StorageClassFunction, 0); + + // Inherit RelaxedPrecision. + // If any of OpSampledImage, underlying image or sampler are marked, inherit the decoration. + bool relaxed_precision = + compiler.has_decoration(sampler_id, DecorationRelaxedPrecision) || + compiler.has_decoration(image_id, DecorationRelaxedPrecision) || + (combined_module_id && compiler.has_decoration(combined_module_id, DecorationRelaxedPrecision)); + + if (relaxed_precision) + compiler.set_decoration(combined_id, DecorationRelaxedPrecision); + + param.id = combined_id; + + compiler.set_name(combined_id, + join("SPIRV_Cross_Combined", compiler.to_name(image_id), compiler.to_name(sampler_id))); + + caller.combined_parameters.push_back(param); + caller.shadow_arguments.push_back({ ptr_type_id, combined_id, 0u, 0u, true }); + } +} + +bool Compiler::DummySamplerForCombinedImageHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + if (need_dummy_sampler) + { + // No need to traverse further, we know the result. + return false; + } + + switch (opcode) + { + case OpLoad: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + bool separate_image = + type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer; + + // If not separate image, don't bother. + if (!separate_image) + return true; + + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + break; + } + + case OpImageFetch: + case OpImageQuerySizeLod: + case OpImageQuerySize: + case OpImageQueryLevels: + case OpImageQuerySamples: + { + // If we are fetching or querying LOD from a plain OpTypeImage, we must pre-combine with our dummy sampler. + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (var) + { + auto &type = compiler.get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer) + need_dummy_sampler = true; + } + + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + auto &type = compiler.get(result_type); + bool separate_image = + type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer; + if (!separate_image) + return true; + + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + + // Other backends might use SPIRAccessChain for this later. + compiler.ir.ids[id].set_allow_type_rewrite(); + break; + } + + default: + break; + } + + return true; +} + +bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + // We need to figure out where samplers and images are loaded from, so do only the bare bones compilation we need. + bool is_fetch = false; + + switch (opcode) + { + case OpLoad: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + + // If not separate image or sampler, don't bother. + if (!separate_image && !separate_sampler) + return true; + + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + return true; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + // Technically, it is possible to have arrays of textures and arrays of samplers and combine them, but this becomes essentially + // impossible to implement, since we don't know which concrete sampler we are accessing. + // One potential way is to create a combinatorial explosion where N textures and M samplers are combined into N * M sampler2Ds, + // but this seems ridiculously complicated for a problem which is easy to work around. + // Checking access chains like this assumes we don't have samplers or textures inside uniform structs, but this makes no sense. + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + if (separate_sampler) + SPIRV_CROSS_THROW( + "Attempting to use arrays or structs of separate samplers. This is not possible to statically " + "remap to plain GLSL."); + + if (separate_image) + { + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + } + return true; + } + + case OpImageFetch: + case OpImageQuerySizeLod: + case OpImageQuerySize: + case OpImageQueryLevels: + case OpImageQuerySamples: + { + // If we are fetching from a plain OpTypeImage or querying LOD, we must pre-combine with our dummy sampler. + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (!var) + return true; + + auto &type = compiler.get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer) + { + if (compiler.dummy_sampler_id == 0) + SPIRV_CROSS_THROW("texelFetch without sampler was found, but no dummy sampler has been created with " + "build_dummy_sampler_for_combined_images()."); + + // Do it outside. + is_fetch = true; + break; + } + + return true; + } + + case OpSampledImage: + // Do it outside. + break; + + default: + return true; + } + + // Registers sampler2D calls used in case they are parameters so + // that their callees know which combined image samplers to propagate down the call stack. + if (!functions.empty()) + { + auto &callee = *functions.top(); + if (callee.do_combined_parameters) + { + uint32_t image_id = args[2]; + + auto *image = compiler.maybe_get_backing_variable(image_id); + if (image) + image_id = image->self; + + uint32_t sampler_id = is_fetch ? compiler.dummy_sampler_id : args[3]; + auto *sampler = compiler.maybe_get_backing_variable(sampler_id); + if (sampler) + sampler_id = sampler->self; + + uint32_t combined_id = args[1]; + + auto &combined_type = compiler.get(args[0]); + register_combined_image_sampler(callee, combined_id, image_id, sampler_id, combined_type.image.depth); + } + } + + // For function calls, we need to remap IDs which are function parameters into global variables. + // This information is statically known from the current place in the call stack. + // Function parameters are not necessarily pointers, so if we don't have a backing variable, remapping will know + // which backing variable the image/sample came from. + VariableID image_id = remap_parameter(args[2]); + VariableID sampler_id = is_fetch ? compiler.dummy_sampler_id : remap_parameter(args[3]); + + auto itr = find_if(begin(compiler.combined_image_samplers), end(compiler.combined_image_samplers), + [image_id, sampler_id](const CombinedImageSampler &combined) { + return combined.image_id == image_id && combined.sampler_id == sampler_id; + }); + + if (itr == end(compiler.combined_image_samplers)) + { + uint32_t sampled_type; + uint32_t combined_module_id; + if (is_fetch) + { + // Have to invent the sampled image type. + sampled_type = compiler.ir.increase_bound_by(1); + auto &type = compiler.set(sampled_type, OpTypeSampledImage); + type = compiler.expression_type(args[2]); + type.self = sampled_type; + type.basetype = SPIRType::SampledImage; + type.image.depth = false; + combined_module_id = 0; + } + else + { + sampled_type = args[0]; + combined_module_id = args[1]; + } + + auto id = compiler.ir.increase_bound_by(2); + auto type_id = id + 0; + auto combined_id = id + 1; + + // Make a new type, pointer to OpTypeSampledImage, so we can make a variable of this type. + // We will probably have this type lying around, but it doesn't hurt to make duplicates for internal purposes. + auto &type = compiler.set(type_id, OpTypePointer); + auto &base = compiler.get(sampled_type); + type = base; + type.pointer = true; + type.storage = StorageClassUniformConstant; + type.parent_type = type_id; + + // Build new variable. + compiler.set(combined_id, type_id, StorageClassUniformConstant, 0); + + // Inherit RelaxedPrecision (and potentially other useful flags if deemed relevant). + // If any of OpSampledImage, underlying image or sampler are marked, inherit the decoration. + bool relaxed_precision = + (sampler_id && compiler.has_decoration(sampler_id, DecorationRelaxedPrecision)) || + (image_id && compiler.has_decoration(image_id, DecorationRelaxedPrecision)) || + (combined_module_id && compiler.has_decoration(combined_module_id, DecorationRelaxedPrecision)); + + if (relaxed_precision) + compiler.set_decoration(combined_id, DecorationRelaxedPrecision); + + // Propagate the array type for the original image as well. + auto *var = compiler.maybe_get_backing_variable(image_id); + if (var) + { + auto &parent_type = compiler.get(var->basetype); + type.array = parent_type.array; + type.array_size_literal = parent_type.array_size_literal; + } + + compiler.combined_image_samplers.push_back({ combined_id, image_id, sampler_id }); + } + + return true; +} + +VariableID Compiler::build_dummy_sampler_for_combined_images() +{ + DummySamplerForCombinedImageHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + if (handler.need_dummy_sampler) + { + uint32_t offset = ir.increase_bound_by(3); + auto type_id = offset + 0; + auto ptr_type_id = offset + 1; + auto var_id = offset + 2; + + auto &sampler = set(type_id, OpTypeSampler); + sampler.basetype = SPIRType::Sampler; + + auto &ptr_sampler = set(ptr_type_id, OpTypePointer); + ptr_sampler = sampler; + ptr_sampler.self = type_id; + ptr_sampler.storage = StorageClassUniformConstant; + ptr_sampler.pointer = true; + ptr_sampler.parent_type = type_id; + + set(var_id, ptr_type_id, StorageClassUniformConstant, 0); + set_name(var_id, "SPIRV_Cross_DummySampler"); + dummy_sampler_id = var_id; + return var_id; + } + else + return 0; +} + +void Compiler::build_combined_image_samplers() +{ + ir.for_each_typed_id([&](uint32_t, SPIRFunction &func) { + func.combined_parameters.clear(); + func.shadow_arguments.clear(); + func.do_combined_parameters = true; + }); + + combined_image_samplers.clear(); + CombinedImageSamplerHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); +} + +SmallVector Compiler::get_specialization_constants() const +{ + SmallVector spec_consts; + ir.for_each_typed_id([&](uint32_t, const SPIRConstant &c) { + if (c.specialization && has_decoration(c.self, DecorationSpecId)) + spec_consts.push_back({ c.self, get_decoration(c.self, DecorationSpecId) }); + }); + return spec_consts; +} + +SPIRConstant &Compiler::get_constant(ConstantID id) +{ + return get(id); +} + +const SPIRConstant &Compiler::get_constant(ConstantID id) const +{ + return get(id); +} + +static bool exists_unaccessed_path_to_return(const CFG &cfg, uint32_t block, const unordered_set &blocks, + unordered_set &visit_cache) +{ + // This block accesses the variable. + if (blocks.find(block) != end(blocks)) + return false; + + // We are at the end of the CFG. + if (cfg.get_succeeding_edges(block).empty()) + return true; + + // If any of our successors have a path to the end, there exists a path from block. + for (auto &succ : cfg.get_succeeding_edges(block)) + { + if (visit_cache.count(succ) == 0) + { + if (exists_unaccessed_path_to_return(cfg, succ, blocks, visit_cache)) + return true; + visit_cache.insert(succ); + } + } + + return false; +} + +void Compiler::analyze_parameter_preservation( + SPIRFunction &entry, const CFG &cfg, const unordered_map> &variable_to_blocks, + const unordered_map> &complete_write_blocks) +{ + for (auto &arg : entry.arguments) + { + // Non-pointers are always inputs. + auto &type = get(arg.type); + if (!type.pointer) + continue; + + // Opaque argument types are always in + bool potential_preserve; + switch (type.basetype) + { + case SPIRType::Sampler: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::AtomicCounter: + potential_preserve = false; + break; + + default: + potential_preserve = true; + break; + } + + if (!potential_preserve) + continue; + + auto itr = variable_to_blocks.find(arg.id); + if (itr == end(variable_to_blocks)) + { + // Variable is never accessed. + continue; + } + + // We have accessed a variable, but there was no complete writes to that variable. + // We deduce that we must preserve the argument. + itr = complete_write_blocks.find(arg.id); + if (itr == end(complete_write_blocks)) + { + arg.read_count++; + continue; + } + + // If there is a path through the CFG where no block completely writes to the variable, the variable will be in an undefined state + // when the function returns. We therefore need to implicitly preserve the variable in case there are writers in the function. + // Major case here is if a function is + // void foo(int &var) { if (cond) var = 10; } + // Using read/write counts, we will think it's just an out variable, but it really needs to be inout, + // because if we don't write anything whatever we put into the function must return back to the caller. + unordered_set visit_cache; + if (exists_unaccessed_path_to_return(cfg, entry.entry_block, itr->second, visit_cache)) + arg.read_count++; + } +} + +Compiler::AnalyzeVariableScopeAccessHandler::AnalyzeVariableScopeAccessHandler(Compiler &compiler_, + SPIRFunction &entry_) + : OpcodeHandler(compiler_) + , entry(entry_) +{ +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::follow_function_call(const SPIRFunction &) +{ + // Only analyze within this function. + return false; +} + +void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBlock &block) +{ + current_block = █ + + // If we're branching to a block which uses OpPhi, in GLSL + // this will be a variable write when we branch, + // so we need to track access to these variables as well to + // have a complete picture. + const auto test_phi = [this, &block](uint32_t to) { + auto &next = compiler.get(to); + for (auto &phi : next.phi_variables) + { + if (phi.parent == block.self) + { + accessed_variables_to_block[phi.function_variable].insert(block.self); + // Phi variables are also accessed in our target branch block. + accessed_variables_to_block[phi.function_variable].insert(next.self); + + notify_variable_access(phi.local_variable, block.self); + } + } + }; + + switch (block.terminator) + { + case SPIRBlock::Direct: + notify_variable_access(block.condition, block.self); + test_phi(block.next_block); + break; + + case SPIRBlock::Select: + notify_variable_access(block.condition, block.self); + test_phi(block.true_block); + test_phi(block.false_block); + break; + + case SPIRBlock::MultiSelect: + { + notify_variable_access(block.condition, block.self); + auto &cases = compiler.get_case_list(block); + for (auto &target : cases) + test_phi(target.block); + if (block.default_block) + test_phi(block.default_block); + break; + } + + default: + break; + } +} + +void Compiler::AnalyzeVariableScopeAccessHandler::notify_variable_access(uint32_t id, uint32_t block) +{ + if (id == 0) + return; + + // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. + auto itr = rvalue_forward_children.find(id); + if (itr != end(rvalue_forward_children)) + for (auto child_id : itr->second) + notify_variable_access(child_id, block); + + if (id_is_phi_variable(id)) + accessed_variables_to_block[id].insert(block); + else if (id_is_potential_temporary(id)) + accessed_temporaries_to_block[id].insert(block); +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_phi_variable(uint32_t id) const +{ + if (id >= compiler.get_current_id_bound()) + return false; + auto *var = compiler.maybe_get(id); + return var && var->phi_variable; +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_potential_temporary(uint32_t id) const +{ + if (id >= compiler.get_current_id_bound()) + return false; + + // Temporaries are not created before we start emitting code. + return compiler.ir.ids[id].empty() || (compiler.ir.ids[id].get_type() == TypeExpression); +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::handle_terminator(const SPIRBlock &block) +{ + switch (block.terminator) + { + case SPIRBlock::Return: + if (block.return_value) + notify_variable_access(block.return_value, block.self); + break; + + case SPIRBlock::Select: + case SPIRBlock::MultiSelect: + notify_variable_access(block.condition, block.self); + break; + + default: + break; + } + + return true; +} + +bool Compiler::AnalyzeVariableScopeAccessHandler::handle(Op op, const uint32_t *args, uint32_t length) +{ + // Keep track of the types of temporaries, so we can hoist them out as necessary. + uint32_t result_type = 0, result_id = 0; + if (instruction_to_result_type(result_type, result_id, op, args, length)) + { + // For some opcodes, we will need to override the result id. + // If we need to hoist the temporary, the temporary type is the input, not the result. + if (op == OpConvertUToAccelerationStructureKHR) + { + auto itr = result_id_to_type.find(args[2]); + if (itr != result_id_to_type.end()) + result_type = itr->second; + } + + result_id_to_type[result_id] = result_type; + } + + switch (op) + { + case OpStore: + case OpCooperativeMatrixStoreKHR: + { + if (length < 2) + return false; + + ID ptr = args[0]; + auto *var = compiler.maybe_get_backing_variable(ptr); + + // If we store through an access chain, we have a partial write. + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == ptr) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); + } + + // args[0] might be an access chain we have to track use of. + notify_variable_access(args[0], current_block->self); + // Might try to store a Phi variable here. + notify_variable_access(args[1], current_block->self); + break; + } + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get(ptr); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + rvalue_forward_children[args[1]].insert(var->self); + } + + // args[2] might be another access chain we have to track use of. + for (uint32_t i = 2; i < length; i++) + { + notify_variable_access(args[i], current_block->self); + rvalue_forward_children[args[1]].insert(args[i]); + } + + // Also keep track of the access chain pointer itself. + // In exceptionally rare cases, we can end up with a case where + // the access chain is generated in the loop body, but is consumed in continue block. + // This means we need complex loop workarounds, and we must detect this via CFG analysis. + notify_variable_access(args[1], current_block->self); + + // The result of an access chain is a fixed expression and is not really considered a temporary. + auto &e = compiler.set(args[1], "", args[0], true); + auto *backing_variable = compiler.maybe_get_backing_variable(ptr); + e.loaded_from = backing_variable ? VariableID(backing_variable->self) : VariableID(0); + + // Other backends might use SPIRAccessChain for this later. + compiler.ir.ids[args[1]].set_allow_type_rewrite(); + access_chain_expressions.insert(args[1]); + break; + } + + case OpCopyMemory: + { + if (length < 2) + return false; + + ID lhs = args[0]; + ID rhs = args[1]; + auto *var = compiler.maybe_get_backing_variable(lhs); + + // If we store through an access chain, we have a partial write. + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == lhs) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); + } + + // args[0:1] might be access chains we have to track use of. + for (uint32_t i = 0; i < 2; i++) + notify_variable_access(args[i], current_block->self); + + var = compiler.maybe_get_backing_variable(rhs); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + break; + } + + case OpCopyObject: + { + // OpCopyObject copies the underlying non-pointer type, + // so any temp variable should be declared using the underlying type. + // If the type is a pointer, get its base type and overwrite the result type mapping. + auto &type = compiler.get(result_type); + if (type.pointer) + result_id_to_type[result_id] = type.parent_type; + + if (length < 3) + return false; + + auto *var = compiler.maybe_get_backing_variable(args[2]); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + + // Might be an access chain which we have to keep track of. + notify_variable_access(args[1], current_block->self); + if (access_chain_expressions.count(args[2])) + access_chain_expressions.insert(args[1]); + + // Might try to copy a Phi variable here. + notify_variable_access(args[2], current_block->self); + break; + } + + case OpLoad: + case OpCooperativeMatrixLoadKHR: + { + if (length < 3) + return false; + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var) + accessed_variables_to_block[var->self].insert(current_block->self); + + // Loaded value is a temporary. + notify_variable_access(args[1], current_block->self); + + // Might be an access chain we have to track use of. + notify_variable_access(args[2], current_block->self); + + // If we're loading an opaque type we cannot lower it to a temporary, + // we must defer access of args[2] until it's used. + auto &type = compiler.get(args[0]); + if (compiler.type_is_opaque_value(type)) + rvalue_forward_children[args[1]].insert(args[2]); + break; + } + + case OpFunctionCall: + { + if (length < 3) + return false; + + // Return value may be a temporary. + if (compiler.get_type(args[0]).basetype != SPIRType::Void) + notify_variable_access(args[1], current_block->self); + + length -= 3; + args += 3; + + for (uint32_t i = 0; i < length; i++) + { + auto *var = compiler.maybe_get_backing_variable(args[i]); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + // Assume we can get partial writes to this variable. + partial_write_variables_to_block[var->self].insert(current_block->self); + } + + // Cannot easily prove if argument we pass to a function is completely written. + // Usually, functions write to a dummy variable, + // which is then copied to in full to the real argument. + + // Might try to copy a Phi variable here. + notify_variable_access(args[i], current_block->self); + } + break; + } + + case OpSelect: + { + // In case of variable pointers, we might access a variable here. + // We cannot prove anything about these accesses however. + for (uint32_t i = 1; i < length; i++) + { + if (i >= 3) + { + auto *var = compiler.maybe_get_backing_variable(args[i]); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + // Assume we can get partial writes to this variable. + partial_write_variables_to_block[var->self].insert(current_block->self); + } + } + + // Might try to copy a Phi variable here. + notify_variable_access(args[i], current_block->self); + } + break; + } + + case OpExtInst: + { + for (uint32_t i = 4; i < length; i++) + notify_variable_access(args[i], current_block->self); + notify_variable_access(args[1], current_block->self); + + uint32_t extension_set = args[2]; + if (compiler.get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(args[3]); + switch (op_450) + { + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + uint32_t ptr = args[5]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var) + { + accessed_variables_to_block[var->self].insert(current_block->self); + if (var->self == ptr) + complete_write_variables_to_block[var->self].insert(current_block->self); + else + partial_write_variables_to_block[var->self].insert(current_block->self); + } + break; + } + + default: + break; + } + } + break; + } + + case OpArrayLength: + // Only result is a temporary. + notify_variable_access(args[1], current_block->self); + break; + + case OpLine: + case OpNoLine: + // Uses literals, but cannot be a phi variable or temporary, so ignore. + break; + + // Atomics shouldn't be able to access function-local variables. + // Some GLSL builtins access a pointer. + + case OpCompositeInsert: + case OpVectorShuffle: + // Specialize for opcode which contains literals. + for (uint32_t i = 1; i < 4; i++) + notify_variable_access(args[i], current_block->self); + break; + + case OpCompositeExtract: + // Specialize for opcode which contains literals. + for (uint32_t i = 1; i < 3; i++) + notify_variable_access(args[i], current_block->self); + break; + + case OpImageWrite: + for (uint32_t i = 0; i < length; i++) + { + // Argument 3 is a literal. + if (i != 3) + notify_variable_access(args[i], current_block->self); + } + break; + + case OpImageSampleImplicitLod: + case OpImageSampleExplicitLod: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleExplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleProjExplicitLod: + case OpImageFetch: + case OpImageSparseFetch: + case OpImageRead: + case OpImageSparseRead: + for (uint32_t i = 1; i < length; i++) + { + // Argument 4 is a literal. + if (i != 4) + notify_variable_access(args[i], current_block->self); + } + break; + + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageGather: + case OpImageSparseGather: + case OpImageDrefGather: + case OpImageSparseDrefGather: + for (uint32_t i = 1; i < length; i++) + { + // Argument 5 is a literal. + if (i != 5) + notify_variable_access(args[i], current_block->self); + } + break; + + default: + { + // Rather dirty way of figuring out where Phi variables are used. + // As long as only IDs are used, we can scan through instructions and try to find any evidence that + // the ID of a variable has been used. + // There are potential false positives here where a literal is used in-place of an ID, + // but worst case, it does not affect the correctness of the compile. + // Exhaustive analysis would be better here, but it's not worth it for now. + for (uint32_t i = 0; i < length; i++) + notify_variable_access(args[i], current_block->self); + break; + } + } + return true; +} + +Compiler::StaticExpressionAccessHandler::StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_) + : OpcodeHandler(compiler_) + , variable_id(variable_id_) +{ +} + +bool Compiler::StaticExpressionAccessHandler::follow_function_call(const SPIRFunction &) +{ + return false; +} + +bool Compiler::StaticExpressionAccessHandler::handle(Op op, const uint32_t *args, uint32_t length) +{ + switch (op) + { + case OpStore: + case OpCooperativeMatrixStoreKHR: + if (length < 2) + return false; + if (args[0] == variable_id) + { + static_expression = args[1]; + write_count++; + } + break; + + case OpLoad: + case OpCooperativeMatrixLoadKHR: + if (length < 3) + return false; + if (args[2] == variable_id && static_expression == 0) // Tried to read from variable before it was initialized. + return false; + break; + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + if (length < 3) + return false; + if (args[2] == variable_id) // If we try to access chain our candidate variable before we store to it, bail. + return false; + break; + + default: + break; + } + + return true; +} + +void Compiler::find_function_local_luts(SPIRFunction &entry, const AnalyzeVariableScopeAccessHandler &handler, + bool single_function) +{ + auto &cfg = *function_cfgs.find(entry.self)->second; + + // For each variable which is statically accessed. + for (auto &accessed_var : handler.accessed_variables_to_block) + { + auto &blocks = accessed_var.second; + auto &var = get(accessed_var.first); + auto &type = expression_type(accessed_var.first); + + // First check if there are writes to the variable. Later, if there are none, we'll + // reconsider it as globally accessed LUT. + if (!var.is_written_to) + { + var.is_written_to = handler.complete_write_variables_to_block.count(var.self) != 0 || + handler.partial_write_variables_to_block.count(var.self) != 0; + } + + // Only consider function local variables here. + // If we only have a single function in our CFG, private storage is also fine, + // since it behaves like a function local variable. + bool allow_lut = var.storage == StorageClassFunction || (single_function && var.storage == StorageClassPrivate); + if (!allow_lut) + continue; + + // We cannot be a phi variable. + if (var.phi_variable) + continue; + + // Only consider arrays here. + if (type.array.empty()) + continue; + + // If the variable has an initializer, make sure it is a constant expression. + uint32_t static_constant_expression = 0; + if (var.initializer) + { + if (ir.ids[var.initializer].get_type() != TypeConstant) + continue; + static_constant_expression = var.initializer; + + // There can be no stores to this variable, we have now proved we have a LUT. + if (var.is_written_to) + continue; + } + else + { + // We can have one, and only one write to the variable, and that write needs to be a constant. + + // No partial writes allowed. + if (handler.partial_write_variables_to_block.count(var.self) != 0) + continue; + + auto itr = handler.complete_write_variables_to_block.find(var.self); + + // No writes? + if (itr == end(handler.complete_write_variables_to_block)) + continue; + + // We write to the variable in more than one block. + auto &write_blocks = itr->second; + if (write_blocks.size() != 1) + continue; + + // The write needs to happen in the dominating block. + DominatorBuilder builder(cfg); + for (auto &block : blocks) + builder.add_block(block); + uint32_t dominator = builder.get_dominator(); + + // The complete write happened in a branch or similar, cannot deduce static expression. + if (write_blocks.count(dominator) == 0) + continue; + + // Find the static expression for this variable. + StaticExpressionAccessHandler static_expression_handler(*this, var.self); + traverse_all_reachable_opcodes(get(dominator), static_expression_handler); + + // We want one, and exactly one write + if (static_expression_handler.write_count != 1 || static_expression_handler.static_expression == 0) + continue; + + // Is it a constant expression? + if (ir.ids[static_expression_handler.static_expression].get_type() != TypeConstant) + continue; + + // We found a LUT! + static_constant_expression = static_expression_handler.static_expression; + } + + get(static_constant_expression).is_used_as_lut = true; + var.static_expression = static_constant_expression; + var.statically_assigned = true; + var.remapped_variable = true; + } +} + +void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeAccessHandler &handler) +{ + // First, we map out all variable access within a function. + // Essentially a map of block -> { variables accessed in the basic block } + traverse_all_reachable_opcodes(entry, handler); + + auto &cfg = *function_cfgs.find(entry.self)->second; + + // Analyze if there are parameters which need to be implicitly preserved with an "in" qualifier. + analyze_parameter_preservation(entry, cfg, handler.accessed_variables_to_block, + handler.complete_write_variables_to_block); + + unordered_map potential_loop_variables; + + // Find the loop dominator block for each block. + for (auto &block_id : entry.blocks) + { + auto &block = get(block_id); + + auto itr = ir.continue_block_to_loop_header.find(block_id); + if (itr != end(ir.continue_block_to_loop_header) && itr->second != block_id) + { + // Continue block might be unreachable in the CFG, but we still like to know the loop dominator. + // Edge case is when continue block is also the loop header, don't set the dominator in this case. + block.loop_dominator = itr->second; + } + else + { + uint32_t loop_dominator = cfg.find_loop_dominator(block_id); + if (loop_dominator != block_id) + block.loop_dominator = loop_dominator; + else + block.loop_dominator = SPIRBlock::NoDominator; + } + } + + // For each variable which is statically accessed. + for (auto &var : handler.accessed_variables_to_block) + { + // Only deal with variables which are considered local variables in this function. + if (find(begin(entry.local_variables), end(entry.local_variables), VariableID(var.first)) == + end(entry.local_variables)) + continue; + + DominatorBuilder builder(cfg); + auto &blocks = var.second; + auto &type = expression_type(var.first); + BlockID potential_continue_block = 0; + + // Figure out which block is dominating all accesses of those variables. + for (auto &block : blocks) + { + // If we're accessing a variable inside a continue block, this variable might be a loop variable. + // We can only use loop variables with scalars, as we cannot track static expressions for vectors. + if (is_continue(block)) + { + // Potentially awkward case to check for. + // We might have a variable inside a loop, which is touched by the continue block, + // but is not actually a loop variable. + // The continue block is dominated by the inner part of the loop, which does not make sense in high-level + // language output because it will be declared before the body, + // so we will have to lift the dominator up to the relevant loop header instead. + builder.add_block(ir.continue_block_to_loop_header[block]); + + // Arrays or structs cannot be loop variables. + if (type.vecsize == 1 && type.columns == 1 && type.basetype != SPIRType::Struct && type.array.empty()) + { + // The variable is used in multiple continue blocks, this is not a loop + // candidate, signal that by setting block to -1u. + if (potential_continue_block == 0) + potential_continue_block = block; + else + potential_continue_block = ~(0u); + } + } + + builder.add_block(block); + } + + builder.lift_continue_block_dominator(); + + // Add it to a per-block list of variables. + BlockID dominating_block = builder.get_dominator(); + + if (dominating_block && potential_continue_block != 0 && potential_continue_block != ~0u) + { + auto &inner_block = get(dominating_block); + + BlockID merge_candidate = 0; + + // Analyze the dominator. If it lives in a different loop scope than the candidate continue + // block, reject the loop variable candidate. + if (inner_block.merge == SPIRBlock::MergeLoop) + merge_candidate = inner_block.merge_block; + else if (inner_block.loop_dominator != SPIRBlock::NoDominator) + merge_candidate = get(inner_block.loop_dominator).merge_block; + + if (merge_candidate != 0 && cfg.is_reachable(merge_candidate)) + { + // If the merge block has a higher post-visit order, we know that continue candidate + // cannot reach the merge block, and we have two separate scopes. + if (!cfg.is_reachable(potential_continue_block) || + cfg.get_visit_order(merge_candidate) > cfg.get_visit_order(potential_continue_block)) + { + potential_continue_block = 0; + } + } + } + + if (potential_continue_block != 0 && potential_continue_block != ~0u) + potential_loop_variables[var.first] = potential_continue_block; + + // For variables whose dominating block is inside a loop, there is a risk that these variables + // actually need to be preserved across loop iterations. We can express this by adding + // a "read" access to the loop header. + // In the dominating block, we must see an OpStore or equivalent as the first access of an OpVariable. + // Should that fail, we look for the outermost loop header and tack on an access there. + // Phi nodes cannot have this problem. + if (dominating_block) + { + auto &variable = get(var.first); + if (!variable.phi_variable) + { + auto *block = &get(dominating_block); + bool preserve = may_read_undefined_variable_in_block(*block, var.first); + if (preserve) + { + // Find the outermost loop scope. + while (block->loop_dominator != BlockID(SPIRBlock::NoDominator)) + block = &get(block->loop_dominator); + + if (block->self != dominating_block) + { + builder.add_block(block->self); + dominating_block = builder.get_dominator(); + } + } + } + } + + // If all blocks here are dead code, this will be 0, so the variable in question + // will be completely eliminated. + if (dominating_block) + { + auto &block = get(dominating_block); + block.dominated_variables.push_back(var.first); + get(var.first).dominator = dominating_block; + } + } + + for (auto &var : handler.accessed_temporaries_to_block) + { + auto itr = handler.result_id_to_type.find(var.first); + + if (itr == end(handler.result_id_to_type)) + { + // We found a false positive ID being used, ignore. + // This should probably be an assert. + continue; + } + + // There is no point in doing domination analysis for opaque types. + auto &type = get(itr->second); + if (type_is_opaque_value(type)) + continue; + + DominatorBuilder builder(cfg); + bool force_temporary = false; + bool used_in_header_hoisted_continue_block = false; + + // Figure out which block is dominating all accesses of those temporaries. + auto &blocks = var.second; + for (auto &block : blocks) + { + builder.add_block(block); + + if (blocks.size() != 1 && is_continue(block)) + { + // The risk here is that inner loop can dominate the continue block. + // Any temporary we access in the continue block must be declared before the loop. + // This is moot for complex loops however. + auto &loop_header_block = get(ir.continue_block_to_loop_header[block]); + assert(loop_header_block.merge == SPIRBlock::MergeLoop); + builder.add_block(loop_header_block.self); + used_in_header_hoisted_continue_block = true; + } + } + + uint32_t dominating_block = builder.get_dominator(); + + if (blocks.size() != 1 && is_single_block_loop(dominating_block)) + { + // Awkward case, because the loop header is also the continue block, + // so hoisting to loop header does not help. + force_temporary = true; + } + + if (dominating_block) + { + // If we touch a variable in the dominating block, this is the expected setup. + // SPIR-V normally mandates this, but we have extra cases for temporary use inside loops. + bool first_use_is_dominator = blocks.count(dominating_block) != 0; + + if (!first_use_is_dominator || force_temporary) + { + if (handler.access_chain_expressions.count(var.first)) + { + // Exceptionally rare case. + // We cannot declare temporaries of access chains (except on MSL perhaps with pointers). + // Rather than do that, we force the indexing expressions to be declared in the right scope by + // tracking their usage to that end. There is no temporary to hoist. + // However, we still need to observe declaration order of the access chain. + + if (used_in_header_hoisted_continue_block) + { + // For this scenario, we used an access chain inside a continue block where we also registered an access to header block. + // This is a problem as we need to declare an access chain properly first with full definition. + // We cannot use temporaries for these expressions, + // so we must make sure the access chain is declared ahead of time. + // Force a complex for loop to deal with this. + // TODO: Out-of-order declaring for loops where continue blocks are emitted last might be another option. + auto &loop_header_block = get(dominating_block); + assert(loop_header_block.merge == SPIRBlock::MergeLoop); + loop_header_block.complex_continue = true; + } + } + else + { + // This should be very rare, but if we try to declare a temporary inside a loop, + // and that temporary is used outside the loop as well (spirv-opt inliner likes this) + // we should actually emit the temporary outside the loop. + hoisted_temporaries.insert(var.first); + forced_temporaries.insert(var.first); + + auto &block_temporaries = get(dominating_block).declare_temporary; + block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first); + } + } + else if (blocks.size() > 1) + { + // Keep track of the temporary as we might have to declare this temporary. + // This can happen if the loop header dominates a temporary, but we have a complex fallback loop. + // In this case, the header is actually inside the for (;;) {} block, and we have problems. + // What we need to do is hoist the temporaries outside the for (;;) {} block in case the header block + // declares the temporary. + auto &block_temporaries = get(dominating_block).potential_declare_temporary; + block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first); + } + } + } + + unordered_set seen_blocks; + + // Now, try to analyze whether or not these variables are actually loop variables. + for (auto &loop_variable : potential_loop_variables) + { + auto &var = get(loop_variable.first); + auto dominator = var.dominator; + BlockID block = loop_variable.second; + + // The variable was accessed in multiple continue blocks, ignore. + if (block == BlockID(~(0u)) || block == BlockID(0)) + continue; + + // Dead code. + if (dominator == ID(0)) + continue; + + BlockID header = 0; + + // Find the loop header for this block if we are a continue block. + { + auto itr = ir.continue_block_to_loop_header.find(block); + if (itr != end(ir.continue_block_to_loop_header)) + { + header = itr->second; + } + else if (get(block).continue_block == block) + { + // Also check for self-referential continue block. + header = block; + } + } + + assert(header); + auto &header_block = get(header); + auto &blocks = handler.accessed_variables_to_block[loop_variable.first]; + + // If a loop variable is not used before the loop, it's probably not a loop variable. + bool has_accessed_variable = blocks.count(header) != 0; + + // Now, there are two conditions we need to meet for the variable to be a loop variable. + // 1. The dominating block must have a branch-free path to the loop header, + // this way we statically know which expression should be part of the loop variable initializer. + + // Walk from the dominator, if there is one straight edge connecting + // dominator and loop header, we statically know the loop initializer. + bool static_loop_init = true; + while (dominator != header) + { + if (blocks.count(dominator) != 0) + has_accessed_variable = true; + + auto &succ = cfg.get_succeeding_edges(dominator); + if (succ.size() != 1) + { + static_loop_init = false; + break; + } + + auto &pred = cfg.get_preceding_edges(succ.front()); + if (pred.size() != 1 || pred.front() != dominator) + { + static_loop_init = false; + break; + } + + dominator = succ.front(); + } + + if (!static_loop_init || !has_accessed_variable) + continue; + + // The second condition we need to meet is that no access after the loop + // merge can occur. Walk the CFG to see if we find anything. + + seen_blocks.clear(); + cfg.walk_from(seen_blocks, header_block.merge_block, [&](uint32_t walk_block) -> bool { + // We found a block which accesses the variable outside the loop. + if (blocks.find(walk_block) != end(blocks)) + static_loop_init = false; + return true; + }); + + if (!static_loop_init) + continue; + + // We have a loop variable. + header_block.loop_variables.push_back(loop_variable.first); + // Need to sort here as variables come from an unordered container, and pushing stuff in wrong order + // will break reproducability in regression runs. + sort(begin(header_block.loop_variables), end(header_block.loop_variables)); + get(loop_variable.first).loop_variable = true; + } +} + +bool Compiler::may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var) +{ + for (auto &op : block.ops) + { + auto *ops = stream(op); + switch (op.op) + { + case OpStore: + case OpCooperativeMatrixStoreKHR: + case OpCopyMemory: + if (ops[0] == var) + return false; + break; + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + // Access chains are generally used to partially read and write. It's too hard to analyze + // if all constituents are written fully before continuing, so just assume it's preserved. + // This is the same as the parameter preservation analysis. + if (ops[2] == var) + return true; + break; + + case OpSelect: + // Variable pointers. + // We might read before writing. + if (ops[3] == var || ops[4] == var) + return true; + break; + + case OpPhi: + { + // Variable pointers. + // We might read before writing. + if (op.length < 2) + break; + + uint32_t count = op.length - 2; + for (uint32_t i = 0; i < count; i += 2) + if (ops[i + 2] == var) + return true; + break; + } + + case OpCopyObject: + case OpLoad: + case OpCooperativeVectorLoadNV: + case OpCooperativeMatrixLoadKHR: + if (ops[2] == var) + return true; + break; + + case OpFunctionCall: + { + if (op.length < 3) + break; + + // May read before writing. + uint32_t count = op.length - 3; + for (uint32_t i = 0; i < count; i++) + if (ops[i + 3] == var) + return true; + break; + } + + default: + break; + } + } + + // Not accessed somehow, at least not in a usual fashion. + // It's likely accessed in a branch, so assume we must preserve. + return true; +} + +bool Compiler::GeometryEmitDisocveryHandler::handle(Op opcode, const uint32_t *, uint32_t) +{ + if (opcode == OpEmitVertex || opcode == OpEndPrimitive) + { + for (auto *func : function_stack) + func->emits_geometry = true; + } + + return true; +} + +bool Compiler::GeometryEmitDisocveryHandler::begin_function_scope(const uint32_t *stream, uint32_t) +{ + auto &callee = compiler.get(stream[2]); + function_stack.push_back(&callee); + return true; +} + +bool Compiler::GeometryEmitDisocveryHandler::end_function_scope(const uint32_t *stream, uint32_t) +{ + (void)stream; + assert(function_stack.back() == &compiler.get(stream[2])); + function_stack.pop_back(); + + return true; +} + +void Compiler::discover_geometry_emitters() +{ + GeometryEmitDisocveryHandler handler(*this); + + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); +} + +Bitset Compiler::get_buffer_block_flags(VariableID id) const +{ + return ir.get_buffer_block_flags(get(id)); +} + +bool Compiler::get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type) +{ + if (type.basetype == SPIRType::Struct) + { + base_type = SPIRType::Unknown; + for (auto &member_type : type.member_types) + { + SPIRType::BaseType member_base; + if (!get_common_basic_type(get(member_type), member_base)) + return false; + + if (base_type == SPIRType::Unknown) + base_type = member_base; + else if (base_type != member_base) + return false; + } + return true; + } + else + { + base_type = type.basetype; + return true; + } +} + +void Compiler::ActiveBuiltinHandler::handle_builtin(const SPIRType &type, BuiltIn builtin, + const Bitset &decoration_flags) +{ + // If used, we will need to explicitly declare a new array size for these builtins. + + if (builtin == BuiltInClipDistance) + { + if (!type.array_size_literal[0]) + SPIRV_CROSS_THROW("Array size for ClipDistance must be a literal."); + uint32_t array_size = type.array[0]; + if (array_size == 0) + SPIRV_CROSS_THROW("Array size for ClipDistance must not be unsized."); + compiler.clip_distance_count = array_size; + } + else if (builtin == BuiltInCullDistance) + { + if (!type.array_size_literal[0]) + SPIRV_CROSS_THROW("Array size for CullDistance must be a literal."); + uint32_t array_size = type.array[0]; + if (array_size == 0) + SPIRV_CROSS_THROW("Array size for CullDistance must not be unsized."); + compiler.cull_distance_count = array_size; + } + else if (builtin == BuiltInPosition) + { + if (decoration_flags.get(DecorationInvariant)) + compiler.position_invariant = true; + } +} + +void Compiler::ActiveBuiltinHandler::add_if_builtin(uint32_t id, bool allow_blocks) +{ + // Only handle plain variables here. + // Builtins which are part of a block are handled in AccessChain. + // If allow_blocks is used however, this is to handle initializers of blocks, + // which implies that all members are written to. + + auto *var = compiler.maybe_get(id); + auto *m = compiler.ir.find_meta(id); + if (var && m) + { + auto &type = compiler.get(var->basetype); + auto &decorations = m->decoration; + auto &flags = type.storage == StorageClassInput ? + compiler.active_input_builtins : compiler.active_output_builtins; + if (decorations.builtin) + { + flags.set(decorations.builtin_type); + handle_builtin(type, decorations.builtin_type, decorations.decoration_flags); + } + else if (allow_blocks && compiler.has_decoration(type.self, DecorationBlock)) + { + uint32_t member_count = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + if (compiler.has_member_decoration(type.self, i, DecorationBuiltIn)) + { + auto &member_type = compiler.get(type.member_types[i]); + BuiltIn builtin = BuiltIn(compiler.get_member_decoration(type.self, i, DecorationBuiltIn)); + flags.set(builtin); + handle_builtin(member_type, builtin, compiler.get_member_decoration_bitset(type.self, i)); + } + } + } + } +} + +void Compiler::ActiveBuiltinHandler::add_if_builtin(uint32_t id) +{ + add_if_builtin(id, false); +} + +void Compiler::ActiveBuiltinHandler::add_if_builtin_or_block(uint32_t id) +{ + add_if_builtin(id, true); +} + +bool Compiler::ActiveBuiltinHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + switch (opcode) + { + case OpStore: + case OpCooperativeMatrixStoreKHR: + if (length < 1) + return false; + + add_if_builtin(args[0]); + break; + + case OpCopyMemory: + if (length < 2) + return false; + + add_if_builtin(args[0]); + add_if_builtin(args[1]); + break; + + case OpCopyObject: + case OpLoad: + case OpCooperativeMatrixLoadKHR: + if (length < 3) + return false; + + add_if_builtin(args[2]); + break; + + case OpSelect: + if (length < 5) + return false; + + add_if_builtin(args[3]); + add_if_builtin(args[4]); + break; + + case OpPhi: + { + if (length < 2) + return false; + + uint32_t count = length - 2; + args += 2; + for (uint32_t i = 0; i < count; i += 2) + add_if_builtin(args[i]); + break; + } + + case OpFunctionCall: + { + if (length < 3) + return false; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + add_if_builtin(args[i]); + break; + } + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + { + if (length < 4) + return false; + + // Only consider global variables, cannot consider variables in functions yet, or other + // access chains as they have not been created yet. + auto *var = compiler.maybe_get(args[2]); + if (!var) + break; + + // Required if we access chain into builtins like gl_GlobalInvocationID. + add_if_builtin(args[2]); + + // Start traversing type hierarchy at the proper non-pointer types. + auto *type = &compiler.get_variable_data_type(*var); + + auto &flags = + var->storage == StorageClassInput ? compiler.active_input_builtins : compiler.active_output_builtins; + + uint32_t count = length - 3; + args += 3; + for (uint32_t i = 0; i < count; i++) + { + // Pointers + // PtrAccessChain functions more like a pointer offset. Type remains the same. + if (opcode == OpPtrAccessChain && i == 0) + continue; + + // Arrays + if (!type->array.empty()) + { + type = &compiler.get(type->parent_type); + } + // Structs + else if (type->basetype == SPIRType::Struct) + { + uint32_t index = compiler.get(args[i]).scalar(); + + if (index < uint32_t(compiler.ir.meta[type->self].members.size())) + { + auto &decorations = compiler.ir.meta[type->self].members[index]; + if (decorations.builtin) + { + flags.set(decorations.builtin_type); + handle_builtin(compiler.get(type->member_types[index]), decorations.builtin_type, + decorations.decoration_flags); + } + } + + type = &compiler.get(type->member_types[index]); + } + else + { + // No point in traversing further. We won't find any extra builtins. + break; + } + } + break; + } + + default: + break; + } + + return true; +} + +void Compiler::update_active_builtins() +{ + active_input_builtins.reset(); + active_output_builtins.reset(); + cull_distance_count = 0; + clip_distance_count = 0; + ActiveBuiltinHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (var.storage != StorageClassOutput) + return; + if (!interface_variable_exists_in_entry_point(var.self)) + return; + + // Also, make sure we preserve output variables which are only initialized, but never accessed by any code. + if (var.initializer != ID(0)) + handler.add_if_builtin_or_block(var.self); + }); +} + +// Returns whether this shader uses a builtin of the storage class +bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage) const +{ + const Bitset *flags; + switch (storage) + { + case StorageClassInput: + flags = &active_input_builtins; + break; + case StorageClassOutput: + flags = &active_output_builtins; + break; + + default: + return false; + } + return flags->get(builtin); +} + +void Compiler::analyze_image_and_sampler_usage() +{ + CombinedImageSamplerDrefHandler dref_handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), dref_handler); + + CombinedImageSamplerUsageHandler handler(*this, dref_handler.dref_combined_samplers); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + // Need to run this traversal twice. First time, we propagate any comparison sampler usage from leaf functions + // down to main(). + // In the second pass, we can propagate up forced depth state coming from main() up into leaf functions. + handler.dependency_hierarchy.clear(); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + comparison_ids = std::move(handler.comparison_ids); + need_subpass_input = handler.need_subpass_input; + need_subpass_input_ms = handler.need_subpass_input_ms; + + // 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(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; +} + +const CFG &Compiler::get_cfg_for_current_function() const +{ + assert(current_function); + return get_cfg_for_function(current_function->self); +} + +const CFG &Compiler::get_cfg_for_function(uint32_t id) const +{ + auto cfg_itr = function_cfgs.find(id); + assert(cfg_itr != end(function_cfgs)); + assert(cfg_itr->second); + return *cfg_itr->second; +} + +void Compiler::build_function_control_flow_graphs_and_analyze() +{ + CFGBuilder handler(*this); + handler.function_cfgs[ir.default_entry_point].reset(new CFG(*this, get(ir.default_entry_point))); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + function_cfgs = std::move(handler.function_cfgs); + bool single_function = function_cfgs.size() <= 1; + + for (auto &f : function_cfgs) + { + auto &func = get(f.first); + AnalyzeVariableScopeAccessHandler scope_handler(*this, func); + analyze_variable_scope(func, scope_handler); + find_function_local_luts(func, scope_handler, single_function); + + // Check if we can actually use the loop variables we found in analyze_variable_scope. + // To use multiple initializers, we need the same type and qualifiers. + for (auto block : func.blocks) + { + auto &b = get(block); + if (b.loop_variables.size() < 2) + continue; + + auto &flags = get_decoration_bitset(b.loop_variables.front()); + uint32_t type = get(b.loop_variables.front()).basetype; + bool invalid_initializers = false; + for (auto loop_variable : b.loop_variables) + { + if (flags != get_decoration_bitset(loop_variable) || + type != get(b.loop_variables.front()).basetype) + { + invalid_initializers = true; + break; + } + } + + if (invalid_initializers) + { + for (auto loop_variable : b.loop_variables) + get(loop_variable).loop_variable = false; + b.loop_variables.clear(); + } + } + } + + // Find LUTs which are not function local. Only consider this case if the CFG is multi-function, + // otherwise we treat Private as Function trivially. + // Needs to be analyzed from the outside since we have to block the LUT optimization if at least + // one function writes to it. + if (!single_function) + { + for (auto &id : global_variables) + { + auto &var = get(id); + auto &type = get_variable_data_type(var); + + if (is_array(type) && var.storage == StorageClassPrivate && + var.initializer && !var.is_written_to && + ir.ids[var.initializer].get_type() == TypeConstant) + { + get(var.initializer).is_used_as_lut = true; + var.static_expression = var.initializer; + var.statically_assigned = true; + var.remapped_variable = true; + } + } + } +} + +Compiler::CFGBuilder::CFGBuilder(Compiler &compiler_) + : OpcodeHandler(compiler_) +{ +} + +bool Compiler::CFGBuilder::handle(Op, const uint32_t *, uint32_t) +{ + return true; +} + +bool Compiler::CFGBuilder::follow_function_call(const SPIRFunction &func) +{ + if (function_cfgs.find(func.self) == end(function_cfgs)) + { + function_cfgs[func.self].reset(new CFG(compiler, func)); + return true; + } + else + return false; +} + +void Compiler::CombinedImageSamplerUsageHandler::add_dependency(uint32_t dst, uint32_t src) +{ + dependency_hierarchy[dst].insert(src); + // Propagate up any comparison state if we're loading from one such variable. + if (comparison_ids.count(src)) + comparison_ids.insert(dst); +} + +bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + auto &func = compiler.get(args[2]); + const auto *arg = &args[3]; + length -= 3; + + for (uint32_t i = 0; i < length; i++) + { + auto &argument = func.arguments[i]; + add_dependency(argument.id, arg[i]); + } + + return true; +} + +void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_ids(uint32_t id) +{ + // 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) +{ + switch (opcode) + { + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + case OpLoad: + { + if (length < 3) + return false; + + add_dependency(args[1], args[2]); + + // Ideally defer this to OpImageRead, but then we'd need to track loaded IDs. + // If we load an image, we're going to use it and there is little harm in declaring an unused gl_FragCoord. + auto &type = compiler.get(args[0]); + if (type.image.dim == DimSubpassData) + { + need_subpass_input = true; + if (type.image.ms) + need_subpass_input_ms = 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; + } + + case OpSampledImage: + { + if (length < 4) + return false; + + // If the underlying resource has been used for comparison then duplicate loads of that resource must be too. + // This image must be a depth image. + uint32_t result_id = args[1]; + uint32_t image = args[2]; + uint32_t sampler = args[3]; + + if (dref_combined_samplers.count(result_id) != 0) + { + add_hierarchy_to_comparison_ids(image); + + // This sampler must be a SamplerComparisonState, and not a regular SamplerState. + add_hierarchy_to_comparison_ids(sampler); + + // Mark the OpSampledImage itself as being comparison state. + comparison_ids.insert(result_id); + } + return true; + } + + default: + break; + } + + return true; +} + +bool Compiler::buffer_is_hlsl_counter_buffer(VariableID id) const +{ + auto *m = ir.find_meta(id); + return m && m->hlsl_is_magic_counter_buffer; +} + +bool Compiler::buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const +{ + auto *m = ir.find_meta(id); + + // First, check for the proper decoration. + if (m && m->hlsl_magic_counter_buffer != 0) + { + counter_id = m->hlsl_magic_counter_buffer; + return true; + } + else + return false; +} + +void Compiler::make_constant_null(uint32_t id, uint32_t type) +{ + auto &constant_type = get(type); + + if (constant_type.pointer) + { + auto &constant = set(id, type); + constant.make_null(constant_type); + } + else if (!constant_type.array.empty()) + { + assert(constant_type.parent_type); + uint32_t parent_id = ir.increase_bound_by(1); + make_constant_null(parent_id, constant_type.parent_type); + + // The array size of OpConstantNull can be either literal or specialization constant. + // In the latter case, we cannot take the value as-is, as it can be changed to anything. + // Rather, we assume it to be *one* for the sake of initializer. + bool is_literal_array_size = constant_type.array_size_literal.back(); + uint32_t count = is_literal_array_size ? constant_type.array.back() : 1; + SmallVector elements(count); + for (uint32_t i = 0; i < count; i++) + elements[i] = parent_id; + auto &constant = set(id, type, elements.data(), uint32_t(elements.size()), false); + constant.is_null_array_specialized_length = !is_literal_array_size; + } + else if (!constant_type.member_types.empty()) + { + uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size())); + SmallVector elements(constant_type.member_types.size()); + for (uint32_t i = 0; i < constant_type.member_types.size(); i++) + { + make_constant_null(member_ids + i, constant_type.member_types[i]); + elements[i] = member_ids + i; + } + set(id, type, elements.data(), uint32_t(elements.size()), false); + } + else + { + auto &constant = set(id, type); + constant.make_null(constant_type); + } +} + +const SmallVector &Compiler::get_declared_capabilities() const +{ + return ir.declared_capabilities; +} + +const SmallVector &Compiler::get_declared_extensions() const +{ + return ir.declared_extensions; +} + +std::string Compiler::get_remapped_declared_block_name(VariableID id) const +{ + return get_remapped_declared_block_name(id, false); +} + +std::string Compiler::get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const +{ + auto itr = declared_block_names.find(id); + if (itr != end(declared_block_names)) + { + return itr->second; + } + else + { + auto &var = get(id); + + if (fallback_prefer_instance_name) + { + return to_name(var.self); + } + else + { + auto &type = get(var.basetype); + auto *type_meta = ir.find_meta(type.self); + auto *block_name = type_meta ? &type_meta->decoration.alias : nullptr; + return (!block_name || block_name->empty()) ? get_block_fallback_name(id) : *block_name; + } + } +} + +bool Compiler::reflection_ssbo_instance_name_is_significant() const +{ + if (!ir.sources.empty() && ir.sources[0].known) + { + // UAVs from HLSL source tend to be declared in a way where the type is reused + // but the instance name is significant, and that's the name we should report. + // For GLSL, SSBOs each have their own block type as that's how GLSL is written. + return ir.sources[0].hlsl; + } + + unordered_set ssbo_type_ids; + bool aliased_ssbo_types = false; + + // If we don't have any OpSource information, we need to perform some shaky heuristics. + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + auto &type = this->get(var.basetype); + if (!type.pointer || var.storage == StorageClassFunction) + return; + + bool ssbo = var.storage == StorageClassStorageBuffer || + (var.storage == StorageClassUniform && has_decoration(type.self, DecorationBufferBlock)); + + if (ssbo) + { + if (ssbo_type_ids.count(type.self)) + aliased_ssbo_types = true; + else + ssbo_type_ids.insert(type.self); + } + }); + + // If the block name is aliased, assume we have HLSL-style UAV declarations. + return aliased_ssbo_types; +} + +bool Compiler::instruction_to_result_type(uint32_t &result_type, uint32_t &result_id, Op op, + const uint32_t *args, uint32_t length) +{ + if (length < 2) + return false; + + bool has_result_id = false, has_result_type = false; + HasResultAndType(op, &has_result_id, &has_result_type); + if (has_result_id && has_result_type) + { + result_type = args[0]; + result_id = args[1]; + return true; + } + else + return false; +} + +Bitset Compiler::combined_decoration_for_member(const SPIRType &type, uint32_t index) const +{ + Bitset flags; + auto *type_meta = ir.find_meta(type.self); + + if (type_meta) + { + auto &members = type_meta->members; + if (index >= members.size()) + return flags; + auto &dec = members[index]; + + flags.merge_or(dec.decoration_flags); + + auto &member_type = get(type.member_types[index]); + + // If our member type is a struct, traverse all the child members as well recursively. + auto &member_childs = member_type.member_types; + for (uint32_t i = 0; i < member_childs.size(); i++) + { + auto &child_member_type = get(member_childs[i]); + if (!child_member_type.pointer) + flags.merge_or(combined_decoration_for_member(member_type, i)); + } + } + + return flags; +} + +bool Compiler::is_desktop_only_format(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; +} + +// An image is determined to be a depth image if it is marked as a depth image and is not also +// explicitly marked with a color format, or if there are any sample/gather compare operations on it. +bool Compiler::is_depth_image(const SPIRType &type, uint32_t id) const +{ + return (type.image.depth && type.image.format == ImageFormatUnknown) || comparison_ids.count(id); +} + +bool Compiler::type_is_opaque_value(const SPIRType &type) const +{ + return !type.pointer && (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Image || + type.basetype == SPIRType::Sampler || type.basetype == SPIRType::Tensor); +} + +// Make these member functions so we can easily break on any force_recompile events. +void Compiler::force_recompile() +{ + is_force_recompile = true; +} + +void Compiler::force_recompile_guarantee_forward_progress() +{ + force_recompile(); + is_force_recompile_forward_progress = true; +} + +bool Compiler::is_forcing_recompilation() const +{ + return is_force_recompile; +} + +void Compiler::clear_force_recompile() +{ + is_force_recompile = false; + is_force_recompile_forward_progress = false; +} + +Compiler::PhysicalStorageBufferPointerHandler::PhysicalStorageBufferPointerHandler(Compiler &compiler_) + : OpcodeHandler(compiler_) +{ +} + +Compiler::PhysicalBlockMeta *Compiler::PhysicalStorageBufferPointerHandler::find_block_meta(uint32_t id) const +{ + auto chain_itr = access_chain_to_physical_block.find(id); + if (chain_itr != access_chain_to_physical_block.end()) + return chain_itr->second; + else + return nullptr; +} + +void Compiler::PhysicalStorageBufferPointerHandler::mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length) +{ + uint32_t mask = *args; + args++; + length--; + if (length && (mask & MemoryAccessVolatileMask) != 0) + { + args++; + length--; + } + + if (length && (mask & MemoryAccessAlignedMask) != 0) + { + uint32_t alignment = *args; + auto *meta = find_block_meta(id); + + // This makes the assumption that the application does not rely on insane edge cases like: + // Bind buffer with ADDR = 8, use block offset of 8 bytes, load/store with 16 byte alignment. + // If we emit the buffer with alignment = 16 here, the first element at offset = 0 should + // actually have alignment of 8 bytes, but this is too theoretical and awkward to support. + // We could potentially keep track of any offset in the access chain, but it's + // practically impossible for high level compilers to emit code like that, + // so deducing overall alignment requirement based on maximum observed Alignment value is probably fine. + if (meta && alignment > meta->alignment) + meta->alignment = alignment; + } +} + +bool Compiler::PhysicalStorageBufferPointerHandler::type_is_bda_block_entry(uint32_t type_id) const +{ + auto &type = compiler.get(type_id); + return compiler.is_physical_pointer(type); +} + +uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_minimum_scalar_alignment(const SPIRType &type) const +{ + if (type.storage == StorageClassPhysicalStorageBuffer) + return 8; + else if (type.basetype == SPIRType::Struct) + { + uint32_t alignment = 0; + for (auto &member_type : type.member_types) + { + uint32_t member_align = get_minimum_scalar_alignment(compiler.get(member_type)); + if (member_align > alignment) + alignment = member_align; + } + return alignment; + } + else + return type.width / 8; +} + +void Compiler::PhysicalStorageBufferPointerHandler::setup_meta_chain(uint32_t type_id, uint32_t var_id) +{ + if (type_is_bda_block_entry(type_id)) + { + auto &meta = physical_block_type_meta[type_id]; + access_chain_to_physical_block[var_id] = &meta; + + auto &type = compiler.get(type_id); + + if (!compiler.is_physical_pointer_to_buffer_block(type)) + non_block_types.insert(type_id); + + if (meta.alignment == 0) + meta.alignment = get_minimum_scalar_alignment(compiler.get_pointee_type(type)); + } +} + +bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t length) +{ + // When a BDA pointer comes to life, we need to keep a mapping of SSA ID -> type ID for the pointer type. + // For every load and store, we'll need to be able to look up the type ID being accessed and mark any alignment + // requirements. + switch (op) + { + case OpConvertUToPtr: + case OpBitcast: + case OpCompositeExtract: + // Extract can begin a new chain if we had a struct or array of pointers as input. + // We don't begin chains before we have a pure scalar pointer. + setup_meta_chain(args[0], args[1]); + break; + + case OpAccessChain: + case OpInBoundsAccessChain: + case OpPtrAccessChain: + case OpCopyObject: + { + auto itr = access_chain_to_physical_block.find(args[2]); + if (itr != access_chain_to_physical_block.end()) + access_chain_to_physical_block[args[1]] = itr->second; + break; + } + + case OpLoad: + { + setup_meta_chain(args[0], args[1]); + if (length >= 4) + mark_aligned_access(args[2], args + 3, length - 3); + break; + } + + case OpStore: + { + if (length >= 3) + mark_aligned_access(args[0], args + 2, length - 2); + break; + } + + case OpCooperativeMatrixLoadKHR: + case OpCooperativeMatrixStoreKHR: + { + // TODO: Can we meaningfully deal with this? + break; + } + + default: + break; + } + + return true; +} + +uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_base_non_block_type_id(uint32_t type_id) const +{ + auto *type = &compiler.get(type_id); + while (compiler.is_physical_pointer(*type) && !type_is_bda_block_entry(type_id)) + { + type_id = type->parent_type; + type = &compiler.get(type_id); + } + + assert(type_is_bda_block_entry(type_id)); + return type_id; +} + +void Compiler::PhysicalStorageBufferPointerHandler::analyze_non_block_types_from_block(const SPIRType &type) +{ + if (analyzed_type_ids.count(type.self)) + return; + analyzed_type_ids.insert(type.self); + + for (auto &member : type.member_types) + { + auto &subtype = compiler.get(member); + + if (compiler.is_physical_pointer(subtype) && !compiler.is_physical_pointer_to_buffer_block(subtype)) + non_block_types.insert(get_base_non_block_type_id(member)); + else if (subtype.basetype == SPIRType::Struct && !compiler.is_pointer(subtype)) + analyze_non_block_types_from_block(subtype); + } +} + +void Compiler::analyze_non_block_pointer_types() +{ + PhysicalStorageBufferPointerHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + // Analyze any block declaration we have to make. It might contain + // physical pointers to POD types which we never used, and thus never added to the list. + // We'll need to add those pointer types to the set of types we declare. + ir.for_each_typed_id([&](uint32_t id, SPIRType &type) { + // Only analyze the raw block struct, not any pointer-to-struct, since that's just redundant. + if (type.self == id && + (has_decoration(type.self, DecorationBlock) || + has_decoration(type.self, DecorationBufferBlock))) + { + handler.analyze_non_block_types_from_block(type); + } + }); + + physical_storage_non_block_pointer_types.reserve(handler.non_block_types.size()); + for (auto type : handler.non_block_types) + physical_storage_non_block_pointer_types.push_back(type); + sort(begin(physical_storage_non_block_pointer_types), end(physical_storage_non_block_pointer_types)); + physical_storage_type_to_alignment = std::move(handler.physical_block_type_meta); +} + +void Compiler::analyze_descriptor_heap_types() +{ + struct HeapHandler : OpcodeHandler + { + bool handle(Op opcode, const uint32_t *args, uint32_t) override + { + switch (opcode) + { + case OpBufferPointerEXT: + { + auto &ptr_type = compiler.get(args[0]); + // BufferPointerEXT can return untyped or typed pointers. + // If it's typed, we resolve it here. + if (ptr_type.basetype == SPIRType::Struct) + { + DescriptorHeapMeta meta = {}; + meta.type = ptr_type.self; + meta.hlsl_style_stride = hlsl_style_stride_access_chains.count(args[2]); + meta.buffer_pointer_id = args[1]; + meta.storage = ptr_type.storage; + meta.nonreadable = compiler.has_decoration(args[1], DecorationNonReadable); + meta.nonwritable = compiler.has_decoration(args[1], DecorationNonWritable); + meta.coherent = compiler.has_decoration(args[1], DecorationCoherent); + meta.is_restrict = compiler.has_decoration(args[1], DecorationRestrict); + meta.is_volatile = compiler.has_decoration(args[1], DecorationVolatile); + add_unique_type(meta); + } + buffer_pointers[args[1]] = { args[0], hlsl_style_stride_access_chains.count(args[2]) != 0 }; + break; + } + + case OpUntypedAccessChainKHR: + case OpUntypedInBoundsAccessChainKHR: + case OpUntypedArrayLengthKHR: + { + auto &data_type = compiler.get(args[2]); + + if (compiler.is_pointer(data_type)) + SPIRV_CROSS_THROW("pointer type not allowed."); + + bool hlsl_style_stride = false; + + // Need to validate the array stride and types. HLLs are not flexible enough to support the full flexibility of SPIR-V. + if (BuiltIn(compiler.get_decoration(args[3], DecorationBuiltIn)) == BuiltInResourceHeapEXT) + { + if (!compiler.is_runtime_size_array(data_type)) + SPIRV_CROSS_THROW("Descriptor heap must be accessed as a runtime array."); + + // The only meaningful use of this is ArrayStride equal to sizeof(type) right now. + uint32_t array_stride_id = compiler.get_decoration(args[2], DecorationArrayStrideIdEXT); + if (!array_stride_id) + SPIRV_CROSS_THROW("Expected ArrayStrideIdEXT to be set for resource heap."); + + auto *spec_c = compiler.maybe_get(array_stride_id); + auto *c = compiler.maybe_get(array_stride_id); + + if (!spec_c && !c) + SPIRV_CROSS_THROW("Array stride must be some constant expression."); + + if (spec_c) + { + // This gets potentially infinitely weird, but if we get HLSL-style shaders + // we expect the array stride to be max(buffer, image) since all descriptors have equal size in D3D12. + // We just have to be a bit loose here since it's impossible to anticipate every theoretical formulation. + // Anything non-conforming to strict GLSL is flagged in the codegen output. + if (spec_c->opcode == OpSelect) + { + auto *true_value = compiler.maybe_get(spec_c->arguments[1]); + auto *false_value = compiler.maybe_get(spec_c->arguments[2]); + hlsl_style_stride = true_value && true_value->size_of_type && + false_value && false_value->size_of_type; + } + + if (!hlsl_style_stride) + SPIRV_CROSS_THROW("Unusual pattern of descriptor stride detected. This probably cannot be expressed in current GLSL."); + } + + if (c && !c->size_of_type) + SPIRV_CROSS_THROW("Resource heap array stride must be ConstantSizeOfEXT for high level languages."); + + auto &element_type = compiler.get(data_type.parent_type); + + if (element_type.basetype == SPIRType::DescriptorHeapBuffer) + { + if (c && compiler.get(c->size_of_type).basetype != SPIRType::DescriptorHeapBuffer) + SPIRV_CROSS_THROW("Buffer descriptors in heap must be ConstantSizeOfEXT(OpTypeBufferEXT) for GLSL."); + } + else if (data_type.basetype == SPIRType::Image) + { + if (c && compiler.get(c->size_of_type).basetype != SPIRType::Image) + SPIRV_CROSS_THROW("Image descriptors in heap must be ConstantSizeOfEXT(OpTypeImage) for GLSL."); + } + else if (data_type.basetype == SPIRType::AccelerationStructure) + { + if (c && compiler.get(c->size_of_type).basetype != SPIRType::AccelerationStructure) + SPIRV_CROSS_THROW("RTAS descriptors in heap must be ConstantSizeOfEXT(OpTypeAccelerationStructure) for GLSL."); + } + } + else if (BuiltIn(compiler.get_decoration(args[3], DecorationBuiltIn)) == BuiltInSamplerHeapEXT) + { + if (!compiler.is_runtime_size_array(data_type)) + SPIRV_CROSS_THROW("Descriptor heap must be accessed as a runtime array."); + + // The only meaningful use of this is ArrayStride equal to sizeof(sampler) right now. + uint32_t array_stride_id = compiler.get_decoration(args[2], DecorationArrayStrideIdEXT); + if (!array_stride_id) + SPIRV_CROSS_THROW("Expected ArrayStrideIdEXT to be set for sampler heap."); + + auto *c = compiler.maybe_get(array_stride_id); + if (!c || !c->size_of_type || compiler.get(c->size_of_type).basetype != SPIRType::Sampler) + SPIRV_CROSS_THROW("Sampler heap array stride must be ConstantSizeOfEXT(OpTypeSampler) for high level languages."); + } + + // Remember this for OpBufferPointerEXT. + if (hlsl_style_stride) + hlsl_style_stride_access_chains.insert(args[1]); + + if (data_type.basetype == SPIRType::SampledImage) + { + SPIRV_CROSS_THROW("Attempting to access heap as combined sampler image. This does not make sense."); + } + else if (data_type.basetype == SPIRType::Image || + data_type.basetype == SPIRType::AccelerationStructure || + data_type.basetype == SPIRType::Sampler) + { + DescriptorHeapMeta meta = {}; + meta.type = data_type.self; + meta.hlsl_style_stride = hlsl_style_stride; + add_unique_type(meta); + } + else if (buffer_pointers.count(args[3]) != 0) + { + if (!compiler.has_decoration(data_type.self, DecorationBlock) && + !compiler.has_decoration(data_type.self, DecorationBufferBlock)) + { + SPIRV_CROSS_THROW("BufferPointerEXT must reference a block type."); + } + + auto &pointer_meta = buffer_pointers[args[3]]; + auto &buffer_type = compiler.get(pointer_meta.type); + if (buffer_type.basetype == SPIRType::Void) + { + // This is where the pointer becomes typed, so register it here. + DescriptorHeapMeta meta = {}; + meta.type = data_type.self; + meta.hlsl_style_stride = pointer_meta.hlsl_style_stride; + meta.buffer_pointer_id = args[3]; + meta.storage = buffer_type.storage; + meta.nonreadable = compiler.has_decoration(args[3], DecorationNonReadable); + meta.nonwritable = compiler.has_decoration(args[3], DecorationNonWritable); + meta.coherent = compiler.has_decoration(args[3], DecorationCoherent); + meta.is_volatile = compiler.has_decoration(args[3], DecorationVolatile); + meta.is_restrict = compiler.has_decoration(args[3], DecorationRestrict); + add_unique_type(meta); + } + } + break; + } + + default: + break; + } + + return true; + } + + explicit HeapHandler(Compiler &compiler_) : OpcodeHandler(compiler_) {} + + std::vector heap_types; + + struct BufferPointerMeta + { + TypeID type; + bool hlsl_style_stride; + }; + std::unordered_map buffer_pointers; + std::unordered_set hlsl_style_stride_access_chains; + + void add_unique_type(const DescriptorHeapMeta &meta) + { + assert(meta.type != 0); + + for (auto &type : heap_types) + { + if (type.type == meta.type && type.storage == meta.storage && + type.buffer_pointer_id == meta.buffer_pointer_id && + type.nonreadable == meta.nonreadable && + type.nonwritable == meta.nonwritable && + type.coherent == meta.coherent && + type.is_restrict == meta.is_restrict && + type.hlsl_style_stride == meta.hlsl_style_stride && + type.is_volatile == meta.is_volatile) + { + return; + } + } + + heap_types.push_back(meta); + } + }; + + HeapHandler handler(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + descriptor_heap_types = std::move(handler.heap_types); +} + +bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t) +{ + if (op == OpBeginInvocationInterlockEXT || op == OpEndInvocationInterlockEXT) + { + if (interlock_function_id != 0 && interlock_function_id != call_stack.back()) + { + // Most complex case, we have no sensible way of dealing with this + // other than taking the 100% conservative approach, exit early. + split_function_case = true; + return false; + } + else + { + interlock_function_id = call_stack.back(); + // If this call is performed inside control flow we have a problem. + auto &cfg = compiler.get_cfg_for_function(interlock_function_id); + + uint32_t from_block_id = compiler.get(interlock_function_id).entry_block; + bool outside_control_flow = cfg.node_terminates_control_flow_in_sub_graph(from_block_id, current_block_id); + if (!outside_control_flow) + control_flow_interlock = true; + } + } + return true; +} + +void Compiler::InterlockedResourceAccessPrepassHandler::rearm_current_block(const SPIRBlock &block) +{ + current_block_id = block.self; +} + +bool Compiler::InterlockedResourceAccessPrepassHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + call_stack.push_back(args[2]); + return true; +} + +bool Compiler::InterlockedResourceAccessPrepassHandler::end_function_scope(const uint32_t *, uint32_t) +{ + call_stack.pop_back(); + return true; +} + +bool Compiler::InterlockedResourceAccessHandler::begin_function_scope(const uint32_t *args, uint32_t length) +{ + if (length < 3) + return false; + + if (args[2] == interlock_function_id) + call_stack_is_interlocked = true; + + call_stack.push_back(args[2]); + return true; +} + +bool Compiler::InterlockedResourceAccessHandler::end_function_scope(const uint32_t *, uint32_t) +{ + if (call_stack.back() == interlock_function_id) + call_stack_is_interlocked = false; + + call_stack.pop_back(); + return true; +} + +void Compiler::InterlockedResourceAccessHandler::access_potential_resource(uint32_t id) +{ + if ((use_critical_section && in_crit_sec) || (control_flow_interlock && call_stack_is_interlocked) || + split_function_case) + { + compiler.interlocked_resources.insert(id); + } +} + +bool Compiler::InterlockedResourceAccessHandler::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + // Only care about critical section analysis if we have simple case. + if (use_critical_section) + { + if (opcode == OpBeginInvocationInterlockEXT) + { + in_crit_sec = true; + return true; + } + + if (opcode == OpEndInvocationInterlockEXT) + { + // End critical section--nothing more to do. + return false; + } + } + + // We need to figure out where images and buffers are loaded from, so do only the bare bones compilation we need. + switch (opcode) + { + case OpLoad: + case OpCooperativeMatrixLoadKHR: + case OpCooperativeVectorLoadNV: + { + if (length < 3) + return false; + + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + + // We're only concerned with buffer and image memory here. + if (!var) + break; + + switch (var->storage) + { + default: + break; + + case StorageClassUniformConstant: + { + uint32_t result_type = args[0]; + uint32_t id = args[1]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + break; + } + + case StorageClassUniform: + // Must have BufferBlock; we only care about SSBOs. + if (!compiler.has_decoration(compiler.get(var->basetype).self, DecorationBufferBlock)) + break; + // fallthrough + case StorageClassStorageBuffer: + access_potential_resource(var->self); + break; + } + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + if (type.storage == StorageClassUniform || type.storage == StorageClassUniformConstant || + type.storage == StorageClassStorageBuffer) + { + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + compiler.ir.ids[id].set_allow_type_rewrite(); + } + break; + } + + case OpImageTexelPointer: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + uint32_t id = args[1]; + uint32_t ptr = args[2]; + auto &e = compiler.set(id, "", result_type, true); + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var) + e.loaded_from = var->self; + break; + } + + case OpStore: + case OpImageWrite: + case OpAtomicStore: + case OpCooperativeMatrixStoreKHR: + case OpCooperativeVectorStoreNV: + { + if (length < 1) + return false; + + uint32_t ptr = args[0]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var && (var->storage == StorageClassUniform || var->storage == StorageClassUniformConstant || + var->storage == StorageClassStorageBuffer)) + { + access_potential_resource(var->self); + } + + break; + } + + case OpCopyMemory: + { + if (length < 2) + return false; + + uint32_t dst = args[0]; + uint32_t src = args[1]; + auto *dst_var = compiler.maybe_get_backing_variable(dst); + auto *src_var = compiler.maybe_get_backing_variable(src); + + if (dst_var && (dst_var->storage == StorageClassUniform || dst_var->storage == StorageClassStorageBuffer)) + access_potential_resource(dst_var->self); + + if (src_var) + { + if (src_var->storage != StorageClassUniform && src_var->storage != StorageClassStorageBuffer) + break; + + if (src_var->storage == StorageClassUniform && + !compiler.has_decoration(compiler.get(src_var->basetype).self, DecorationBufferBlock)) + { + break; + } + + access_potential_resource(src_var->self); + } + + break; + } + + case OpImageRead: + case OpAtomicLoad: + { + if (length < 3) + return false; + + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + + // We're only concerned with buffer and image memory here. + if (!var) + break; + + switch (var->storage) + { + default: + break; + + case StorageClassUniform: + // Must have BufferBlock; we only care about SSBOs. + if (!compiler.has_decoration(compiler.get(var->basetype).self, DecorationBufferBlock)) + break; + // fallthrough + case StorageClassUniformConstant: + case StorageClassStorageBuffer: + access_potential_resource(var->self); + break; + } + break; + } + + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + { + if (length < 3) + return false; + + uint32_t ptr = args[2]; + auto *var = compiler.maybe_get_backing_variable(ptr); + if (var && (var->storage == StorageClassUniform || var->storage == StorageClassUniformConstant || + var->storage == StorageClassStorageBuffer)) + { + access_potential_resource(var->self); + } + + break; + } + + default: + break; + } + + return true; +} + +void Compiler::analyze_interlocked_resource_usage() +{ + if (get_execution_model() == ExecutionModelFragment && + (get_entry_point().flags.get(ExecutionModePixelInterlockOrderedEXT) || + get_entry_point().flags.get(ExecutionModePixelInterlockUnorderedEXT) || + get_entry_point().flags.get(ExecutionModeSampleInterlockOrderedEXT) || + get_entry_point().flags.get(ExecutionModeSampleInterlockUnorderedEXT))) + { + InterlockedResourceAccessPrepassHandler prepass_handler(*this, ir.default_entry_point); + traverse_all_reachable_opcodes(get(ir.default_entry_point), prepass_handler); + + InterlockedResourceAccessHandler handler(*this, ir.default_entry_point); + handler.interlock_function_id = prepass_handler.interlock_function_id; + handler.split_function_case = prepass_handler.split_function_case; + handler.control_flow_interlock = prepass_handler.control_flow_interlock; + handler.use_critical_section = !handler.split_function_case && !handler.control_flow_interlock; + + traverse_all_reachable_opcodes(get(ir.default_entry_point), handler); + + // For GLSL. If we hit any of these cases, we have to fall back to conservative approach. + interlocked_is_complex = + !handler.use_critical_section || handler.interlock_function_id != ir.default_entry_point; + } +} + +// Helper function +bool Compiler::check_internal_recursion(const SPIRType &type, std::unordered_set &checked_ids) +{ + if (type.basetype != SPIRType::Struct) + return false; + + if (checked_ids.count(type.self)) + return true; + + // Recurse into struct members + bool is_recursive = false; + checked_ids.insert(type.self); + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t mbr_idx = 0; !is_recursive && mbr_idx < mbr_cnt; mbr_idx++) + { + uint32_t mbr_type_id = type.member_types[mbr_idx]; + auto &mbr_type = get(mbr_type_id); + is_recursive |= check_internal_recursion(mbr_type, checked_ids); + } + checked_ids.erase(type.self); + return is_recursive; +} + +// Return whether the struct type contains a structural recursion nested somewhere within its content. +bool Compiler::type_contains_recursion(const SPIRType &type) +{ + std::unordered_set checked_ids; + return check_internal_recursion(type, checked_ids); +} + +bool Compiler::type_is_array_of_pointers(const SPIRType &type) const +{ + if (!is_array(type)) + return false; + + // BDA types must have parent type hierarchy. + if (!type.parent_type) + return false; + + // Punch through all array layers. + auto *parent = &get(type.parent_type); + while (is_array(*parent)) + parent = &get(parent->parent_type); + + return is_pointer(*parent); +} + +bool Compiler::flush_phi_required(BlockID from, BlockID to) const +{ + auto &child = get(to); + for (auto &phi : child.phi_variables) + if (phi.parent == from) + return true; + return false; +} + +void Compiler::add_loop_level() +{ + current_loop_level++; +} + +const SPIRType *Compiler::OpcodeHandler::get_expression_result_type(uint32_t id) const +{ + auto itr = result_types.find(id); + if (itr == result_types.end()) + return nullptr; + + return &compiler.get(itr->second); +} diff --git a/thirdparty/SPIRV-Cross/spirv_cross.hpp b/thirdparty/SPIRV-Cross/spirv_cross.hpp new file mode 100644 index 000000000..d8b05169f --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross.hpp @@ -0,0 +1,1264 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_HPP +#define SPIRV_CROSS_HPP + +#ifndef SPV_ENABLE_UTILITY_CODE +#define SPV_ENABLE_UTILITY_CODE +#endif + +// Pragmatic hack to avoid symbol conflicts when including both hpp11 and hpp headers in same translation unit. +// This is an unfortunate SPIRV-Headers issue that we cannot easily deal with ourselves. +#ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE +#define spv SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE +#endif + +#include "spirv.hpp" +#include "spirv_cfg.hpp" +#include "spirv_cross_parsed_ir.hpp" + +namespace SPIRV_CROSS_NAMESPACE +{ +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +struct Resource +{ + // Resources are identified with their SPIR-V ID. + // This is the ID of the OpVariable. + ID id; + + // The type ID of the variable which includes arrays and all type modifications. + // This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general + // since these modifications typically happen on the base_type_id. + TypeID type_id; + + // The base type of the declared resource. + // This type is the base type which ignores pointers and arrays of the type_id. + // This is mostly useful to parse decorations of the underlying type. + // base_type_id can also be obtained with get_type(get_type(type_id).self). + TypeID base_type_id; + + // The declared name (OpName) of the resource. + // For Buffer blocks, the name actually reflects the externally + // visible Block name. + // + // This name can be retrieved again by using either + // get_name(id) or get_name(base_type_id) depending if it's a buffer block or not. + // + // This name can be an empty string in which case get_fallback_name(id) can be + // used which obtains a suitable fallback identifier for an ID. + std::string name; +}; + +struct BuiltInResource +{ + // This is mostly here to support reflection of builtins such as Position/PointSize/CullDistance/ClipDistance. + // This needs to be different from Resource since we can collect builtins from blocks. + // A builtin present here does not necessarily mean it's considered an active builtin, + // since variable ID "activeness" is only tracked on OpVariable level, not Block members. + // For that, update_active_builtins() -> has_active_builtin() can be used to further refine the reflection. + BuiltIn builtin; + + // This is the actual value type of the builtin. + // Typically float4, float, array for the gl_PerVertex builtins. + // If the builtin is a control point, the control point array type will be stripped away here as appropriate. + TypeID value_type_id; + + // This refers to the base resource which contains the builtin. + // If resource is a Block, it can hold multiple builtins, or it might not be a block. + // For advanced reflection scenarios, all information in builtin/value_type_id can be deduced, + // it's just more convenient this way. + Resource resource; +}; + +// Needs to stay in sync 1:1 with C API. +enum ResourceType +{ + ResourceTypeUnknown = 0, + ResourceTypeUniformBuffer = 1, + ResourceTypeStorageBuffer = 2, + ResourceTypeStageInput = 3, + ResourceTypeStageOutput = 4, + ResourceTypeSubpassInput = 5, + ResourceTypeStorageImage = 6, + ResourceTypeSampledImage = 7, + ResourceTypeAtomicCounter = 8, + ResourceTypePushConstant = 9, + ResourceTypeSeparateImage = 10, + ResourceTypeSeparateSamplers = 11, + ResourceTypeAccelerationStructure = 12, + ResourceTypeRayQuery = 13, + ResourceTypeShaderRecordBuffer = 14, + ResourceTypeGLPlainUniform = 15, + ResourceTypeTensor = 16 +}; + +struct ShaderResources +{ + SmallVector uniform_buffers; + SmallVector storage_buffers; + SmallVector stage_inputs; + SmallVector stage_outputs; + SmallVector subpass_inputs; + SmallVector storage_images; + SmallVector sampled_images; + SmallVector atomic_counters; + SmallVector acceleration_structures; + SmallVector gl_plain_uniforms; + SmallVector tensors; + + // There can only be one push constant block, + // but keep the vector in case this restriction is lifted in the future. + SmallVector push_constant_buffers; + + SmallVector shader_record_buffers; + + // For Vulkan GLSL and HLSL source, + // these correspond to separate texture2D and samplers respectively. + SmallVector separate_images; + SmallVector separate_samplers; + + SmallVector builtin_inputs; + SmallVector builtin_outputs; +}; + +struct CombinedImageSampler +{ + // The ID of the sampler2D variable. + VariableID combined_id; + // The ID of the texture2D variable. + VariableID image_id; + // The ID of the sampler variable. + VariableID sampler_id; +}; + +struct SpecializationConstant +{ + // The ID of the specialization constant. + ConstantID id; + // The constant ID of the constant, used in Vulkan during pipeline creation. + uint32_t constant_id; +}; + +struct BufferRange +{ + unsigned index; + size_t offset; + size_t range; +}; + +enum BufferPackingStandard +{ + BufferPackingStd140, + BufferPackingStd430, + BufferPackingStd140EnhancedLayout, + BufferPackingStd430EnhancedLayout, + BufferPackingHLSLCbuffer, + BufferPackingHLSLCbufferPackOffset, + BufferPackingScalar, + BufferPackingScalarEnhancedLayout +}; + +struct EntryPoint +{ + std::string name; + ExecutionModel execution_model; +}; + +class Compiler +{ +public: + friend class CFG; + friend class DominatorBuilder; + + // The constructor takes a buffer of SPIR-V words and parses it. + // It will create its own parser, parse the SPIR-V and move the parsed IR + // as if you had called the constructors taking ParsedIR directly. + explicit Compiler(std::vector ir); + Compiler(const uint32_t *ir, size_t word_count); + + // This is more modular. We can also consume a ParsedIR structure directly, either as a move, or copy. + // With copy, we can reuse the same parsed IR for multiple Compiler instances. + explicit Compiler(const ParsedIR &ir); + explicit Compiler(ParsedIR &&ir); + + virtual ~Compiler() = default; + + // After parsing, API users can modify the SPIR-V via reflection and call this + // to disassemble the SPIR-V into the desired langauage. + // Sub-classes actually implement this. + virtual std::string compile(); + + // Gets the identifier (OpName) of an ID. If not defined, an empty string will be returned. + const std::string &get_name(ID id) const; + + // Applies a decoration to an ID. Effectively injects OpDecorate. + void set_decoration(ID id, Decoration decoration, uint32_t argument = 0); + void set_decoration_string(ID id, Decoration decoration, const std::string &argument); + + // Overrides the identifier OpName of an ID. + // Identifiers beginning with underscores or identifiers which contain double underscores + // are reserved by the implementation. + void set_name(ID id, const std::string &name); + + // Gets a bitmask for the decorations which are applied to ID. + // I.e. (1ull << DecorationFoo) | (1ull << DecorationBar) + const Bitset &get_decoration_bitset(ID id) const; + + // Returns whether the decoration has been applied to the ID. + bool has_decoration(ID id, Decoration decoration) const; + + // Gets the value for decorations which take arguments. + // If the decoration is a boolean (i.e. DecorationNonWritable), + // 1 will be returned. + // If decoration doesn't exist or decoration is not recognized, + // 0 will be returned. + uint32_t get_decoration(ID id, Decoration decoration) const; + const std::string &get_decoration_string(ID id, Decoration decoration) const; + + // Removes the decoration for an ID. + void unset_decoration(ID id, Decoration decoration); + + // Gets the SPIR-V type associated with ID. + // Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource. + const SPIRType &get_type(TypeID id) const; + + // Gets the SPIR-V type of a variable. + const SPIRType &get_type_from_variable(VariableID id) const; + + // Gets the underlying storage class for an OpVariable. + StorageClass get_storage_class(VariableID id) const; + + // If get_name() is an empty string, get the fallback name which will be used + // instead in the disassembled source. + virtual const std::string get_fallback_name(ID id) const; + + // If get_name() of a Block struct is an empty string, get the fallback name. + // This needs to be per-variable as multiple variables can use the same block type. + virtual const std::string get_block_fallback_name(VariableID id) const; + + // Given an OpTypeStruct in ID, obtain the identifier for member number "index". + // This may be an empty string. + const std::string &get_member_name(TypeID id, uint32_t index) const; + + // Given an OpTypeStruct in ID, obtain the OpMemberDecoration for member number "index". + uint32_t get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const; + const std::string &get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const; + + // Sets the member identifier for OpTypeStruct ID, member number "index". + void set_member_name(TypeID id, uint32_t index, const std::string &name); + + // Returns the qualified member identifier for OpTypeStruct ID, member number "index", + // or an empty string if no qualified alias exists + const std::string &get_member_qualified_name(TypeID type_id, uint32_t index) const; + + // Gets the decoration mask for a member of a struct, similar to get_decoration_mask. + const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; + + // Returns whether the decoration has been applied to a member of a struct. + bool has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const; + + // Similar to set_decoration, but for struct members. + void set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument = 0); + void set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, + const std::string &argument); + + // Unsets a member decoration, similar to unset_decoration. + void unset_member_decoration(TypeID id, uint32_t index, Decoration decoration); + + // Gets the fallback name for a member, similar to get_fallback_name. + virtual const std::string get_fallback_member_name(uint32_t index) const + { + return join("_", index); + } + + // Returns a vector of which members of a struct are potentially in use by a + // SPIR-V shader. The granularity of this analysis is per-member of a struct. + // This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks. + // ID is the Resource::id obtained from get_shader_resources(). + SmallVector get_active_buffer_ranges(VariableID id) const; + + // Returns the effective size of a buffer block. + size_t get_declared_struct_size(const SPIRType &struct_type) const; + + // Returns the effective size of a buffer block, with a given array size + // for a runtime array. + // SSBOs are typically declared as runtime arrays. get_declared_struct_size() will return 0 for the size. + // This is not very helpful for applications which might need to know the array stride of its last member. + // This can be done through the API, but it is not very intuitive how to accomplish this, so here we provide a helper function + // to query the size of the buffer, assuming that the last member has a certain size. + // If the buffer does not contain a runtime array, array_size is ignored, and the function will behave as + // get_declared_struct_size(). + // To get the array stride of the last member, something like: + // get_declared_struct_size_runtime_array(type, 1) - get_declared_struct_size_runtime_array(type, 0) will work. + size_t get_declared_struct_size_runtime_array(const SPIRType &struct_type, size_t array_size) const; + + // Returns the effective size of a buffer block struct member. + size_t get_declared_struct_member_size(const SPIRType &struct_type, uint32_t index) const; + + // Returns a set of all global variables which are statically accessed + // by the control flow graph from the current entry point. + // Only variables which change the interface for a shader are returned, that is, + // variables with storage class of Input, Output, Uniform, UniformConstant, PushConstant and AtomicCounter + // storage classes are returned. + // + // To use the returned set as the filter for which variables are used during compilation, + // this set can be moved to set_enabled_interface_variables(). + std::unordered_set get_active_interface_variables() const; + + // Sets the interface variables which are used during compilation. + // By default, all variables are used. + // Once set, compile() will only consider the set in active_variables. + void set_enabled_interface_variables(std::unordered_set active_variables); + + // Query shader resources, use ids with reflection interface to modify or query binding points, etc. + ShaderResources get_shader_resources() const; + + // Query shader resources, but only return the variables which are part of active_variables. + // E.g.: get_shader_resources(get_active_variables()) to only return the variables which are statically + // accessed. + ShaderResources get_shader_resources(const std::unordered_set &active_variables) const; + + // Remapped variables are considered built-in variables and a backend will + // not emit a declaration for this variable. + // This is mostly useful for making use of builtins which are dependent on extensions. + void set_remapped_variable_state(VariableID id, bool remap_enable); + bool get_remapped_variable_state(VariableID id) const; + + // For subpassInput variables which are remapped to plain variables, + // the number of components in the remapped + // variable must be specified as the backing type of subpass inputs are opaque. + void set_subpass_input_remapped_components(VariableID id, uint32_t components); + uint32_t get_subpass_input_remapped_components(VariableID id) const; + + // All operations work on the current entry point. + // Entry points can be swapped out with set_entry_point(). + // Entry points should be set right after the constructor completes as some reflection functions traverse the graph from the entry point. + // Resource reflection also depends on the entry point. + // By default, the current entry point is set to the first OpEntryPoint which appears in the SPIR-V module. + + // Some shader languages restrict the names that can be given to entry points, and the + // corresponding backend will automatically rename an entry point name, during the call + // to compile() if it is illegal. For example, the common entry point name main() is + // illegal in MSL, and is renamed to an alternate name by the MSL backend. + // Given the original entry point name contained in the SPIR-V, this function returns + // the name, as updated by the backend during the call to compile(). If the name is not + // illegal, and has not been renamed, or if this function is called before compile(), + // this function will simply return the same name. + + // New variants of entry point query and reflection. + // Names for entry points in the SPIR-V module may alias if they belong to different execution models. + // To disambiguate, we must pass along with the entry point names the execution model. + SmallVector get_entry_points_and_stages() const; + void set_entry_point(const std::string &entry, ExecutionModel execution_model); + + // Renames an entry point from old_name to new_name. + // If old_name is currently selected as the current entry point, it will continue to be the current entry point, + // albeit with a new name. + // get_entry_points() is essentially invalidated at this point. + void rename_entry_point(const std::string &old_name, const std::string &new_name, + ExecutionModel execution_model); + const SPIREntryPoint &get_entry_point(const std::string &name, ExecutionModel execution_model) const; + SPIREntryPoint &get_entry_point(const std::string &name, ExecutionModel execution_model); + const std::string &get_cleansed_entry_point_name(const std::string &name, + ExecutionModel execution_model) const; + + // Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader. + void update_active_builtins(); + bool has_active_builtin(BuiltIn builtin, StorageClass storage) const; + + // Query and modify OpExecutionMode. + const Bitset &get_execution_mode_bitset() const; + + void unset_execution_mode(ExecutionMode mode); + void set_execution_mode(ExecutionMode mode, uint32_t arg0 = 0, uint32_t arg1 = 0, uint32_t arg2 = 0); + + // Gets argument for an execution mode (LocalSize, Invocations, OutputVertices). + // For LocalSize or LocalSizeId, the index argument is used to select the dimension (X = 0, Y = 1, Z = 2). + // For execution modes which do not have arguments, 0 is returned. + // LocalSizeId query returns an ID. If LocalSizeId execution mode is not used, it returns 0. + // LocalSize always returns a literal. If execution mode is LocalSizeId, + // the literal (spec constant or not) is still returned. + uint32_t get_execution_mode_argument(ExecutionMode mode, uint32_t index = 0) const; + ExecutionModel get_execution_model() const; + + bool is_tessellation_shader() const; + bool is_tessellating_triangles() const; + + // In SPIR-V, the compute work group size can be represented by a constant vector, in which case + // the LocalSize execution mode is ignored. + // + // This constant vector can be a constant vector, specialization constant vector, or partly specialized constant vector. + // To modify and query work group dimensions which are specialization constants, SPIRConstant values must be modified + // directly via get_constant() rather than using LocalSize directly. This function will return which constants should be modified. + // + // To modify dimensions which are *not* specialization constants, set_execution_mode should be used directly. + // Arguments to set_execution_mode which are specialization constants are effectively ignored during compilation. + // NOTE: This is somewhat different from how SPIR-V works. In SPIR-V, the constant vector will completely replace LocalSize, + // while in this interface, LocalSize is only ignored for specialization constants. + // + // The specialization constant will be written to x, y and z arguments. + // If the component is not a specialization constant, a zeroed out struct will be written. + // The return value is the constant ID of the builtin WorkGroupSize, but this is not expected to be useful + // for most use cases. + // If LocalSizeId is used, there is no uvec3 value representing the workgroup size, so the return value is 0, + // but x, y and z are written as normal if the components are specialization constants. + uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, + SpecializationConstant &z) const; + + // Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where + // said instruction is used without a combined image sampler. + // GLSL targets do not support the use of texelFetch without a sampler. + // To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of + // texelFetch as necessary. + // + // This must be called before build_combined_image_samplers(). + // build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero. + // The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object + // is required. + // + // If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile(). + // Calling this function also invalidates get_active_interface_variables(), so this should be called + // before that function. + VariableID build_dummy_sampler_for_combined_images(); + + // Analyzes all separate image and samplers used from the currently selected entry point, + // and re-routes them all to a combined image sampler instead. + // This is required to "support" separate image samplers in targets which do not natively support + // this feature, like GLSL/ESSL. + // + // This must be called before compile() if such remapping is desired. + // This call will add new sampled images to the SPIR-V, + // so it will appear in reflection if get_shader_resources() is called after build_combined_image_samplers. + // + // If any image/sampler remapping was found, no separate image/samplers will appear in the decompiled output, + // but will still appear in reflection. + // + // The resulting samplers will be void of any decorations like name, descriptor sets and binding points, + // so this can be added before compile() if desired. + // + // Combined image samplers originating from this set are always considered active variables. + // Arrays of separate samplers are not supported, but arrays of separate images are supported. + // Array of images + sampler -> Array of combined image samplers. + void build_combined_image_samplers(); + + // Gets a remapping for the combined image samplers. + const SmallVector &get_combined_image_samplers() const + { + return combined_image_samplers; + } + + // Set a new variable type remap callback. + // The type remapping is designed to allow global interface variable to assume more special types. + // A typical example here is to remap sampler2D into samplerExternalOES, which currently isn't supported + // directly by SPIR-V. + // + // In compile() while emitting code, + // for every variable that is declared, including function parameters, the callback will be called + // and the API user has a chance to change the textual representation of the type used to declare the variable. + // The API user can detect special patterns in names to guide the remapping. + void set_variable_type_remap_callback(VariableTypeRemapCallback cb) + { + variable_remap_callback = std::move(cb); + } + + // API for querying which specialization constants exist. + // To modify a specialization constant before compile(), use get_constant(constant.id), + // then update constants directly in the SPIRConstant data structure. + // For composite types, the subconstants can be iterated over and modified. + // constant_type is the SPIRType for the specialization constant, + // which can be queried to determine which fields in the unions should be poked at. + SmallVector get_specialization_constants() const; + SPIRConstant &get_constant(ConstantID id); + const SPIRConstant &get_constant(ConstantID id) const; + + uint32_t get_current_id_bound() const + { + return uint32_t(ir.ids.size()); + } + + // API for querying buffer objects. + // The type passed in here should be the base type of a resource, i.e. + // get_type(resource.base_type_id) + // as decorations are set in the basic Block type. + // The type passed in here must have these decorations set, or an exception is raised. + // Only UBOs and SSBOs or sub-structs which are part of these buffer types will have these decorations set. + uint32_t type_struct_member_offset(const SPIRType &type, uint32_t index) const; + uint32_t type_struct_member_array_stride(const SPIRType &type, uint32_t index) const; + uint32_t type_struct_member_matrix_stride(const SPIRType &type, uint32_t index) const; + + // Gets the offset in SPIR-V words (uint32_t) for a decoration which was originally declared in the SPIR-V binary. + // The offset will point to one or more uint32_t literals which can be modified in-place before using the SPIR-V binary. + // Note that adding or removing decorations using the reflection API will not change the behavior of this function. + // If the decoration was declared, sets the word_offset to an offset into the provided SPIR-V binary buffer and returns true, + // otherwise, returns false. + // If the decoration does not have any value attached to it (e.g. DecorationRelaxedPrecision), this function will also return false. + bool get_binary_offset_for_decoration(VariableID id, Decoration decoration, uint32_t &word_offset) const; + + // HLSL counter buffer reflection interface. + // Append/Consume/Increment/Decrement in HLSL is implemented as two "neighbor" buffer objects where + // one buffer implements the storage, and a single buffer containing just a lone "int" implements the counter. + // To SPIR-V these will be exposed as two separate buffers, but glslang HLSL frontend emits a special indentifier + // which lets us link the two buffers together. + + // Queries if a variable ID is a counter buffer which "belongs" to a regular buffer object. + + // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. + // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will + // only return true if OpSource was reported HLSL. + // To rely on this functionality, ensure that the SPIR-V module is not stripped. + + bool buffer_is_hlsl_counter_buffer(VariableID id) const; + + // Queries if a buffer object has a neighbor "counter" buffer. + // If so, the ID of that counter buffer will be returned in counter_id. + // If SPV_GOOGLE_hlsl_functionality1 is used, this can be used even with a stripped SPIR-V module. + // Otherwise, this query is purely based on OpName identifiers as found in the SPIR-V module, and will + // only return true if OpSource was reported HLSL. + // To rely on this functionality, ensure that the SPIR-V module is not stripped. + bool buffer_get_hlsl_counter_buffer(VariableID id, uint32_t &counter_id) const; + + // Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module. + const SmallVector &get_declared_capabilities() const; + + // Gets the list of all SPIR-V extensions which were declared in the SPIR-V module. + const SmallVector &get_declared_extensions() const; + + // When declaring buffer blocks in GLSL, the name declared in the GLSL source + // might not be the same as the name declared in the SPIR-V module due to naming conflicts. + // In this case, SPIRV-Cross needs to find a fallback-name, and it might only + // be possible to know this name after compiling to GLSL. + // This is particularly important for HLSL input and UAVs which tends to reuse the same block type + // for multiple distinct blocks. For these cases it is not possible to modify the name of the type itself + // because it might be unique. Instead, you can use this interface to check after compilation which + // name was actually used if your input SPIR-V tends to have this problem. + // For other names like remapped names for variables, etc, it's generally enough to query the name of the variables + // after compiling, block names are an exception to this rule. + // ID is the name of a variable as returned by Resource::id, and must be a variable with a Block-like type. + // + // This also applies to HLSL cbuffers. + std::string get_remapped_declared_block_name(VariableID id) const; + + // For buffer block variables, get the decorations for that variable. + // Sometimes, decorations for buffer blocks are found in member decorations instead + // of direct decorations on the variable itself. + // The most common use here is to check if a buffer is readonly or writeonly. + Bitset get_buffer_block_flags(VariableID id) const; + + // Returns whether the position output is invariant + bool is_position_invariant() const + { + return position_invariant; + } + + const ParsedIR &get_ir() const { return ir; } + uint32_t evaluate_constant_u32(uint32_t id) const; + +protected: + const uint32_t *stream(const Instruction &instr) const + { + // If we're not going to use any arguments, just return nullptr. + // We want to avoid case where we return an out of range pointer + // that trips debug assertions on some platforms. + if (!instr.length) + return nullptr; + + if (instr.is_embedded()) + { + auto &embedded = static_cast(instr); + assert(embedded.ops.size() == instr.length); + return embedded.ops.data(); + } + else + { + if (instr.offset + instr.length > ir.spirv.size()) + SPIRV_CROSS_THROW("Compiler::stream() out of range."); + return &ir.spirv[instr.offset]; + } + } + + uint32_t *stream_mutable(const Instruction &instr) const + { + return const_cast(stream(instr)); + } + + ParsedIR ir; + // Marks variables which have global scope and variables which can alias with other variables + // (SSBO, image load store, etc) + SmallVector global_variables; + SmallVector aliased_variables; + SmallVector buffer_pointer_variables; + + SPIRFunction *current_function = nullptr; + SPIRBlock *current_block = nullptr; + uint32_t current_loop_level = 0; + std::unordered_set active_interface_variables; + bool check_active_interface_variables = false; + + void add_loop_level(); + + void set_initializers(SPIRExpression &e) + { + e.emitted_loop_level = current_loop_level; + } + + template + void set_initializers(const T &) + { + } + + // If our IDs are out of range here as part of opcodes, throw instead of + // undefined behavior. + template + T &set(uint32_t id, P &&... args) + { + ir.add_typed_id(static_cast(T::type), id); + auto &var = variant_set(ir.ids[id], std::forward

(args)...); + var.self = id; + set_initializers(var); + return var; + } + + template + T &get(uint32_t id) + { + return variant_get(ir.ids[id]); + } + + template + T *maybe_get(uint32_t id) + { + if (id >= ir.ids.size()) + return nullptr; + else if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + template + const T &get(uint32_t id) const + { + return variant_get(ir.ids[id]); + } + + template + const T *maybe_get(uint32_t id) const + { + if (id >= ir.ids.size()) + return nullptr; + else if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + // Gets the id of SPIR-V type underlying the given type_id, which might be a pointer. + uint32_t get_pointee_type_id(uint32_t type_id) const; + + // Gets the SPIR-V type underlying the given type, which might be a pointer. + const SPIRType &get_pointee_type(const SPIRType &type) const; + + // Gets the SPIR-V type underlying the given type_id, which might be a pointer. + const SPIRType &get_pointee_type(uint32_t type_id) const; + + // Gets the ID of the SPIR-V type underlying a variable. + uint32_t get_variable_data_type_id(const SPIRVariable &var) const; + + // Gets the SPIR-V type underlying a variable. + SPIRType &get_variable_data_type(const SPIRVariable &var); + + // Gets the SPIR-V type underlying a variable. + const SPIRType &get_variable_data_type(const SPIRVariable &var) const; + + // Gets the SPIR-V element type underlying an array variable. + SPIRType &get_variable_element_type(const SPIRVariable &var); + + // Gets the SPIR-V element type underlying an array variable. + const SPIRType &get_variable_element_type(const SPIRVariable &var) const; + + // Sets the qualified member identifier for OpTypeStruct ID, member number "index". + void set_member_qualified_name(uint32_t type_id, uint32_t index, const std::string &name); + void set_qualified_name(uint32_t id, const std::string &name); + + // Returns if the given type refers to a sampled image. + bool is_sampled_image_type(const SPIRType &type); + + const SPIREntryPoint &get_entry_point() const; + SPIREntryPoint &get_entry_point(); + static bool is_tessellation_shader(ExecutionModel model); + + virtual std::string to_name(uint32_t id, bool allow_alias = true) const; + bool is_builtin_variable(const SPIRVariable &var) const; + bool is_builtin_type(const SPIRType &type) const; + bool is_hidden_variable(const SPIRVariable &var, bool include_builtins = false) const; + bool is_immutable(uint32_t id) const; + bool is_member_builtin(const SPIRType &type, uint32_t index, BuiltIn *builtin) const; + bool is_scalar(const SPIRType &type) const; + bool is_vector(const SPIRType &type) const; + bool is_matrix(const SPIRType &type) const; + bool is_array(const SPIRType &type) const; + bool is_pointer(const SPIRType &type) const; + bool is_physical_pointer(const SPIRType &type) const; + bool is_physical_or_buffer_pointer(const SPIRType &type) const; + bool is_physical_pointer_to_buffer_block(const SPIRType &type) const; + static bool is_runtime_size_array(const SPIRType &type); + uint32_t expression_type_id(uint32_t id) const; + const SPIRType &expression_type(uint32_t id) const; + bool expression_is_lvalue(uint32_t id) const; + bool variable_storage_is_aliased(const SPIRVariable &var); + SPIRVariable *maybe_get_backing_variable(uint32_t chain); + SPIRExpression *maybe_get_backing_buffer_pointer(uint32_t chain); + + void register_read(uint32_t expr, uint32_t chain, bool forwarded); + void register_write(uint32_t chain); + + inline bool is_continue(uint32_t next) const + { + return (ir.block_meta[next] & ParsedIR::BLOCK_META_CONTINUE_BIT) != 0; + } + + inline bool is_single_block_loop(uint32_t next) const + { + auto &block = get(next); + return block.merge == SPIRBlock::MergeLoop && block.continue_block == ID(next); + } + + inline bool is_break(uint32_t next) const + { + return (ir.block_meta[next] & + (ParsedIR::BLOCK_META_LOOP_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; + } + + inline bool is_loop_break(uint32_t next) const + { + return (ir.block_meta[next] & ParsedIR::BLOCK_META_LOOP_MERGE_BIT) != 0; + } + + inline bool is_conditional(uint32_t next) const + { + return (ir.block_meta[next] & + (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT)) != 0; + } + + // Dependency tracking for temporaries read from variables. + void flush_dependees(SPIRVariable &var); + void flush_dependees(SPIRExpression &expr); + void flush_all_active_variables(); + void flush_control_dependent_expressions(uint32_t block); + void flush_all_atomic_capable_variables(); + void flush_all_aliased_variables(); + void register_global_read_dependencies(const SPIRBlock &func, uint32_t id); + void register_global_read_dependencies(const SPIRFunction &func, uint32_t id); + std::unordered_set invalid_expressions; + + void update_name_cache(std::unordered_set &cache, std::string &name); + + // A variant which takes two sets of names. The secondary is only used to verify there are no collisions, + // but the set is not updated when we have found a new name. + // Used primarily when adding block interface names. + void update_name_cache(std::unordered_set &cache_primary, + const std::unordered_set &cache_secondary, std::string &name); + + bool function_is_pure(const SPIRFunction &func); + bool block_is_pure(const SPIRBlock &block); + bool function_is_control_dependent(const SPIRFunction &func); + bool block_is_control_dependent(const SPIRBlock &block); + + bool execution_is_branchless(const SPIRBlock &from, const SPIRBlock &to) const; + bool execution_is_direct_branch(const SPIRBlock &from, const SPIRBlock &to) const; + bool execution_is_noop(const SPIRBlock &from, const SPIRBlock &to) const; + SPIRBlock::ContinueBlockType continue_block_type(const SPIRBlock &continue_block) const; + + void force_recompile(); + void force_recompile_guarantee_forward_progress(); + void clear_force_recompile(); + bool is_forcing_recompilation() const; + bool is_force_recompile = false; + bool is_force_recompile_forward_progress = false; + + bool block_is_noop(const SPIRBlock &block) const; + bool block_is_loop_candidate(const SPIRBlock &block, SPIRBlock::Method method) const; + + bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; + void inherit_expression_dependencies(uint32_t dst, uint32_t source); + void add_implied_read_expression(SPIRExpression &e, uint32_t source); + void add_implied_read_expression(SPIRAccessChain &e, uint32_t source); + void add_active_interface_variable(uint32_t var_id); + + // For proper multiple entry point support, allow querying if an Input or Output + // variable is part of that entry points interface. + bool interface_variable_exists_in_entry_point(uint32_t id) const; + + SmallVector combined_image_samplers; + + void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const + { + if (variable_remap_callback) + variable_remap_callback(type, var_name, type_name); + } + + void set_ir(const ParsedIR &parsed); + void set_ir(ParsedIR &&parsed); + void parse_fixup(); + + // Used internally to implement various traversals for queries. + struct OpcodeHandler + { + explicit OpcodeHandler(Compiler &compiler_) : compiler(compiler_) {} + virtual ~OpcodeHandler() = default; + + // Return true if traversal should continue. + // If false, traversal will end immediately. + virtual bool handle(Op opcode, const uint32_t *args, uint32_t length) = 0; + virtual bool handle_terminator(const SPIRBlock &) + { + return true; + } + + virtual bool follow_function_call(const SPIRFunction &) + { + return true; + } + + virtual void set_current_block(const SPIRBlock &) + { + } + + // Called after returning from a function or when entering a block, + // can be called multiple times per block, + // while set_current_block is only called on block entry. + virtual void rearm_current_block(const SPIRBlock &) + { + } + + virtual bool begin_function_scope(const uint32_t *, uint32_t) + { + return true; + } + + virtual bool end_function_scope(const uint32_t *, uint32_t) + { + return true; + } + + Compiler &compiler; + std::unordered_map result_types; + const SPIRType *get_expression_result_type(uint32_t id) const; + bool enable_result_types = false; + + template T &get(uint32_t id) + { + return compiler.get(id); + } + + template const T &get(uint32_t id) const + { + return compiler.get(id); + } + + template + T &set(uint32_t id, P &&... args) + { + return compiler.set(id, std::forward

(args)...); + } + }; + + struct BufferAccessHandler : OpcodeHandler + { + BufferAccessHandler(const Compiler &compiler_, SmallVector &ranges_, uint32_t id_) + : OpcodeHandler(const_cast(compiler_)) + , ranges(ranges_) + , id(id_) + { + } + + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + + SmallVector &ranges; + uint32_t id; + + std::unordered_set seen; + }; + + struct InterfaceVariableAccessHandler : OpcodeHandler + { + InterfaceVariableAccessHandler(const Compiler &compiler_, std::unordered_set &variables_) + : OpcodeHandler(const_cast(compiler_)) + , variables(variables_) + { + } + + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + + std::unordered_set &variables; + }; + + struct CombinedImageSamplerHandler : OpcodeHandler + { + explicit CombinedImageSamplerHandler(Compiler &compiler_) + : OpcodeHandler(compiler_) + { + } + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + // Each function in the call stack needs its own remapping for parameters so we can deduce which global variable each texture/sampler the parameter is statically bound to. + std::stack> parameter_remapping; + std::stack functions; + + uint32_t remap_parameter(uint32_t id); + void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length); + void pop_remap_parameters(); + void register_combined_image_sampler(SPIRFunction &caller, VariableID combined_id, VariableID texture_id, + VariableID sampler_id, bool depth); + }; + + struct DummySamplerForCombinedImageHandler : OpcodeHandler + { + explicit DummySamplerForCombinedImageHandler(Compiler &compiler_) + : OpcodeHandler(compiler_) + { + } + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + bool need_dummy_sampler = false; + }; + + struct ActiveBuiltinHandler : OpcodeHandler + { + explicit ActiveBuiltinHandler(Compiler &compiler_) + : OpcodeHandler(compiler_) + { + } + + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + + void handle_builtin(const SPIRType &type, BuiltIn builtin, const Bitset &decoration_flags); + void add_if_builtin(uint32_t id); + void add_if_builtin_or_block(uint32_t id); + void add_if_builtin(uint32_t id, bool allow_blocks); + }; + + bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const; + bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const; + // This must be an ordered data structure so we always pick the same type aliases. + SmallVector global_struct_cache; + + ShaderResources get_shader_resources(const std::unordered_set *active_variables) const; + + VariableTypeRemapCallback variable_remap_callback; + + bool get_common_basic_type(const SPIRType &type, SPIRType::BaseType &base_type); + + std::unordered_set forced_temporaries; + std::unordered_set forwarded_temporaries; + std::unordered_set suppressed_usage_tracking; + std::unordered_set hoisted_temporaries; + std::unordered_set forced_invariant_temporaries; + + Bitset active_input_builtins; + Bitset active_output_builtins; + uint32_t clip_distance_count = 0; + uint32_t cull_distance_count = 0; + bool position_invariant = false; + + void analyze_parameter_preservation( + SPIRFunction &entry, const CFG &cfg, + const std::unordered_map> &variable_to_blocks, + const std::unordered_map> &complete_write_blocks); + + // If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler. + // SPIR-V does not support this distinction, so we must keep track of this information outside the type system. + // 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_ids; + bool need_subpass_input = false; + bool need_subpass_input_ms = false; + + // In certain backends, we will need to use a dummy sampler to be able to emit code. + // GLSL does not support texelFetch on texture2D objects, but SPIR-V does, + // so we need to workaround by having the application inject a dummy sampler. + uint32_t dummy_sampler_id = 0; + + void analyze_image_and_sampler_usage(); + + struct CombinedImageSamplerDrefHandler : OpcodeHandler + { + explicit CombinedImageSamplerDrefHandler(Compiler &compiler_) + : OpcodeHandler(compiler_) + { + } + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + + std::unordered_set dref_combined_samplers; + }; + + struct CombinedImageSamplerUsageHandler : OpcodeHandler + { + CombinedImageSamplerUsageHandler(Compiler &compiler_, + const std::unordered_set &dref_combined_samplers_) + : OpcodeHandler(compiler_) + , dref_combined_samplers(dref_combined_samplers_) + { + } + + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + const std::unordered_set &dref_combined_samplers; + + std::unordered_map> dependency_hierarchy; + std::unordered_set comparison_ids; + + void add_hierarchy_to_comparison_ids(uint32_t ids); + bool need_subpass_input = false; + bool need_subpass_input_ms = false; + void add_dependency(uint32_t dst, uint32_t src); + }; + + void build_function_control_flow_graphs_and_analyze(); + std::unordered_map> function_cfgs; + const CFG &get_cfg_for_current_function() const; + const CFG &get_cfg_for_function(uint32_t id) const; + + struct CFGBuilder : OpcodeHandler + { + explicit CFGBuilder(Compiler &compiler_); + + bool follow_function_call(const SPIRFunction &func) override; + bool handle(Op op, const uint32_t *args, uint32_t length) override; + std::unordered_map> function_cfgs; + }; + + struct AnalyzeVariableScopeAccessHandler : OpcodeHandler + { + AnalyzeVariableScopeAccessHandler(Compiler &compiler_, SPIRFunction &entry_); + + bool follow_function_call(const SPIRFunction &) override; + void set_current_block(const SPIRBlock &block) override; + + void notify_variable_access(uint32_t id, uint32_t block); + bool id_is_phi_variable(uint32_t id) const; + bool id_is_potential_temporary(uint32_t id) const; + bool handle(Op op, const uint32_t *args, uint32_t length) override; + bool handle_terminator(const SPIRBlock &block) override; + + SPIRFunction &entry; + std::unordered_map> accessed_variables_to_block; + std::unordered_map> accessed_temporaries_to_block; + std::unordered_map result_id_to_type; + std::unordered_map> complete_write_variables_to_block; + std::unordered_map> partial_write_variables_to_block; + std::unordered_set access_chain_expressions; + // Access chains used in multiple blocks mean hoisting all the variables used to construct the access chain as not all backends can use pointers. + // This is also relevant when forwarding opaque objects since we cannot lower these to temporaries. + std::unordered_map> rvalue_forward_children; + const SPIRBlock *current_block = nullptr; + }; + + struct StaticExpressionAccessHandler : OpcodeHandler + { + StaticExpressionAccessHandler(Compiler &compiler_, uint32_t variable_id_); + bool follow_function_call(const SPIRFunction &) override; + bool handle(Op op, const uint32_t *args, uint32_t length) override; + + uint32_t variable_id; + uint32_t static_expression = 0; + uint32_t write_count = 0; + }; + + struct PhysicalBlockMeta + { + uint32_t alignment = 0; + }; + + struct PhysicalStorageBufferPointerHandler : OpcodeHandler + { + explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_); + bool handle(Op op, const uint32_t *args, uint32_t length) override; + + std::unordered_set non_block_types; + std::unordered_map physical_block_type_meta; + std::unordered_map access_chain_to_physical_block; + std::unordered_set analyzed_type_ids; + + void mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length); + PhysicalBlockMeta *find_block_meta(uint32_t id) const; + bool type_is_bda_block_entry(uint32_t type_id) const; + void setup_meta_chain(uint32_t type_id, uint32_t var_id); + uint32_t get_minimum_scalar_alignment(const SPIRType &type) const; + void analyze_non_block_types_from_block(const SPIRType &type); + uint32_t get_base_non_block_type_id(uint32_t type_id) const; + }; + void analyze_non_block_pointer_types(); + SmallVector physical_storage_non_block_pointer_types; + std::unordered_map physical_storage_type_to_alignment; + + struct DescriptorHeapMeta + { + TypeID type; + bool hlsl_style_stride; + + // For buffers + ID buffer_pointer_id; + StorageClass storage; + bool nonwritable; + bool nonreadable; + bool coherent; + bool is_volatile; + bool is_restrict; + }; + std::vector descriptor_heap_types; + void analyze_descriptor_heap_types(); + + void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler); + void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler, + bool single_function); + bool may_read_undefined_variable_in_block(const SPIRBlock &block, uint32_t var); + + struct GeometryEmitDisocveryHandler : OpcodeHandler + { + explicit GeometryEmitDisocveryHandler(Compiler &compiler_) + : OpcodeHandler(compiler_) + { + } + + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *, uint32_t) override; + bool end_function_scope(const uint32_t *, uint32_t) override; + SmallVector function_stack; + }; + + void discover_geometry_emitters(); + + // Finds all resources that are written to from inside the critical section, if present. + // The critical section is delimited by OpBeginInvocationInterlockEXT and + // OpEndInvocationInterlockEXT instructions. In MSL and HLSL, any resources written + // while inside the critical section must be placed in a raster order group. + struct InterlockedResourceAccessHandler : OpcodeHandler + { + InterlockedResourceAccessHandler(Compiler &compiler_, uint32_t entry_point_id) + : OpcodeHandler(compiler_) + { + call_stack.push_back(entry_point_id); + } + + bool handle(Op op, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + bool in_crit_sec = false; + + uint32_t interlock_function_id = 0; + bool split_function_case = false; + bool control_flow_interlock = false; + bool use_critical_section = false; + bool call_stack_is_interlocked = false; + SmallVector call_stack; + + void access_potential_resource(uint32_t id); + }; + + struct InterlockedResourceAccessPrepassHandler : OpcodeHandler + { + InterlockedResourceAccessPrepassHandler(Compiler &compiler_, uint32_t entry_point_id) + : OpcodeHandler(compiler_) + { + call_stack.push_back(entry_point_id); + } + + void rearm_current_block(const SPIRBlock &block) override; + bool handle(Op op, const uint32_t *args, uint32_t length) override; + bool begin_function_scope(const uint32_t *args, uint32_t length) override; + bool end_function_scope(const uint32_t *args, uint32_t length) override; + + uint32_t interlock_function_id = 0; + uint32_t current_block_id = 0; + bool split_function_case = false; + bool control_flow_interlock = false; + SmallVector call_stack; + }; + + void analyze_interlocked_resource_usage(); + // The set of all resources written while inside the critical section, if present. + std::unordered_set interlocked_resources; + bool interlocked_is_complex = false; + + void make_constant_null(uint32_t id, uint32_t type); + + std::unordered_map declared_block_names; + + static bool instruction_to_result_type( + uint32_t &result_type, uint32_t &result_id, 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(ImageFormat format); + + bool is_depth_image(const SPIRType &type, uint32_t id) const; + + void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0); + uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; + bool has_extended_decoration(uint32_t id, ExtendedDecorations decoration) const; + void unset_extended_decoration(uint32_t id, ExtendedDecorations decoration); + + void set_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration, + uint32_t value = 0); + uint32_t get_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; + bool has_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration) const; + void unset_extended_member_decoration(uint32_t type, uint32_t index, ExtendedDecorations decoration); + + bool check_internal_recursion(const SPIRType &type, std::unordered_set &checked_ids); + bool type_contains_recursion(const SPIRType &type); + bool type_is_array_of_pointers(const SPIRType &type) const; + bool type_is_block_like(const SPIRType &type) const; + bool type_is_explicit_layout(const SPIRType &type) const; + bool type_is_top_level_block(const SPIRType &type) const; + bool type_is_opaque_value(const SPIRType &type) const; + + bool reflection_ssbo_instance_name_is_significant() const; + std::string get_remapped_declared_block_name(uint32_t id, bool fallback_prefer_instance_name) const; + + bool flush_phi_required(BlockID from, BlockID to) const; + + uint32_t evaluate_spec_constant_u32(const SPIRConstantOp &spec) const; + + bool is_vertex_like_shader() const; + + // Get the correct case list for the OpSwitch, since it can be either a + // 32 bit wide condition or a 64 bit, but the type is not embedded in the + // instruction itself. + const SmallVector &get_case_list(const SPIRBlock &block) const; + +private: + // Used only to implement the old deprecated get_entry_point() interface. + const SPIREntryPoint &get_first_entry_point(const std::string &name) const; + SPIREntryPoint &get_first_entry_point(const std::string &name); +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#ifdef SPIRV_CROSS_SPV_HEADER_NAMESPACE_OVERRIDE +#undef spv +#endif + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cross_c.cpp b/thirdparty/SPIRV-Cross/spirv_cross_c.cpp new file mode 100644 index 000000000..1604385e5 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_c.cpp @@ -0,0 +1,2984 @@ +/* + * Copyright 2019-2021 Hans-Kristian Arntzen + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_cross_c.h" + +#if SPIRV_CROSS_C_API_CPP +#include "spirv_cpp.hpp" +#endif +#if SPIRV_CROSS_C_API_GLSL +#include "spirv_glsl.hpp" +#else +#include "spirv_cross.hpp" +#endif +#if SPIRV_CROSS_C_API_HLSL +#include "spirv_hlsl.hpp" +#endif +#if SPIRV_CROSS_C_API_MSL +#include "spirv_msl.hpp" +#endif +#if SPIRV_CROSS_C_API_REFLECT +#include "spirv_reflect.hpp" +#endif + +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION +#include "gitversion.h" +#endif + +#include "spirv_parser.hpp" +#include +#include +#include + +// clang-format off + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) +#pragma warning(disable : 4065) // switch with 'default' but not 'case'. +#endif + +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#define SPVC_BEGIN_SAFE_SCOPE try +#else +#define SPVC_BEGIN_SAFE_SCOPE +#endif + +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#define SPVC_END_SAFE_SCOPE(context, error) \ + catch (const std::exception &e) \ + { \ + (context)->report_error(e.what()); \ + return (error); \ + } +#else +#define SPVC_END_SAFE_SCOPE(context, error) +#endif + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace std; +using namespace SPIRV_CROSS_NAMESPACE; + +struct ScratchMemoryAllocation +{ + virtual ~ScratchMemoryAllocation() = default; +}; + +struct StringAllocation : ScratchMemoryAllocation +{ + explicit StringAllocation(const char *name) + : str(name) + { + } + + explicit StringAllocation(std::string name) + : str(std::move(name)) + { + } + + std::string str; +}; + +template +struct TemporaryBuffer : ScratchMemoryAllocation +{ + SmallVector buffer; +}; + +template +static inline std::unique_ptr spvc_allocate(Ts &&... ts) +{ + return std::unique_ptr(new T(std::forward(ts)...)); +} + +struct spvc_context_s +{ + string last_error; + SmallVector> allocations; + const char *allocate_name(const std::string &name); + + spvc_error_callback callback = nullptr; + void *callback_userdata = nullptr; + void report_error(std::string msg); +}; + +void spvc_context_s::report_error(std::string msg) +{ + last_error = std::move(msg); + if (callback) + callback(callback_userdata, last_error.c_str()); +} + +const char *spvc_context_s::allocate_name(const std::string &name) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto alloc = spvc_allocate(name); + auto *ret = alloc->str.c_str(); + allocations.emplace_back(std::move(alloc)); + return ret; + } + SPVC_END_SAFE_SCOPE(this, nullptr) +} + +struct spvc_parsed_ir_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + ParsedIR parsed; +}; + +struct spvc_compiler_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + unique_ptr compiler; + spvc_backend backend = SPVC_BACKEND_NONE; +}; + +struct spvc_compiler_options_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + uint32_t backend_flags = 0; +#if SPIRV_CROSS_C_API_GLSL + CompilerGLSL::Options glsl; +#endif +#if SPIRV_CROSS_C_API_MSL + CompilerMSL::Options msl; +#endif +#if SPIRV_CROSS_C_API_HLSL + CompilerHLSL::Options hlsl; +#endif +}; + +struct spvc_set_s : ScratchMemoryAllocation +{ + std::unordered_set set; +}; + +// Dummy-inherit to we can keep our opaque type handle type safe in C-land as well, +// and avoid just throwing void * around. +struct spvc_type_s : SPIRType +{ +}; + +struct spvc_constant_s : SPIRConstant +{ +}; + +struct spvc_resources_s : ScratchMemoryAllocation +{ + spvc_context context = nullptr; + SmallVector uniform_buffers; + SmallVector storage_buffers; + SmallVector stage_inputs; + SmallVector stage_outputs; + SmallVector subpass_inputs; + SmallVector storage_images; + SmallVector sampled_images; + SmallVector atomic_counters; + SmallVector push_constant_buffers; + SmallVector shader_record_buffers; + SmallVector separate_images; + SmallVector separate_samplers; + SmallVector acceleration_structures; + SmallVector gl_plain_uniforms; + SmallVector tensors; + + SmallVector builtin_inputs; + SmallVector builtin_outputs; + + bool copy_resources(SmallVector &outputs, const SmallVector &inputs); + bool copy_resources(SmallVector &outputs, const SmallVector &inputs); + bool copy_resources(const ShaderResources &resources); +}; + +spvc_result spvc_context_create(spvc_context *context) +{ + auto *ctx = new (std::nothrow) spvc_context_s; + if (!ctx) + return SPVC_ERROR_OUT_OF_MEMORY; + + *context = ctx; + return SPVC_SUCCESS; +} + +void spvc_context_destroy(spvc_context context) +{ + delete context; +} + +void spvc_context_release_allocations(spvc_context context) +{ + context->allocations.clear(); +} + +const char *spvc_context_get_last_error_string(spvc_context context) +{ + return context->last_error.c_str(); +} + +SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata) +{ + context->callback = cb; + context->callback_userdata = userdata; +} + +spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count, + spvc_parsed_ir *parsed_ir) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr pir(new (std::nothrow) spvc_parsed_ir_s); + if (!pir) + { + context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + pir->context = context; + Parser parser(spirv, word_count); + parser.parse(); + pir->parsed = std::move(parser.get_parsed_ir()); + *parsed_ir = pir.get(); + context->allocations.push_back(std::move(pir)); + } + SPVC_END_SAFE_SCOPE(context, SPVC_ERROR_INVALID_SPIRV) + return SPVC_SUCCESS; +} + +spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend, spvc_parsed_ir parsed_ir, + spvc_capture_mode mode, spvc_compiler *compiler) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr comp(new (std::nothrow) spvc_compiler_s); + if (!comp) + { + context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + comp->backend = backend; + comp->context = context; + + if (mode != SPVC_CAPTURE_MODE_COPY && mode != SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + { + context->report_error("Invalid argument for capture mode."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + switch (backend) + { + case SPVC_BACKEND_NONE: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new Compiler(std::move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new Compiler(parsed_ir->parsed)); + break; + +#if SPIRV_CROSS_C_API_GLSL + case SPVC_BACKEND_GLSL: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerGLSL(std::move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerGLSL(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_BACKEND_HLSL: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerHLSL(std::move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerHLSL(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_MSL + case SPVC_BACKEND_MSL: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerMSL(std::move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerMSL(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_CPP + case SPVC_BACKEND_CPP: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerCPP(std::move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerCPP(parsed_ir->parsed)); + break; +#endif + +#if SPIRV_CROSS_C_API_REFLECT + case SPVC_BACKEND_JSON: + if (mode == SPVC_CAPTURE_MODE_TAKE_OWNERSHIP) + comp->compiler.reset(new CompilerReflection(std::move(parsed_ir->parsed))); + else if (mode == SPVC_CAPTURE_MODE_COPY) + comp->compiler.reset(new CompilerReflection(parsed_ir->parsed)); + break; +#endif + + default: + context->report_error("Invalid backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + *compiler = comp.get(); + context->allocations.push_back(std::move(comp)); + } + SPVC_END_SAFE_SCOPE(context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler, spvc_compiler_options *options) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr opt(new (std::nothrow) spvc_compiler_options_s); + if (!opt) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + opt->context = compiler->context; + opt->backend_flags = 0; + switch (compiler->backend) + { +#if SPIRV_CROSS_C_API_MSL + case SPVC_BACKEND_MSL: + opt->backend_flags |= SPVC_COMPILER_OPTION_MSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT; + opt->glsl = static_cast(compiler->compiler.get())->get_common_options(); + opt->msl = static_cast(compiler->compiler.get())->get_msl_options(); + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_BACKEND_HLSL: + opt->backend_flags |= SPVC_COMPILER_OPTION_HLSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT; + opt->glsl = static_cast(compiler->compiler.get())->get_common_options(); + opt->hlsl = static_cast(compiler->compiler.get())->get_hlsl_options(); + break; +#endif + +#if SPIRV_CROSS_C_API_GLSL + case SPVC_BACKEND_GLSL: + opt->backend_flags |= SPVC_COMPILER_OPTION_GLSL_BIT | SPVC_COMPILER_OPTION_COMMON_BIT; + opt->glsl = static_cast(compiler->compiler.get())->get_common_options(); + break; +#endif + + default: + break; + } + + *options = opt.get(); + compiler->context->allocations.push_back(std::move(opt)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options, spvc_compiler_option option, + spvc_bool value) +{ + return spvc_compiler_options_set_uint(options, option, value ? 1 : 0); +} + +spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_compiler_option option, unsigned value) +{ + (void)value; + (void)option; + uint32_t supported_mask = options->backend_flags; + uint32_t required_mask = option & SPVC_COMPILER_OPTION_LANG_BITS; + if ((required_mask | supported_mask) != supported_mask) + { + options->context->report_error("Option is not supported by current backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + switch (option) + { +#if SPIRV_CROSS_C_API_GLSL + case SPVC_COMPILER_OPTION_FORCE_TEMPORARY: + options->glsl.force_temporary = value != 0; + break; + case SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS: + options->glsl.flatten_multidimensional_arrays = value != 0; + break; + case SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION: + options->glsl.vertex.fixup_clipspace = value != 0; + break; + case SPVC_COMPILER_OPTION_FLIP_VERTEX_Y: + options->glsl.vertex.flip_vert_y = value != 0; + break; + case SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES: + options->glsl.emit_line_directives = value != 0; + break; + case SPVC_COMPILER_OPTION_ENABLE_STORAGE_IMAGE_QUALIFIER_DEDUCTION: + options->glsl.enable_storage_image_qualifier_deduction = value != 0; + break; + case SPVC_COMPILER_OPTION_FORCE_ZERO_INITIALIZED_VARIABLES: + options->glsl.force_zero_initialized_variables = value != 0; + break; + + case SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE: + options->glsl.vertex.support_nonzero_base_instance = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS: + options->glsl.separate_shader_objects = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION: + options->glsl.enable_420pack_extension = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_VERSION: + options->glsl.version = value; + break; + case SPVC_COMPILER_OPTION_GLSL_ES: + options->glsl.es = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS: + options->glsl.vulkan_semantics = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP: + options->glsl.fragment.default_float_precision = + value != 0 ? CompilerGLSL::Options::Precision::Highp : CompilerGLSL::Options::Precision::Mediump; + break; + case SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP: + options->glsl.fragment.default_int_precision = + value != 0 ? CompilerGLSL::Options::Precision::Highp : CompilerGLSL::Options::Precision::Mediump; + break; + case SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER: + options->glsl.emit_push_constant_as_uniform_buffer = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS: + options->glsl.emit_uniform_buffer_as_plain_uniforms = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS: + options->glsl.force_flattened_io_blocks = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT: + options->glsl.ovr_multiview_view_count = value; + break; + case SPVC_COMPILER_OPTION_RELAX_NAN_CHECKS: + options->glsl.relax_nan_checks = value != 0; + break; + case SPVC_COMPILER_OPTION_GLSL_ENABLE_ROW_MAJOR_LOAD_WORKAROUND: + options->glsl.enable_row_major_load_workaround = value != 0; + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL: + options->hlsl.shader_model = value; + break; + + case SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT: + options->hlsl.point_size_compat = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT: + options->hlsl.point_coord_compat = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE: + options->hlsl.support_nonzero_base_vertex_base_instance = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_FORCE_STORAGE_BUFFER_AS_UAV: + options->hlsl.force_storage_buffer_as_uav = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV: + options->hlsl.nonwritable_uav_texture_as_srv = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_ENABLE_16BIT_TYPES: + options->hlsl.enable_16bit_types = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS: + options->hlsl.flatten_matrix_vertex_input_semantics = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_USE_ENTRY_POINT_NAME: + options->hlsl.use_entry_point_name = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_PRESERVE_STRUCTURED_BUFFERS: + options->hlsl.preserve_structured_buffers = value != 0; + break; + + case SPVC_COMPILER_OPTION_HLSL_USER_SEMANTIC: + options->hlsl.user_semantic = value != 0; + break; +#endif + +#if SPIRV_CROSS_C_API_MSL + case SPVC_COMPILER_OPTION_MSL_VERSION: + options->msl.msl_version = value; + break; + + case SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH: + options->msl.texel_buffer_texture_width = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SWIZZLE_BUFFER_INDEX: + options->msl.swizzle_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX: + options->msl.indirect_params_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX: + options->msl.shader_output_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX: + options->msl.shader_patch_output_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX: + options->msl.shader_tess_factor_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX: + options->msl.shader_input_wg_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN: + options->msl.enable_point_size_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_DEFAULT: + options->msl.enable_point_size_default = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION: + options->msl.disable_rasterization = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_AUTO_DISABLE_RASTERIZATION: + options->msl.auto_disable_rasterization = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER: + options->msl.capture_output_to_buffer = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES: + options->msl.swizzle_texture_samples = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS: + options->msl.pad_fragment_output_components = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT: + options->msl.tess_domain_origin_lower_left = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_PLATFORM: + options->msl.platform = static_cast(value); + break; + + case SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS: + options->msl.argument_buffers = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_TEXTURE_BUFFER_NATIVE: + options->msl.texture_buffer_native = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX: + options->msl.buffer_size_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_MULTIVIEW: + options->msl.multiview = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX: + options->msl.view_mask_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX: + options->msl.device_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX: + options->msl.view_index_from_device_index = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE: + options->msl.dispatch_base = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_DYNAMIC_OFFSETS_BUFFER_INDEX: + options->msl.dynamic_offsets_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_TEXTURE_1D_AS_2D: + options->msl.texture_1D_as_2D = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_BASE_INDEX_ZERO: + options->msl.enable_base_index_zero = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS: + options->msl.use_framebuffer_fetch_subpasses = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH: + options->msl.invariant_float_math = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY: + options->msl.emulate_cube_array = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING: + options->msl.enable_decoration_binding = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES: + options->msl.force_active_argument_buffer_resources = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS: + options->msl.force_native_arrays = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK: + options->msl.enable_frag_output_mask = value; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN: + options->msl.enable_frag_depth_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN: + options->msl.enable_frag_stencil_ref_builtin = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING: + options->msl.enable_clip_distance_user_varying = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_MULTI_PATCH_WORKGROUP: + options->msl.multi_patch_workgroup = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_BUFFER_INDEX: + options->msl.shader_input_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_INDEX_BUFFER_INDEX: + options->msl.shader_index_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_VERTEX_FOR_TESSELLATION: + options->msl.vertex_for_tessellation = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE: + options->msl.vertex_index_type = static_cast(value); + break; + + case SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING: + options->msl.multiview_layered_rendering = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ARRAYED_SUBPASS_INPUT: + options->msl.arrayed_subpass_input = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_R32UI_LINEAR_TEXTURE_ALIGNMENT: + options->msl.r32ui_linear_texture_alignment = value; + break; + + case SPVC_COMPILER_OPTION_MSL_R32UI_ALIGNMENT_CONSTANT_ID: + options->msl.r32ui_alignment_constant_id = value; + break; + + case SPVC_COMPILER_OPTION_MSL_IOS_USE_SIMDGROUP_FUNCTIONS: + options->msl.ios_use_simdgroup_functions = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_EMULATE_SUBGROUPS: + options->msl.emulate_subgroups = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FIXED_SUBGROUP_SIZE: + options->msl.fixed_subgroup_size = value; + break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING: + options->msl.force_sample_rate_shading = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE: + options->msl.ios_support_base_vertex_instance = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_RAW_BUFFER_TESE_INPUT: + options->msl.raw_buffer_tese_input = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_INPUT_BUFFER_INDEX: + options->msl.shader_patch_input_buffer_index = value; + break; + + case SPVC_COMPILER_OPTION_MSL_MANUAL_HELPER_INVOCATION_UPDATES: + options->msl.manual_helper_invocation_updates = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_CHECK_DISCARDED_FRAG_STORES: + options->msl.check_discarded_frag_stores = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS_TIER: + options->msl.argument_buffers_tier = static_cast(value); + break; + + case SPVC_COMPILER_OPTION_MSL_SAMPLE_DREF_LOD_ARRAY_AS_GRAD: + options->msl.sample_dref_lod_array_as_grad = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_READWRITE_TEXTURE_FENCES: + options->msl.readwrite_texture_fences = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_REPLACE_RECURSIVE_INPUTS: + options->msl.replace_recursive_inputs = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_AGX_MANUAL_CUBE_GRAD_FIXUP: + options->msl.agx_manual_cube_grad_fixup = value != 0; + break; + + case SPVC_COMPILER_OPTION_MSL_FORCE_FRAGMENT_WITH_SIDE_EFFECTS_EXECUTION: + options->msl.force_fragment_with_side_effects_execution = value != 0; + break; +#endif + + default: + options->context->report_error("Unknown option."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler, spvc_compiler_options options) +{ + (void)options; + switch (compiler->backend) + { +#if SPIRV_CROSS_C_API_GLSL + case SPVC_BACKEND_GLSL: + static_cast(*compiler->compiler).set_common_options(options->glsl); + break; +#endif + +#if SPIRV_CROSS_C_API_HLSL + case SPVC_BACKEND_HLSL: + static_cast(*compiler->compiler).set_common_options(options->glsl); + static_cast(*compiler->compiler).set_hlsl_options(options->hlsl); + break; +#endif + +#if SPIRV_CROSS_C_API_MSL + case SPVC_BACKEND_MSL: + static_cast(*compiler->compiler).set_common_options(options->glsl); + static_cast(*compiler->compiler).set_msl_options(options->msl); + break; +#endif + + default: + break; + } + + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->add_header_line(line); + return SPVC_SUCCESS; +#else + (void)line; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *line) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->require_extension(line); + return SPVC_SUCCESS; +#else + (void)line; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +size_t spvc_compiler_get_num_required_extensions(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend != SPVC_BACKEND_GLSL) + { + compiler->context->report_error("Enabled extensions can only be queried on GLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + return static_cast(compiler->compiler.get())->get_required_extensions().size(); +#else + compiler->context->report_error("Enabled extensions can only be queried on GLSL backend."); + return 0; +#endif +} + +const char *spvc_compiler_get_required_extension(spvc_compiler compiler, size_t index) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend != SPVC_BACKEND_GLSL) + { + compiler->context->report_error("Enabled extensions can only be queried on GLSL backend."); + return nullptr; + } + + auto &exts = static_cast(compiler->compiler.get())->get_required_extensions(); + if (index < exts.size()) + return exts[index].c_str(); + else + return nullptr; +#else + (void)index; + compiler->context->report_error("Enabled extensions can only be queried on GLSL backend."); + return nullptr; +#endif +} + +spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->flatten_buffer_block(id); + return SPVC_SUCCESS; +#else + (void)id; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + return static_cast(compiler->compiler.get())->variable_is_depth_or_compare(id) ? SPVC_TRUE : SPVC_FALSE; +#else + (void)id; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_FALSE; +#endif +} + +spvc_result spvc_compiler_mask_stage_output_by_location(spvc_compiler compiler, + unsigned location, unsigned component) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->mask_stage_output_by_location(location, component); + return SPVC_SUCCESS; +#else + (void)location; + (void)component; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_mask_stage_output_by_builtin(spvc_compiler compiler, SpvBuiltIn builtin) +{ +#if SPIRV_CROSS_C_API_GLSL + if (compiler->backend == SPVC_BACKEND_NONE) + { + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + static_cast(compiler->compiler.get())->mask_stage_output_by_builtin(BuiltIn(builtin)); + return SPVC_SUCCESS; +#else + (void)builtin; + compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler, + const spvc_hlsl_root_constants *constant_info, + size_t count) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + vector roots; + roots.reserve(count); + for (size_t i = 0; i < count; i++) + { + RootConstants root; + root.binding = constant_info[i].binding; + root.space = constant_info[i].space; + root.start = constant_info[i].start; + root.end = constant_info[i].end; + roots.push_back(root); + } + + hlsl.set_root_constant_layouts(std::move(roots)); + return SPVC_SUCCESS; +#else + (void)constant_info; + (void)count; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler, + const spvc_hlsl_vertex_attribute_remap *remap, + size_t count) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + HLSLVertexAttributeRemap re; + auto &hlsl = *static_cast(compiler->compiler.get()); + for (size_t i = 0; i < count; i++) + { + re.location = remap[i].location; + re.semantic = remap[i].semantic; + hlsl.add_vertex_attribute_remap(re); + } + + return SPVC_SUCCESS; +#else + (void)remap; + (void)count; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return 0; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + return hlsl.remap_num_workgroups_builtin(); +#else + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return 0; +#endif +} + +spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, + spvc_hlsl_binding_flags flags) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + hlsl.set_resource_binding_flags(flags); + return SPVC_SUCCESS; +#else + (void)flags; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + HLSLResourceBinding bind; + bind.binding = binding->binding; + bind.desc_set = binding->desc_set; + bind.stage = static_cast(binding->stage); + bind.cbv.register_binding = binding->cbv.register_binding; + bind.cbv.register_space = binding->cbv.register_space; + bind.uav.register_binding = binding->uav.register_binding; + bind.uav.register_space = binding->uav.register_space; + bind.srv.register_binding = binding->srv.register_binding; + bind.srv.register_space = binding->srv.register_space; + bind.sampler.register_binding = binding->sampler.register_binding; + bind.sampler.register_space = binding->sampler.register_space; + hlsl.add_hlsl_resource_binding(bind); + return SPVC_SUCCESS; +#else + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set, + unsigned binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + return hlsl.is_hlsl_resource_binding_used(static_cast(model), set, binding) ? SPVC_TRUE : + SPVC_FALSE; +#else + (void)model; + (void)set; + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_is_rasterization_disabled() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_swizzle_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_swizzle_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_buffer_size_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_buffer_size_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler) +{ + return spvc_compiler_msl_needs_swizzle_buffer(compiler); +} + +spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_output_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_patch_output_buffer() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.needs_input_threadgroup_mem() ? SPVC_TRUE : SPVC_FALSE; +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, const spvc_msl_vertex_attribute *va) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLShaderInterfaceVariable attr; + attr.location = va->location; + attr.format = static_cast(va->format); + attr.builtin = static_cast(va->builtin); + msl.add_msl_shader_input(attr); + return SPVC_SUCCESS; +#else + (void)va; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, const spvc_msl_shader_interface_var *si) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLShaderInterfaceVariable input; + input.location = si->location; + input.format = static_cast(si->format); + input.builtin = static_cast(si->builtin); + input.vecsize = si->vecsize; + msl.add_msl_shader_input(input); + return SPVC_SUCCESS; +#else + (void)si; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_shader_input_2(spvc_compiler compiler, const spvc_msl_shader_interface_var_2 *si) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLShaderInterfaceVariable input; + input.location = si->location; + input.format = static_cast(si->format); + input.builtin = static_cast(si->builtin); + input.vecsize = si->vecsize; + input.rate = static_cast(si->rate); + msl.add_msl_shader_input(input); + return SPVC_SUCCESS; +#else + (void)si; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_shader_output(spvc_compiler compiler, const spvc_msl_shader_interface_var *so) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLShaderInterfaceVariable output; + output.location = so->location; + output.format = static_cast(so->format); + output.builtin = static_cast(so->builtin); + output.vecsize = so->vecsize; + msl.add_msl_shader_output(output); + return SPVC_SUCCESS; +#else + (void)so; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_shader_output_2(spvc_compiler compiler, const spvc_msl_shader_interface_var_2 *so) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLShaderInterfaceVariable output; + output.location = so->location; + output.format = static_cast(so->format); + output.builtin = static_cast(so->builtin); + output.vecsize = so->vecsize; + output.rate = static_cast(so->rate); + msl.add_msl_shader_output(output); + return SPVC_SUCCESS; +#else + (void)so; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler, + const spvc_msl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLResourceBinding bind; + bind.binding = binding->binding; + bind.desc_set = binding->desc_set; + bind.stage = static_cast(binding->stage); + bind.msl_buffer = binding->msl_buffer; + bind.msl_texture = binding->msl_texture; + bind.msl_sampler = binding->msl_sampler; + msl.add_msl_resource_binding(bind); + return SPVC_SUCCESS; +#else + (void)binding; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_resource_binding_2(spvc_compiler compiler, + const spvc_msl_resource_binding_2 *binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLResourceBinding bind; + bind.binding = binding->binding; + bind.desc_set = binding->desc_set; + bind.stage = static_cast(binding->stage); + bind.msl_buffer = binding->msl_buffer; + bind.msl_texture = binding->msl_texture; + bind.msl_sampler = binding->msl_sampler; + bind.count = binding->count; + msl.add_msl_resource_binding(bind); + return SPVC_SUCCESS; +#else + (void)binding; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.add_dynamic_buffer(desc_set, binding, index); + return SPVC_SUCCESS; +#else + (void)binding; + (void)desc_set; + (void)index; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.add_inline_uniform_block(desc_set, binding); + return SPVC_SUCCESS; +#else + (void)binding; + (void)desc_set; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.add_discrete_descriptor_set(desc_set); + return SPVC_SUCCESS; +#else + (void)desc_set; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.set_argument_buffer_device_address_space(desc_set, bool(device_address)); + return SPVC_SUCCESS; +#else + (void)desc_set; + (void)device_address; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_msl_is_shader_input_used(spvc_compiler compiler, unsigned location) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.is_msl_shader_input_used(location) ? SPVC_TRUE : SPVC_FALSE; +#else + (void)location; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_is_shader_output_used(spvc_compiler compiler, unsigned location) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.is_msl_shader_output_used(location) ? SPVC_TRUE : SPVC_FALSE; +#else + (void)location; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location) +{ + return spvc_compiler_msl_is_shader_input_used(compiler, location); +} + +spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set, + unsigned binding) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.is_msl_resource_binding_used(static_cast(model), set, binding) ? SPVC_TRUE : + SPVC_FALSE; +#else + (void)model; + (void)set; + (void)binding; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_FALSE; +#endif +} + +spvc_result spvc_compiler_msl_set_combined_sampler_suffix(spvc_compiler compiler, const char *suffix) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.set_combined_sampler_suffix(suffix); + return SPVC_SUCCESS; +#else + (void)suffix; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +const char *spvc_compiler_msl_get_combined_sampler_suffix(spvc_compiler compiler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return ""; + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_combined_sampler_suffix(); +#else + compiler->context->report_error("MSL function used on a non-MSL backend."); + return ""; +#endif +} + +#if SPIRV_CROSS_C_API_MSL +static void spvc_convert_msl_sampler(MSLConstexprSampler &samp, const spvc_msl_constexpr_sampler *sampler) +{ + samp.s_address = static_cast(sampler->s_address); + samp.t_address = static_cast(sampler->t_address); + samp.r_address = static_cast(sampler->r_address); + samp.lod_clamp_min = sampler->lod_clamp_min; + samp.lod_clamp_max = sampler->lod_clamp_max; + samp.lod_clamp_enable = sampler->lod_clamp_enable != 0; + samp.min_filter = static_cast(sampler->min_filter); + samp.mag_filter = static_cast(sampler->mag_filter); + samp.mip_filter = static_cast(sampler->mip_filter); + samp.compare_enable = sampler->compare_enable != 0; + samp.anisotropy_enable = sampler->anisotropy_enable != 0; + samp.max_anisotropy = sampler->max_anisotropy; + samp.compare_func = static_cast(sampler->compare_func); + samp.coord = static_cast(sampler->coord); + samp.border_color = static_cast(sampler->border_color); +} + +static void spvc_convert_msl_sampler_ycbcr_conversion(MSLConstexprSampler &samp, const spvc_msl_sampler_ycbcr_conversion *conv) +{ + samp.ycbcr_conversion_enable = conv != nullptr; + if (conv == nullptr) return; + samp.planes = conv->planes; + samp.resolution = static_cast(conv->resolution); + samp.chroma_filter = static_cast(conv->chroma_filter); + samp.x_chroma_offset = static_cast(conv->x_chroma_offset); + samp.y_chroma_offset = static_cast(conv->y_chroma_offset); + for (int i = 0; i < 4; i++) + samp.swizzle[i] = static_cast(conv->swizzle[i]); + samp.ycbcr_model = static_cast(conv->ycbcr_model); + samp.ycbcr_range = static_cast(conv->ycbcr_range); + samp.bpc = conv->bpc; +} +#endif + +spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, + const spvc_msl_constexpr_sampler *sampler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + msl.remap_constexpr_sampler(id, samp); + return SPVC_SUCCESS; +#else + (void)id; + (void)sampler; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler compiler, + unsigned desc_set, unsigned binding, + const spvc_msl_constexpr_sampler *sampler) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + msl.remap_constexpr_sampler_by_binding(desc_set, binding, samp); + return SPVC_SUCCESS; +#else + (void)desc_set; + (void)binding; + (void)sampler; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_remap_constexpr_sampler_ycbcr(spvc_compiler compiler, spvc_variable_id id, + const spvc_msl_constexpr_sampler *sampler, + const spvc_msl_sampler_ycbcr_conversion *conv) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + spvc_convert_msl_sampler_ycbcr_conversion(samp, conv); + msl.remap_constexpr_sampler(id, samp); + return SPVC_SUCCESS; +#else + (void)id; + (void)sampler; + (void)conv; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(spvc_compiler compiler, + unsigned desc_set, unsigned binding, + const spvc_msl_constexpr_sampler *sampler, + const spvc_msl_sampler_ycbcr_conversion *conv) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + MSLConstexprSampler samp; + spvc_convert_msl_sampler(samp, sampler); + spvc_convert_msl_sampler_ycbcr_conversion(samp, conv); + msl.remap_constexpr_sampler_by_binding(desc_set, binding, samp); + return SPVC_SUCCESS; +#else + (void)desc_set; + (void)binding; + (void)sampler; + (void)conv; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, + unsigned components) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &msl = *static_cast(compiler->compiler.get()); + msl.set_fragment_output_components(location, components); + return SPVC_SUCCESS; +#else + (void)location; + (void)components; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +unsigned spvc_compiler_msl_get_automatic_resource_binding(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_automatic_msl_resource_binding(id); +#else + (void)id; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); +#endif +} + +unsigned spvc_compiler_msl_get_automatic_resource_binding_secondary(spvc_compiler compiler, spvc_variable_id id) +{ +#if SPIRV_CROSS_C_API_MSL + if (compiler->backend != SPVC_BACKEND_MSL) + { + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); + } + + auto &msl = *static_cast(compiler->compiler.get()); + return msl.get_automatic_msl_resource_binding_secondary(id); +#else + (void)id; + compiler->context->report_error("MSL function used on a non-MSL backend."); + return uint32_t(-1); +#endif +} + +spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto result = compiler->compiler->compile(); + if (result.empty()) + { + compiler->context->report_error("Unsupported SPIR-V."); + return SPVC_ERROR_UNSUPPORTED_SPIRV; + } + + *source = compiler->context->allocate_name(result); + if (!*source) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + return SPVC_SUCCESS; + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV) +} + +bool spvc_resources_s::copy_resources(SmallVector &outputs, + const SmallVector &inputs) +{ + for (auto &i : inputs) + { + spvc_reflected_resource r; + r.base_type_id = i.base_type_id; + r.type_id = i.type_id; + r.id = i.id; + r.name = context->allocate_name(i.name); + if (!r.name) + return false; + + outputs.push_back(r); + } + + return true; +} + +bool spvc_resources_s::copy_resources(SmallVector &outputs, + const SmallVector &inputs) +{ + for (auto &i : inputs) + { + spvc_reflected_builtin_resource br; + + br.value_type_id = i.value_type_id; + br.builtin = SpvBuiltIn(i.builtin); + + auto &r = br.resource; + r.base_type_id = i.resource.base_type_id; + r.type_id = i.resource.type_id; + r.id = i.resource.id; + r.name = context->allocate_name(i.resource.name); + if (!r.name) + return false; + + outputs.push_back(br); + } + + return true; +} + +bool spvc_resources_s::copy_resources(const ShaderResources &resources) +{ + if (!copy_resources(uniform_buffers, resources.uniform_buffers)) + return false; + if (!copy_resources(storage_buffers, resources.storage_buffers)) + return false; + if (!copy_resources(stage_inputs, resources.stage_inputs)) + return false; + if (!copy_resources(stage_outputs, resources.stage_outputs)) + return false; + if (!copy_resources(subpass_inputs, resources.subpass_inputs)) + return false; + if (!copy_resources(storage_images, resources.storage_images)) + return false; + if (!copy_resources(sampled_images, resources.sampled_images)) + return false; + if (!copy_resources(atomic_counters, resources.atomic_counters)) + return false; + if (!copy_resources(push_constant_buffers, resources.push_constant_buffers)) + return false; + if (!copy_resources(shader_record_buffers, resources.shader_record_buffers)) + return false; + if (!copy_resources(separate_images, resources.separate_images)) + return false; + if (!copy_resources(separate_samplers, resources.separate_samplers)) + return false; + if (!copy_resources(acceleration_structures, resources.acceleration_structures)) + return false; + if (!copy_resources(gl_plain_uniforms, resources.gl_plain_uniforms)) + return false; + if (!copy_resources(tensors, resources.tensors)) + return false; + if (!copy_resources(builtin_inputs, resources.builtin_inputs)) + return false; + if (!copy_resources(builtin_outputs, resources.builtin_outputs)) + return false; + + return true; +} + +spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr ptr(new (std::nothrow) spvc_set_s); + if (!ptr) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + auto active = compiler->compiler->get_active_interface_variables(); + ptr->set = std::move(active); + *set = ptr.get(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set) +{ + SPVC_BEGIN_SAFE_SCOPE + { + compiler->compiler->set_enabled_interface_variables(set->set); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler, spvc_resources *resources, + spvc_set set) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr res(new (std::nothrow) spvc_resources_s); + if (!res) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + res->context = compiler->context; + auto accessed_resources = compiler->compiler->get_shader_resources(set->set); + + if (!res->copy_resources(accessed_resources)) + { + res->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + *resources = res.get(); + compiler->context->allocations.push_back(std::move(res)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources) +{ + SPVC_BEGIN_SAFE_SCOPE + { + std::unique_ptr res(new (std::nothrow) spvc_resources_s); + if (!res) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + res->context = compiler->context; + auto accessed_resources = compiler->compiler->get_shader_resources(); + + if (!res->copy_resources(accessed_resources)) + { + res->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + + *resources = res.get(); + compiler->context->allocations.push_back(std::move(res)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type, + const spvc_reflected_resource **resource_list, + size_t *resource_size) +{ + const SmallVector *list = nullptr; + switch (type) + { + case SPVC_RESOURCE_TYPE_UNIFORM_BUFFER: + list = &resources->uniform_buffers; + break; + + case SPVC_RESOURCE_TYPE_STORAGE_BUFFER: + list = &resources->storage_buffers; + break; + + case SPVC_RESOURCE_TYPE_STAGE_INPUT: + list = &resources->stage_inputs; + break; + + case SPVC_RESOURCE_TYPE_STAGE_OUTPUT: + list = &resources->stage_outputs; + break; + + case SPVC_RESOURCE_TYPE_SUBPASS_INPUT: + list = &resources->subpass_inputs; + break; + + case SPVC_RESOURCE_TYPE_STORAGE_IMAGE: + list = &resources->storage_images; + break; + + case SPVC_RESOURCE_TYPE_SAMPLED_IMAGE: + list = &resources->sampled_images; + break; + + case SPVC_RESOURCE_TYPE_ATOMIC_COUNTER: + list = &resources->atomic_counters; + break; + + case SPVC_RESOURCE_TYPE_PUSH_CONSTANT: + list = &resources->push_constant_buffers; + break; + + case SPVC_RESOURCE_TYPE_SEPARATE_IMAGE: + list = &resources->separate_images; + break; + + case SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS: + list = &resources->separate_samplers; + break; + + case SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE: + list = &resources->acceleration_structures; + break; + + case SPVC_RESOURCE_TYPE_SHADER_RECORD_BUFFER: + list = &resources->shader_record_buffers; + break; + + case SPVC_RESOURCE_TYPE_GL_PLAIN_UNIFORM: + list = &resources->gl_plain_uniforms; + break; + + case SPVC_RESOURCE_TYPE_TENSOR: + list = &resources->tensors; + break; + + default: + break; + } + + if (!list) + { + resources->context->report_error("Invalid argument."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + *resource_size = list->size(); + *resource_list = list->data(); + return SPVC_SUCCESS; +} + +spvc_result spvc_resources_get_builtin_resource_list_for_type( + spvc_resources resources, spvc_builtin_resource_type type, + const spvc_reflected_builtin_resource **resource_list, + size_t *resource_size) +{ + const SmallVector *list = nullptr; + switch (type) + { + case SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT: + list = &resources->builtin_inputs; + break; + + case SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT: + list = &resources->builtin_outputs; + break; + + default: + break; + } + + if (!list) + { + resources->context->report_error("Invalid argument."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + *resource_size = list->size(); + *resource_list = list->data(); + return SPVC_SUCCESS; +} + +void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, unsigned argument) +{ + compiler->compiler->set_decoration(id, static_cast(decoration), argument); +} + +void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + const char *argument) +{ + compiler->compiler->set_decoration_string(id, static_cast(decoration), argument); +} + +void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument) +{ + compiler->compiler->set_name(id, argument); +} + +void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration, unsigned argument) +{ + compiler->compiler->set_member_decoration(id, member_index, static_cast(decoration), argument); +} + +void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration, const char *argument) +{ + compiler->compiler->set_member_decoration_string(id, member_index, static_cast(decoration), + argument); +} + +void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index, const char *argument) +{ + compiler->compiler->set_member_name(id, member_index, argument); +} + +void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + compiler->compiler->unset_decoration(id, static_cast(decoration)); +} + +void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + compiler->compiler->unset_member_decoration(id, member_index, static_cast(decoration)); +} + +spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + return compiler->compiler->has_decoration(id, static_cast(decoration)) ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + return compiler->compiler->has_member_decoration(id, member_index, static_cast(decoration)) ? + SPVC_TRUE : + SPVC_FALSE; +} + +const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id) +{ + return compiler->compiler->get_name(id).c_str(); +} + +unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + return compiler->compiler->get_decoration(id, static_cast(decoration)); +} + +const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration) +{ + return compiler->compiler->get_decoration_string(id, static_cast(decoration)).c_str(); +} + +unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + return compiler->compiler->get_member_decoration(id, member_index, static_cast(decoration)); +} + +const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration) +{ + return compiler->compiler->get_member_decoration_string(id, member_index, static_cast(decoration)) + .c_str(); +} + +const char *spvc_compiler_get_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index) +{ + return compiler->compiler->get_member_name(id, member_index).c_str(); +} + +spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler, const spvc_entry_point **entry_points, + size_t *num_entry_points) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto entries = compiler->compiler->get_entry_points_and_stages(); + SmallVector translated; + translated.reserve(entries.size()); + + for (auto &entry : entries) + { + spvc_entry_point new_entry; + new_entry.execution_model = static_cast(entry.execution_model); + new_entry.name = compiler->context->allocate_name(entry.name); + if (!new_entry.name) + { + compiler->context->report_error("Out of memory."); + return SPVC_ERROR_OUT_OF_MEMORY; + } + translated.push_back(new_entry); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *entry_points = ptr->buffer.data(); + *num_entry_points = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name, SpvExecutionModel model) +{ + SPVC_BEGIN_SAFE_SCOPE + { + compiler->compiler->set_entry_point(name, static_cast(model)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name, const char *new_name, + SpvExecutionModel model) +{ + SPVC_BEGIN_SAFE_SCOPE + { + compiler->compiler->rename_entry_point(old_name, new_name, static_cast(model)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name, + SpvExecutionModel model) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto cleansed_name = + compiler->compiler->get_cleansed_entry_point_name(name, static_cast(model)); + return compiler->context->allocate_name(cleansed_name); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode) +{ + compiler->compiler->set_execution_mode(static_cast(mode)); +} + +void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode, unsigned arg0, + unsigned arg1, + unsigned arg2) +{ + compiler->compiler->set_execution_mode(static_cast(mode), arg0, arg1, arg2); +} + +void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode) +{ + compiler->compiler->unset_execution_mode(static_cast(mode)); +} + +spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes, size_t *num_modes) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto ptr = spvc_allocate>(); + + compiler->compiler->get_execution_mode_bitset().for_each_bit( + [&](uint32_t bit) { ptr->buffer.push_back(static_cast(bit)); }); + + *modes = ptr->buffer.data(); + *num_modes = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode) +{ + return compiler->compiler->get_execution_mode_argument(static_cast(mode)); +} + +unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler, SpvExecutionMode mode, + unsigned index) +{ + return compiler->compiler->get_execution_mode_argument(static_cast(mode), index); +} + +SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler) +{ + return static_cast(compiler->compiler->get_execution_model()); +} + +void spvc_compiler_update_active_builtins(spvc_compiler compiler) +{ + compiler->compiler->update_active_builtins(); +} + +spvc_bool spvc_compiler_has_active_builtin(spvc_compiler compiler, SpvBuiltIn builtin, SpvStorageClass storage) +{ + return compiler->compiler->has_active_builtin(static_cast(builtin), static_cast(storage)) ? + SPVC_TRUE : + SPVC_FALSE; +} + +spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id) +{ + // Should only throw if an intentionally garbage ID is passed, but the IDs are not type-safe. + SPVC_BEGIN_SAFE_SCOPE + { + return static_cast(&compiler->compiler->get_type(id)); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +spvc_type_id spvc_type_get_base_type_id(spvc_type type) +{ + return type->self; +} + +static spvc_basetype convert_basetype(SPIRType::BaseType type) +{ + // For now the enums match up. + return static_cast(type); +} + +spvc_basetype spvc_type_get_basetype(spvc_type type) +{ + return convert_basetype(type->basetype); +} + +unsigned spvc_type_get_bit_width(spvc_type type) +{ + return type->width; +} + +unsigned spvc_type_get_vector_size(spvc_type type) +{ + return type->vecsize; +} + +unsigned spvc_type_get_columns(spvc_type type) +{ + return type->columns; +} + +unsigned spvc_type_get_num_array_dimensions(spvc_type type) +{ + return unsigned(type->array.size()); +} + +spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension) +{ + return type->array_size_literal[dimension] ? SPVC_TRUE : SPVC_FALSE; +} + +SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension) +{ + return type->array[dimension]; +} + +unsigned spvc_type_get_num_member_types(spvc_type type) +{ + return unsigned(type->member_types.size()); +} + +spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index) +{ + return type->member_types[index]; +} + +SpvStorageClass spvc_type_get_storage_class(spvc_type type) +{ + return static_cast(type->storage); +} + +// Image type query. +spvc_type_id spvc_type_get_image_sampled_type(spvc_type type) +{ + return type->image.type; +} + +SpvDim spvc_type_get_image_dimension(spvc_type type) +{ + return static_cast(type->image.dim); +} + +spvc_bool spvc_type_get_image_is_depth(spvc_type type) +{ + return type->image.depth ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_type_get_image_arrayed(spvc_type type) +{ + return type->image.arrayed ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_type_get_image_multisampled(spvc_type type) +{ + return type->image.ms ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_type_get_image_is_storage(spvc_type type) +{ + return type->image.sampled == 2 ? SPVC_TRUE : SPVC_FALSE; +} + +SpvImageFormat spvc_type_get_image_storage_format(spvc_type type) +{ + return static_cast(static_cast(type)->image.format); +} + +SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type) +{ + return static_cast(static_cast(type)->image.access); +} + +spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *size = compiler->compiler->get_declared_struct_size(*static_cast(struct_type)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler, spvc_type struct_type, + size_t array_size, size_t *size) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *size = compiler->compiler->get_declared_struct_size_runtime_array(*static_cast(struct_type), + array_size); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_declared_struct_member_size(spvc_compiler compiler, spvc_type struct_type, unsigned index, size_t *size) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *size = compiler->compiler->get_declared_struct_member_size(*static_cast(struct_type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *offset) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *offset = compiler->compiler->type_struct_member_offset(*static_cast(type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *stride) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *stride = compiler->compiler->type_struct_member_array_stride(*static_cast(type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler, spvc_type type, unsigned index, unsigned *stride) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *stride = compiler->compiler->type_struct_member_matrix_stride(*static_cast(type), index); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id) +{ + SPVC_BEGIN_SAFE_SCOPE + { + *id = compiler->compiler->build_dummy_sampler_for_combined_images(); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler) +{ + SPVC_BEGIN_SAFE_SCOPE + { + compiler->compiler->build_combined_image_samplers(); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler, + const spvc_combined_image_sampler **samplers, + size_t *num_samplers) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto combined = compiler->compiler->get_combined_image_samplers(); + SmallVector translated; + translated.reserve(combined.size()); + for (auto &c : combined) + { + spvc_combined_image_sampler trans = { c.combined_id, c.image_id, c.sampler_id }; + translated.push_back(trans); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *samplers = ptr->buffer.data(); + *num_samplers = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler, + const spvc_specialization_constant **constants, + size_t *num_constants) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto spec_constants = compiler->compiler->get_specialization_constants(); + SmallVector translated; + translated.reserve(spec_constants.size()); + for (auto &c : spec_constants) + { + spvc_specialization_constant trans = { c.id, c.constant_id }; + translated.push_back(trans); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *constants = ptr->buffer.data(); + *num_constants = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler, spvc_variable_id id) +{ + SPVC_BEGIN_SAFE_SCOPE + { + return static_cast(&compiler->compiler->get_constant(id)); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler, + spvc_specialization_constant *x, + spvc_specialization_constant *y, + spvc_specialization_constant *z) +{ + SpecializationConstant tmpx; + SpecializationConstant tmpy; + SpecializationConstant tmpz; + spvc_constant_id ret = compiler->compiler->get_work_group_size_specialization_constants(tmpx, tmpy, tmpz); + x->id = tmpx.id; + x->constant_id = tmpx.constant_id; + y->id = tmpy.id; + y->constant_id = tmpy.constant_id; + z->id = tmpz.id; + z->constant_id = tmpz.constant_id; + return ret; +} + +spvc_result spvc_compiler_get_active_buffer_ranges(spvc_compiler compiler, + spvc_variable_id id, + const spvc_buffer_range **ranges, + size_t *num_ranges) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto active_ranges = compiler->compiler->get_active_buffer_ranges(id); + SmallVector translated; + translated.reserve(active_ranges.size()); + for (auto &r : active_ranges) + { + spvc_buffer_range trans = { r.index, r.offset, r.range }; + translated.push_back(trans); + } + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(translated); + *ranges = ptr->buffer.data(); + *num_ranges = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_f16(column, row); +} + +float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_f32(column, row); +} + +double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_f64(column, row); +} + +unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar(column, row); +} + +int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_i32(column, row); +} + +unsigned long long spvc_constant_get_scalar_u64(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_u64(column, row); +} + +long long spvc_constant_get_scalar_i64(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_i64(column, row); +} + +unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_u16(column, row); +} + +int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_i16(column, row); +} + +unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_u8(column, row); +} + +int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row) +{ + return constant->scalar_i8(column, row); +} + +void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count) +{ + static_assert(sizeof(spvc_constant_id) == sizeof(constant->subconstants.front()), "ID size is not consistent."); + *constituents = reinterpret_cast(constant->subconstants.data()); + *count = constant->subconstants.size(); +} + +spvc_type_id spvc_constant_get_type(spvc_constant constant) +{ + return constant->constant_type; +} + +void spvc_constant_set_scalar_fp16(spvc_constant constant, unsigned column, unsigned row, unsigned short value) +{ + constant->m.c[column].r[row].u32 = value; +} + +void spvc_constant_set_scalar_fp32(spvc_constant constant, unsigned column, unsigned row, float value) +{ + constant->m.c[column].r[row].f32 = value; +} + +void spvc_constant_set_scalar_fp64(spvc_constant constant, unsigned column, unsigned row, double value) +{ + constant->m.c[column].r[row].f64 = value; +} + +void spvc_constant_set_scalar_u32(spvc_constant constant, unsigned column, unsigned row, unsigned value) +{ + constant->m.c[column].r[row].u32 = value; +} + +void spvc_constant_set_scalar_i32(spvc_constant constant, unsigned column, unsigned row, int value) +{ + constant->m.c[column].r[row].i32 = value; +} + +void spvc_constant_set_scalar_u64(spvc_constant constant, unsigned column, unsigned row, unsigned long long value) +{ + constant->m.c[column].r[row].u64 = value; +} + +void spvc_constant_set_scalar_i64(spvc_constant constant, unsigned column, unsigned row, long long value) +{ + constant->m.c[column].r[row].i64 = value; +} + +void spvc_constant_set_scalar_u16(spvc_constant constant, unsigned column, unsigned row, unsigned short value) +{ + constant->m.c[column].r[row].u32 = uint32_t(value); +} + +void spvc_constant_set_scalar_i16(spvc_constant constant, unsigned column, unsigned row, signed short value) +{ + constant->m.c[column].r[row].u32 = uint32_t(value); +} + +void spvc_constant_set_scalar_u8(spvc_constant constant, unsigned column, unsigned row, unsigned char value) +{ + constant->m.c[column].r[row].u32 = uint32_t(value); +} + +void spvc_constant_set_scalar_i8(spvc_constant constant, unsigned column, unsigned row, signed char value) +{ + constant->m.c[column].r[row].u32 = uint32_t(value); +} + +spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler, spvc_variable_id id, + SpvDecoration decoration, + unsigned *word_offset) +{ + uint32_t off = 0; + bool ret = compiler->compiler->get_binary_offset_for_decoration(id, static_cast(decoration), off); + if (ret) + { + *word_offset = off; + return SPVC_TRUE; + } + else + return SPVC_FALSE; +} + +spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id) +{ + return compiler->compiler->buffer_is_hlsl_counter_buffer(id) ? SPVC_TRUE : SPVC_FALSE; +} + +spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id, + spvc_variable_id *counter_id) +{ + uint32_t buffer; + bool ret = compiler->compiler->buffer_get_hlsl_counter_buffer(id, buffer); + if (ret) + { + *counter_id = buffer; + return SPVC_TRUE; + } + else + return SPVC_FALSE; +} + +spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler, const SpvCapability **capabilities, + size_t *num_capabilities) +{ + auto &caps = compiler->compiler->get_declared_capabilities(); + static_assert(sizeof(SpvCapability) == sizeof(Capability), "Enum size mismatch."); + *capabilities = reinterpret_cast(caps.data()); + *num_capabilities = caps.size(); + return SPVC_SUCCESS; +} + +spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions, + size_t *num_extensions) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto &exts = compiler->compiler->get_declared_extensions(); + SmallVector duped; + duped.reserve(exts.size()); + for (auto &ext : exts) + duped.push_back(compiler->context->allocate_name(ext)); + + auto ptr = spvc_allocate>(); + ptr->buffer = std::move(duped); + *extensions = ptr->buffer.data(); + *num_extensions = ptr->buffer.size(); + compiler->context->allocations.push_back(std::move(ptr)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_OUT_OF_MEMORY) + return SPVC_SUCCESS; +} + +const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto name = compiler->compiler->get_remapped_declared_block_name(id); + return compiler->context->allocate_name(name); + } + SPVC_END_SAFE_SCOPE(compiler->context, nullptr) +} + +spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id, + const SpvDecoration **decorations, size_t *num_decorations) +{ + SPVC_BEGIN_SAFE_SCOPE + { + auto flags = compiler->compiler->get_buffer_block_flags(id); + auto bitset = spvc_allocate>(); + + flags.for_each_bit([&](uint32_t bit) { bitset->buffer.push_back(static_cast(bit)); }); + + *decorations = bitset->buffer.data(); + *num_decorations = bitset->buffer.size(); + compiler->context->allocations.push_back(std::move(bitset)); + } + SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_INVALID_ARGUMENT) + return SPVC_SUCCESS; +} + +unsigned spvc_msl_get_aux_buffer_struct_version(void) +{ + return SPVC_MSL_AUX_BUFFER_STRUCT_VERSION; +} + +void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr) +{ +#if SPIRV_CROSS_C_API_MSL + // Crude, but works. + MSLShaderInterfaceVariable attr_default; + attr->location = attr_default.location; + attr->format = static_cast(attr_default.format); + attr->builtin = static_cast(attr_default.builtin); +#else + memset(attr, 0, sizeof(*attr)); +#endif +} + +void spvc_msl_shader_interface_var_init(spvc_msl_shader_interface_var *var) +{ +#if SPIRV_CROSS_C_API_MSL + MSLShaderInterfaceVariable var_default; + var->location = var_default.location; + var->format = static_cast(var_default.format); + var->builtin = static_cast(var_default.builtin); + var->vecsize = var_default.vecsize; +#else + memset(var, 0, sizeof(*var)); +#endif +} + +void spvc_msl_shader_input_init(spvc_msl_shader_input *input) +{ + spvc_msl_shader_interface_var_init(input); +} + +void spvc_msl_shader_interface_var_init_2(spvc_msl_shader_interface_var_2 *var) +{ +#if SPIRV_CROSS_C_API_MSL + MSLShaderInterfaceVariable var_default; + var->location = var_default.location; + var->format = static_cast(var_default.format); + var->builtin = static_cast(var_default.builtin); + var->vecsize = var_default.vecsize; + var->rate = static_cast(var_default.rate); +#else + memset(var, 0, sizeof(*var)); +#endif +} + +void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_MSL + MSLResourceBinding binding_default; + binding->desc_set = binding_default.desc_set; + binding->binding = binding_default.binding; + binding->msl_buffer = binding_default.msl_buffer; + binding->msl_texture = binding_default.msl_texture; + binding->msl_sampler = binding_default.msl_sampler; + binding->stage = static_cast(binding_default.stage); +#else + memset(binding, 0, sizeof(*binding)); +#endif +} + +void spvc_msl_resource_binding_init_2(spvc_msl_resource_binding_2 *binding) +{ +#if SPIRV_CROSS_C_API_MSL + MSLResourceBinding binding_default; + binding->desc_set = binding_default.desc_set; + binding->binding = binding_default.binding; + binding->msl_buffer = binding_default.msl_buffer; + binding->msl_texture = binding_default.msl_texture; + binding->msl_sampler = binding_default.msl_sampler; + binding->stage = static_cast(binding_default.stage); + binding->count = 0; +#else + memset(binding, 0, sizeof(*binding)); +#endif +} + +void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + HLSLResourceBinding binding_default; + binding->desc_set = binding_default.desc_set; + binding->binding = binding_default.binding; + binding->cbv.register_binding = binding_default.cbv.register_binding; + binding->cbv.register_space = binding_default.cbv.register_space; + binding->srv.register_binding = binding_default.srv.register_binding; + binding->srv.register_space = binding_default.srv.register_space; + binding->uav.register_binding = binding_default.uav.register_binding; + binding->uav.register_space = binding_default.uav.register_space; + binding->sampler.register_binding = binding_default.sampler.register_binding; + binding->sampler.register_space = binding_default.sampler.register_space; + binding->stage = static_cast(binding_default.stage); +#else + memset(binding, 0, sizeof(*binding)); +#endif +} + +void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler) +{ +#if SPIRV_CROSS_C_API_MSL + MSLConstexprSampler defaults; + sampler->anisotropy_enable = defaults.anisotropy_enable ? SPVC_TRUE : SPVC_FALSE; + sampler->border_color = static_cast(defaults.border_color); + sampler->compare_enable = defaults.compare_enable ? SPVC_TRUE : SPVC_FALSE; + sampler->coord = static_cast(defaults.coord); + sampler->compare_func = static_cast(defaults.compare_func); + sampler->lod_clamp_enable = defaults.lod_clamp_enable ? SPVC_TRUE : SPVC_FALSE; + sampler->lod_clamp_max = defaults.lod_clamp_max; + sampler->lod_clamp_min = defaults.lod_clamp_min; + sampler->mag_filter = static_cast(defaults.mag_filter); + sampler->min_filter = static_cast(defaults.min_filter); + sampler->mip_filter = static_cast(defaults.mip_filter); + sampler->max_anisotropy = defaults.max_anisotropy; + sampler->s_address = static_cast(defaults.s_address); + sampler->t_address = static_cast(defaults.t_address); + sampler->r_address = static_cast(defaults.r_address); +#else + memset(sampler, 0, sizeof(*sampler)); +#endif +} + +void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycbcr_conversion *conv) +{ +#if SPIRV_CROSS_C_API_MSL + MSLConstexprSampler defaults; + conv->planes = defaults.planes; + conv->resolution = static_cast(defaults.resolution); + conv->chroma_filter = static_cast(defaults.chroma_filter); + conv->x_chroma_offset = static_cast(defaults.x_chroma_offset); + conv->y_chroma_offset = static_cast(defaults.y_chroma_offset); + for (int i = 0; i < 4; i++) + conv->swizzle[i] = static_cast(defaults.swizzle[i]); + conv->ycbcr_model = static_cast(defaults.ycbcr_model); + conv->ycbcr_range = static_cast(defaults.ycbcr_range); +#else + memset(conv, 0, sizeof(*conv)); +#endif +} + +unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler) +{ + return compiler->compiler->get_current_id_bound(); +} + +void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch) +{ + *major = SPVC_C_API_VERSION_MAJOR; + *minor = SPVC_C_API_VERSION_MINOR; + *patch = SPVC_C_API_VERSION_PATCH; +} + +const char *spvc_get_commit_revision_and_timestamp(void) +{ +#ifdef HAVE_SPIRV_CROSS_GIT_VERSION + return SPIRV_CROSS_GIT_REVISION; +#else + return ""; +#endif +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cross_c.h b/thirdparty/SPIRV-Cross/spirv_cross_c.h new file mode 100644 index 000000000..30f1c459c --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_c.h @@ -0,0 +1,1130 @@ +/* + * Copyright 2019-2021 Hans-Kristian Arntzen + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_C_API_H +#define SPIRV_CROSS_C_API_H + +#include +#include "spirv.h" + +/* + * C89-compatible wrapper for SPIRV-Cross' API. + * Documentation here is sparse unless the behavior does not map 1:1 with C++ API. + * It is recommended to look at the canonical C++ API for more detailed information. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Bumped if ABI or API breaks backwards compatibility. */ +#define SPVC_C_API_VERSION_MAJOR 0 +/* Bumped if APIs or enumerations are added in a backwards compatible way. */ +#define SPVC_C_API_VERSION_MINOR 68 +/* Bumped if internal implementation details change. */ +#define SPVC_C_API_VERSION_PATCH 0 + +#if !defined(SPVC_PUBLIC_API) +#if defined(SPVC_EXPORT_SYMBOLS) +/* Exports symbols. Standard C calling convention is used. */ +#if defined(__GNUC__) +#define SPVC_PUBLIC_API __attribute__((visibility("default"))) +#elif defined(_MSC_VER) +#define SPVC_PUBLIC_API __declspec(dllexport) +#else +#define SPVC_PUBLIC_API +#endif +#else +#define SPVC_PUBLIC_API +#endif +#endif + +/* + * Gets the SPVC_C_API_VERSION_* used to build this library. + * Can be used to check for ABI mismatch if so-versioning did not catch it. + */ +SPVC_PUBLIC_API void spvc_get_version(unsigned *major, unsigned *minor, unsigned *patch); + +/* Gets a human readable version string to identify which commit a particular binary was created from. */ +SPVC_PUBLIC_API const char *spvc_get_commit_revision_and_timestamp(void); + +/* These types are opaque to the user. */ +typedef struct spvc_context_s *spvc_context; +typedef struct spvc_parsed_ir_s *spvc_parsed_ir; +typedef struct spvc_compiler_s *spvc_compiler; +typedef struct spvc_compiler_options_s *spvc_compiler_options; +typedef struct spvc_resources_s *spvc_resources; +struct spvc_type_s; +typedef const struct spvc_type_s *spvc_type; +typedef struct spvc_constant_s *spvc_constant; +struct spvc_set_s; +typedef const struct spvc_set_s *spvc_set; + +/* + * Shallow typedefs. All SPIR-V IDs are plain 32-bit numbers, but this helps communicate which data is used. + * Maps to a SPIRType. + */ +typedef SpvId spvc_type_id; +/* Maps to a SPIRVariable. */ +typedef SpvId spvc_variable_id; +/* Maps to a SPIRConstant. */ +typedef SpvId spvc_constant_id; + +/* See C++ API. */ +typedef struct spvc_reflected_resource +{ + spvc_variable_id id; + spvc_type_id base_type_id; + spvc_type_id type_id; + const char *name; +} spvc_reflected_resource; + +typedef struct spvc_reflected_builtin_resource +{ + SpvBuiltIn builtin; + spvc_type_id value_type_id; + spvc_reflected_resource resource; +} spvc_reflected_builtin_resource; + +/* See C++ API. */ +typedef struct spvc_entry_point +{ + SpvExecutionModel execution_model; + const char *name; +} spvc_entry_point; + +/* See C++ API. */ +typedef struct spvc_combined_image_sampler +{ + spvc_variable_id combined_id; + spvc_variable_id image_id; + spvc_variable_id sampler_id; +} spvc_combined_image_sampler; + +/* See C++ API. */ +typedef struct spvc_specialization_constant +{ + spvc_constant_id id; + unsigned constant_id; +} spvc_specialization_constant; + +/* See C++ API. */ +typedef struct spvc_buffer_range +{ + unsigned index; + size_t offset; + size_t range; +} spvc_buffer_range; + +/* See C++ API. */ +typedef struct spvc_hlsl_root_constants +{ + unsigned start; + unsigned end; + unsigned binding; + unsigned space; +} spvc_hlsl_root_constants; + +/* See C++ API. */ +typedef struct spvc_hlsl_vertex_attribute_remap +{ + unsigned location; + const char *semantic; +} spvc_hlsl_vertex_attribute_remap; + +/* + * Be compatible with non-C99 compilers, which do not have stdbool. + * Only recent MSVC compilers supports this for example, and ideally SPIRV-Cross should be linkable + * from a wide range of compilers in its C wrapper. + */ +typedef unsigned char spvc_bool; +#define SPVC_TRUE ((spvc_bool)1) +#define SPVC_FALSE ((spvc_bool)0) + +typedef enum spvc_result +{ + /* Success. */ + SPVC_SUCCESS = 0, + + /* The SPIR-V is invalid. Should have been caught by validation ideally. */ + SPVC_ERROR_INVALID_SPIRV = -1, + + /* The SPIR-V might be valid or invalid, but SPIRV-Cross currently cannot correctly translate this to your target language. */ + SPVC_ERROR_UNSUPPORTED_SPIRV = -2, + + /* If for some reason we hit this, new or malloc failed. */ + SPVC_ERROR_OUT_OF_MEMORY = -3, + + /* Invalid API argument. */ + SPVC_ERROR_INVALID_ARGUMENT = -4, + + SPVC_ERROR_INT_MAX = 0x7fffffff +} spvc_result; + +typedef enum spvc_capture_mode +{ + /* The Parsed IR payload will be copied, and the handle can be reused to create other compiler instances. */ + SPVC_CAPTURE_MODE_COPY = 0, + + /* + * The payload will now be owned by the compiler. + * parsed_ir should now be considered a dead blob and must not be used further. + * This is optimal for performance and should be the go-to option. + */ + SPVC_CAPTURE_MODE_TAKE_OWNERSHIP = 1, + + SPVC_CAPTURE_MODE_INT_MAX = 0x7fffffff +} spvc_capture_mode; + +typedef enum spvc_backend +{ + /* This backend can only perform reflection, no compiler options are supported. Maps to spirv_cross::Compiler. */ + SPVC_BACKEND_NONE = 0, + SPVC_BACKEND_GLSL = 1, /* spirv_cross::CompilerGLSL */ + SPVC_BACKEND_HLSL = 2, /* CompilerHLSL */ + SPVC_BACKEND_MSL = 3, /* CompilerMSL */ + SPVC_BACKEND_CPP = 4, /* CompilerCPP */ + SPVC_BACKEND_JSON = 5, /* CompilerReflection w/ JSON backend */ + SPVC_BACKEND_INT_MAX = 0x7fffffff +} spvc_backend; + +/* Maps to C++ API. */ +typedef enum spvc_resource_type +{ + SPVC_RESOURCE_TYPE_UNKNOWN = 0, + SPVC_RESOURCE_TYPE_UNIFORM_BUFFER = 1, + SPVC_RESOURCE_TYPE_STORAGE_BUFFER = 2, + SPVC_RESOURCE_TYPE_STAGE_INPUT = 3, + SPVC_RESOURCE_TYPE_STAGE_OUTPUT = 4, + SPVC_RESOURCE_TYPE_SUBPASS_INPUT = 5, + SPVC_RESOURCE_TYPE_STORAGE_IMAGE = 6, + SPVC_RESOURCE_TYPE_SAMPLED_IMAGE = 7, + SPVC_RESOURCE_TYPE_ATOMIC_COUNTER = 8, + SPVC_RESOURCE_TYPE_PUSH_CONSTANT = 9, + SPVC_RESOURCE_TYPE_SEPARATE_IMAGE = 10, + SPVC_RESOURCE_TYPE_SEPARATE_SAMPLERS = 11, + SPVC_RESOURCE_TYPE_ACCELERATION_STRUCTURE = 12, + SPVC_RESOURCE_TYPE_RAY_QUERY = 13, + SPVC_RESOURCE_TYPE_SHADER_RECORD_BUFFER = 14, + SPVC_RESOURCE_TYPE_GL_PLAIN_UNIFORM = 15, + SPVC_RESOURCE_TYPE_TENSOR = 16, + SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff +} spvc_resource_type; + +typedef enum spvc_builtin_resource_type +{ + SPVC_BUILTIN_RESOURCE_TYPE_UNKNOWN = 0, + SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT = 1, + SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT = 2, + SPVC_BUILTIN_RESOURCE_TYPE_INT_MAX = 0x7fffffff +} spvc_builtin_resource_type; + +/* Maps to spirv_cross::SPIRType::BaseType. */ +typedef enum spvc_basetype +{ + SPVC_BASETYPE_UNKNOWN = 0, + SPVC_BASETYPE_VOID = 1, + SPVC_BASETYPE_BOOLEAN = 2, + SPVC_BASETYPE_INT8 = 3, + SPVC_BASETYPE_UINT8 = 4, + SPVC_BASETYPE_INT16 = 5, + SPVC_BASETYPE_UINT16 = 6, + SPVC_BASETYPE_INT32 = 7, + SPVC_BASETYPE_UINT32 = 8, + SPVC_BASETYPE_INT64 = 9, + SPVC_BASETYPE_UINT64 = 10, + SPVC_BASETYPE_ATOMIC_COUNTER = 11, + SPVC_BASETYPE_FP16 = 12, + SPVC_BASETYPE_FP32 = 13, + SPVC_BASETYPE_FP64 = 14, + SPVC_BASETYPE_STRUCT = 15, + SPVC_BASETYPE_IMAGE = 16, + SPVC_BASETYPE_SAMPLED_IMAGE = 17, + SPVC_BASETYPE_SAMPLER = 18, + SPVC_BASETYPE_ACCELERATION_STRUCTURE = 19, + + SPVC_BASETYPE_INT_MAX = 0x7fffffff +} spvc_basetype; + +#define SPVC_COMPILER_OPTION_COMMON_BIT 0x1000000 +#define SPVC_COMPILER_OPTION_GLSL_BIT 0x2000000 +#define SPVC_COMPILER_OPTION_HLSL_BIT 0x4000000 +#define SPVC_COMPILER_OPTION_MSL_BIT 0x8000000 +#define SPVC_COMPILER_OPTION_LANG_BITS 0x0f000000 +#define SPVC_COMPILER_OPTION_ENUM_BITS 0xffffff + +#define SPVC_MAKE_MSL_VERSION(major, minor, patch) ((major) * 10000 + (minor) * 100 + (patch)) + +/* Maps to C++ API. */ +typedef enum spvc_msl_platform +{ + SPVC_MSL_PLATFORM_IOS = 0, + SPVC_MSL_PLATFORM_MACOS = 1, + SPVC_MSL_PLATFORM_MAX_INT = 0x7fffffff +} spvc_msl_platform; + +/* Maps to C++ API. */ +typedef enum spvc_msl_index_type +{ + SPVC_MSL_INDEX_TYPE_NONE = 0, + SPVC_MSL_INDEX_TYPE_UINT16 = 1, + SPVC_MSL_INDEX_TYPE_UINT32 = 2, + SPVC_MSL_INDEX_TYPE_MAX_INT = 0x7fffffff +} spvc_msl_index_type; + +/* Maps to C++ API. */ +typedef enum spvc_msl_shader_variable_format +{ + SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER = 0, + SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8 = 1, + SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16 = 2, + SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY16 = 3, + SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY32 = 4, + + /* Deprecated names. */ + SPVC_MSL_VERTEX_FORMAT_OTHER = SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER, + SPVC_MSL_VERTEX_FORMAT_UINT8 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8, + SPVC_MSL_VERTEX_FORMAT_UINT16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16, + SPVC_MSL_SHADER_INPUT_FORMAT_OTHER = SPVC_MSL_SHADER_VARIABLE_FORMAT_OTHER, + SPVC_MSL_SHADER_INPUT_FORMAT_UINT8 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT8, + SPVC_MSL_SHADER_INPUT_FORMAT_UINT16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_UINT16, + SPVC_MSL_SHADER_INPUT_FORMAT_ANY16 = SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY16, + SPVC_MSL_SHADER_INPUT_FORMAT_ANY32 = SPVC_MSL_SHADER_VARIABLE_FORMAT_ANY32, + + + SPVC_MSL_SHADER_INPUT_FORMAT_INT_MAX = 0x7fffffff +} spvc_msl_shader_variable_format, spvc_msl_shader_input_format, spvc_msl_vertex_format; + +/* Maps to C++ API. Deprecated; use spvc_msl_shader_interface_var. */ +typedef struct spvc_msl_vertex_attribute +{ + unsigned location; + + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_buffer; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_offset; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + unsigned msl_stride; + /* Obsolete, do not use. Only lingers on for ABI compatibility. */ + spvc_bool per_instance; + + spvc_msl_vertex_format format; + SpvBuiltIn builtin; +} spvc_msl_vertex_attribute; + +/* + * Initializes the vertex attribute struct. + */ +SPVC_PUBLIC_API void spvc_msl_vertex_attribute_init(spvc_msl_vertex_attribute *attr); + +/* Maps to C++ API. Deprecated; use spvc_msl_shader_interface_var_2. */ +typedef struct spvc_msl_shader_interface_var +{ + unsigned location; + spvc_msl_vertex_format format; + SpvBuiltIn builtin; + unsigned vecsize; +} spvc_msl_shader_interface_var, spvc_msl_shader_input; + +/* + * Initializes the shader input struct. + * Deprecated. Use spvc_msl_shader_interface_var_init_2(). + */ +SPVC_PUBLIC_API void spvc_msl_shader_interface_var_init(spvc_msl_shader_interface_var *var); +/* + * Deprecated. Use spvc_msl_shader_interface_var_init_2(). + */ +SPVC_PUBLIC_API void spvc_msl_shader_input_init(spvc_msl_shader_input *input); + +/* Maps to C++ API. */ +typedef enum spvc_msl_shader_variable_rate +{ + SPVC_MSL_SHADER_VARIABLE_RATE_PER_VERTEX = 0, + SPVC_MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE = 1, + SPVC_MSL_SHADER_VARIABLE_RATE_PER_PATCH = 2, + + SPVC_MSL_SHADER_VARIABLE_RATE_INT_MAX = 0x7fffffff, +} spvc_msl_shader_variable_rate; + +/* Maps to C++ API. */ +typedef struct spvc_msl_shader_interface_var_2 +{ + unsigned location; + spvc_msl_shader_variable_format format; + SpvBuiltIn builtin; + unsigned vecsize; + spvc_msl_shader_variable_rate rate; +} spvc_msl_shader_interface_var_2; + +/* + * Initializes the shader interface variable struct. + */ +SPVC_PUBLIC_API void spvc_msl_shader_interface_var_init_2(spvc_msl_shader_interface_var_2 *var); + +/* Maps to C++ API. + * Deprecated. Use spvc_msl_resource_binding_2. */ +typedef struct spvc_msl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + unsigned msl_buffer; + unsigned msl_texture; + unsigned msl_sampler; +} spvc_msl_resource_binding; + +typedef struct spvc_msl_resource_binding_2 +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + unsigned count; + unsigned msl_buffer; + unsigned msl_texture; + unsigned msl_sampler; +} spvc_msl_resource_binding_2; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + * Deprecated: Use spvc_msl_resource_binding_init_2. + */ +SPVC_PUBLIC_API void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding); +SPVC_PUBLIC_API void spvc_msl_resource_binding_init_2(spvc_msl_resource_binding_2 *binding); + +#define SPVC_MSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_MSL_PUSH_CONSTANT_BINDING (0) +#define SPVC_MSL_SWIZZLE_BUFFER_BINDING (~(1u)) +#define SPVC_MSL_BUFFER_SIZE_BUFFER_BINDING (~(2u)) +#define SPVC_MSL_ARGUMENT_BUFFER_BINDING (~(3u)) + +/* Obsolete. Sticks around for backwards compatibility. */ +#define SPVC_MSL_AUX_BUFFER_STRUCT_VERSION 1 + +/* Runtime check for incompatibility. Obsolete. */ +SPVC_PUBLIC_API unsigned spvc_msl_get_aux_buffer_struct_version(void); + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_coord +{ + SPVC_MSL_SAMPLER_COORD_NORMALIZED = 0, + SPVC_MSL_SAMPLER_COORD_PIXEL = 1, + SPVC_MSL_SAMPLER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_coord; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_filter +{ + SPVC_MSL_SAMPLER_FILTER_NEAREST = 0, + SPVC_MSL_SAMPLER_FILTER_LINEAR = 1, + SPVC_MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_filter; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_mip_filter +{ + SPVC_MSL_SAMPLER_MIP_FILTER_NONE = 0, + SPVC_MSL_SAMPLER_MIP_FILTER_NEAREST = 1, + SPVC_MSL_SAMPLER_MIP_FILTER_LINEAR = 2, + SPVC_MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff +} spvc_msl_sampler_mip_filter; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_address +{ + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0, + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1, + SPVC_MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2, + SPVC_MSL_SAMPLER_ADDRESS_REPEAT = 3, + SPVC_MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4, + SPVC_MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff +} spvc_msl_sampler_address; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_compare_func +{ + SPVC_MSL_SAMPLER_COMPARE_FUNC_NEVER = 0, + SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS = 1, + SPVC_MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2, + SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER = 3, + SPVC_MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4, + SPVC_MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5, + SPVC_MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6, + SPVC_MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7, + SPVC_MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff +} spvc_msl_sampler_compare_func; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_border_color +{ + SPVC_MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0, + SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1, + SPVC_MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2, + SPVC_MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff +} spvc_msl_sampler_border_color; + +/* Maps to C++ API. */ +typedef enum spvc_msl_format_resolution +{ + SPVC_MSL_FORMAT_RESOLUTION_444 = 0, + SPVC_MSL_FORMAT_RESOLUTION_422, + SPVC_MSL_FORMAT_RESOLUTION_420, + SPVC_MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff +} spvc_msl_format_resolution; + +/* Maps to C++ API. */ +typedef enum spvc_msl_chroma_location +{ + SPVC_MSL_CHROMA_LOCATION_COSITED_EVEN = 0, + SPVC_MSL_CHROMA_LOCATION_MIDPOINT, + SPVC_MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff +} spvc_msl_chroma_location; + +/* Maps to C++ API. */ +typedef enum spvc_msl_component_swizzle +{ + SPVC_MSL_COMPONENT_SWIZZLE_IDENTITY = 0, + SPVC_MSL_COMPONENT_SWIZZLE_ZERO, + SPVC_MSL_COMPONENT_SWIZZLE_ONE, + SPVC_MSL_COMPONENT_SWIZZLE_R, + SPVC_MSL_COMPONENT_SWIZZLE_G, + SPVC_MSL_COMPONENT_SWIZZLE_B, + SPVC_MSL_COMPONENT_SWIZZLE_A, + SPVC_MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff +} spvc_msl_component_swizzle; + +/* Maps to C++ API. */ +typedef enum spvc_msl_sampler_ycbcr_model_conversion +{ + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020, + SPVC_MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff +} spvc_msl_sampler_ycbcr_model_conversion; + +/* Maps to C+ API. */ +typedef enum spvc_msl_sampler_ycbcr_range +{ + SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, + SPVC_MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW, + SPVC_MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff +} spvc_msl_sampler_ycbcr_range; + +/* Maps to C++ API. */ +typedef struct spvc_msl_constexpr_sampler +{ + spvc_msl_sampler_coord coord; + spvc_msl_sampler_filter min_filter; + spvc_msl_sampler_filter mag_filter; + spvc_msl_sampler_mip_filter mip_filter; + spvc_msl_sampler_address s_address; + spvc_msl_sampler_address t_address; + spvc_msl_sampler_address r_address; + spvc_msl_sampler_compare_func compare_func; + spvc_msl_sampler_border_color border_color; + float lod_clamp_min; + float lod_clamp_max; + int max_anisotropy; + + spvc_bool compare_enable; + spvc_bool lod_clamp_enable; + spvc_bool anisotropy_enable; +} spvc_msl_constexpr_sampler; + +/* + * Initializes the constexpr sampler struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler); + +/* Maps to the sampler Y'CbCr conversion-related portions of MSLConstexprSampler. See C++ API for defaults and details. */ +typedef struct spvc_msl_sampler_ycbcr_conversion +{ + unsigned planes; + spvc_msl_format_resolution resolution; + spvc_msl_sampler_filter chroma_filter; + spvc_msl_chroma_location x_chroma_offset; + spvc_msl_chroma_location y_chroma_offset; + spvc_msl_component_swizzle swizzle[4]; + spvc_msl_sampler_ycbcr_model_conversion ycbcr_model; + spvc_msl_sampler_ycbcr_range ycbcr_range; + unsigned bpc; +} spvc_msl_sampler_ycbcr_conversion; + +/* + * Initializes the constexpr sampler struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycbcr_conversion *conv); + +/* Maps to C++ API. */ +typedef enum spvc_hlsl_binding_flag_bits +{ + SPVC_HLSL_BINDING_AUTO_NONE_BIT = 0, + SPVC_HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, + SPVC_HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, + SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, + SPVC_HLSL_BINDING_AUTO_UAV_BIT = 1 << 3, + SPVC_HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4, + SPVC_HLSL_BINDING_AUTO_ALL = 0x7fffffff +} spvc_hlsl_binding_flag_bits; +typedef unsigned spvc_hlsl_binding_flags; + +#define SPVC_HLSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_HLSL_PUSH_CONSTANT_BINDING (0) + +/* Maps to C++ API. */ +typedef struct spvc_hlsl_resource_binding_mapping +{ + unsigned register_space; + unsigned register_binding; +} spvc_hlsl_resource_binding_mapping; + +typedef struct spvc_hlsl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + + spvc_hlsl_resource_binding_mapping cbv, uav, srv, sampler; +} spvc_hlsl_resource_binding; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding); + +/* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */ +typedef enum spvc_compiler_option +{ + SPVC_COMPILER_OPTION_UNKNOWN = 0, + + SPVC_COMPILER_OPTION_FORCE_TEMPORARY = 1 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FLATTEN_MULTIDIMENSIONAL_ARRAYS = 2 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FIXUP_DEPTH_CONVENTION = 3 | SPVC_COMPILER_OPTION_COMMON_BIT, + SPVC_COMPILER_OPTION_FLIP_VERTEX_Y = 4 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_GLSL_SUPPORT_NONZERO_BASE_INSTANCE = 5 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_SEPARATE_SHADER_OBJECTS = 6 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ENABLE_420PACK_EXTENSION = 7 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_VERSION = 8 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES = 9 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS = 10 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_FLOAT_PRECISION_HIGHP = 11 | SPVC_COMPILER_OPTION_GLSL_BIT, + SPVC_COMPILER_OPTION_GLSL_ES_DEFAULT_INT_PRECISION_HIGHP = 12 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL = 13 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_POINT_SIZE_COMPAT = 14 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_POINT_COORD_COMPAT = 15 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_SUPPORT_NONZERO_BASE_VERTEX_BASE_INSTANCE = 16 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_VERSION = 17 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TEXEL_BUFFER_TEXTURE_WIDTH = 18 | SPVC_COMPILER_OPTION_MSL_BIT, + + /* Obsolete, use SWIZZLE_BUFFER_INDEX instead. */ + SPVC_COMPILER_OPTION_MSL_AUX_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SWIZZLE_BUFFER_INDEX = 19 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_MSL_INDIRECT_PARAMS_BUFFER_INDEX = 20 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_OUTPUT_BUFFER_INDEX = 21 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_OUTPUT_BUFFER_INDEX = 22 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_TESS_FACTOR_OUTPUT_BUFFER_INDEX = 23 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_WORKGROUP_INDEX = 24 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_BUILTIN = 25 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DISABLE_RASTERIZATION = 26 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_CAPTURE_OUTPUT_TO_BUFFER = 27 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SWIZZLE_TEXTURE_SAMPLES = 28 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_PAD_FRAGMENT_OUTPUT_COMPONENTS = 29 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TESS_DOMAIN_ORIGIN_LOWER_LEFT = 30 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_PLATFORM = 31 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS = 32 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_EMIT_PUSH_CONSTANT_AS_UNIFORM_BUFFER = 33 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_TEXTURE_BUFFER_NATIVE = 34 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS = 35 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_BUFFER_SIZE_BUFFER_INDEX = 36 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_EMIT_LINE_DIRECTIVES = 37 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTIVIEW = 38 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VIEW_MASK_BUFFER_INDEX = 39 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DEVICE_INDEX = 40 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VIEW_INDEX_FROM_DEVICE_INDEX = 41 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DISPATCH_BASE = 42 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_DYNAMIC_OFFSETS_BUFFER_INDEX = 43 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_TEXTURE_1D_AS_2D = 44 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_BASE_INDEX_ZERO = 45 | SPVC_COMPILER_OPTION_MSL_BIT, + + /* Obsolete. Use MSL_FRAMEBUFFER_FETCH_SUBPASS instead. */ + SPVC_COMPILER_OPTION_MSL_IOS_FRAMEBUFFER_FETCH_SUBPASS = 46 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FRAMEBUFFER_FETCH_SUBPASS = 46 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_MSL_INVARIANT_FP_MATH = 47 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_EMULATE_CUBEMAP_ARRAY = 48 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_DECORATION_BINDING = 49 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_ACTIVE_ARGUMENT_BUFFER_RESOURCES = 50 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_NATIVE_ARRAYS = 51 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_ENABLE_STORAGE_IMAGE_QUALIFIER_DEDUCTION = 52 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_FORCE_STORAGE_BUFFER_AS_UAV = 53 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_FORCE_ZERO_INITIALIZED_VARIABLES = 54 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_HLSL_NONWRITABLE_UAV_TEXTURE_AS_SRV = 55 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_OUTPUT_MASK = 56 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_DEPTH_BUILTIN = 57 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_FRAG_STENCIL_REF_BUILTIN = 58 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ENABLE_CLIP_DISTANCE_USER_VARYING = 59 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_ENABLE_16BIT_TYPES = 60 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTI_PATCH_WORKGROUP = 61 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INPUT_BUFFER_INDEX = 62 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_INDEX_BUFFER_INDEX = 63 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VERTEX_FOR_TESSELLATION = 64 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_VERTEX_INDEX_TYPE = 65 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS = 66 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_MULTIVIEW_LAYERED_RENDERING = 67 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_ARRAYED_SUBPASS_INPUT = 68 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_R32UI_LINEAR_TEXTURE_ALIGNMENT = 69 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_R32UI_ALIGNMENT_CONSTANT_ID = 70 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_FLATTEN_MATRIX_VERTEX_INPUT_SEMANTICS = 71 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_IOS_USE_SIMDGROUP_FUNCTIONS = 72 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_EMULATE_SUBGROUPS = 73 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FIXED_SUBGROUP_SIZE = 74 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING = 75 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE = 76 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT = 77 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_RELAX_NAN_CHECKS = 78 | SPVC_COMPILER_OPTION_COMMON_BIT, + + SPVC_COMPILER_OPTION_MSL_RAW_BUFFER_TESE_INPUT = 79 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_INPUT_BUFFER_INDEX = 80 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_MANUAL_HELPER_INVOCATION_UPDATES = 81 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_CHECK_DISCARDED_FRAG_STORES = 82 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_GLSL_ENABLE_ROW_MAJOR_LOAD_WORKAROUND = 83 | SPVC_COMPILER_OPTION_GLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_ARGUMENT_BUFFERS_TIER = 84 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_SAMPLE_DREF_LOD_ARRAY_AS_GRAD = 85 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_READWRITE_TEXTURE_FENCES = 86 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_REPLACE_RECURSIVE_INPUTS = 87 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_AGX_MANUAL_CUBE_GRAD_FIXUP = 88 | SPVC_COMPILER_OPTION_MSL_BIT, + SPVC_COMPILER_OPTION_MSL_FORCE_FRAGMENT_WITH_SIDE_EFFECTS_EXECUTION = 89 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_USE_ENTRY_POINT_NAME = 90 | SPVC_COMPILER_OPTION_HLSL_BIT, + SPVC_COMPILER_OPTION_HLSL_PRESERVE_STRUCTURED_BUFFERS = 91 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_MSL_AUTO_DISABLE_RASTERIZATION = 92 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_MSL_ENABLE_POINT_SIZE_DEFAULT = 93 | SPVC_COMPILER_OPTION_MSL_BIT, + + SPVC_COMPILER_OPTION_HLSL_USER_SEMANTIC = 94 | SPVC_COMPILER_OPTION_HLSL_BIT, + + SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff +} spvc_compiler_option; + +/* + * Context is the highest-level API construct. + * The context owns all memory allocations made by its child object hierarchy, including various non-opaque structs and strings. + * This means that the API user only has to care about one "destroy" call ever when using the C API. + * All pointers handed out by the APIs are only valid as long as the context + * is alive and spvc_context_release_allocations has not been called. + */ +SPVC_PUBLIC_API spvc_result spvc_context_create(spvc_context *context); + +/* Frees all memory allocations and objects associated with the context and its child objects. */ +SPVC_PUBLIC_API void spvc_context_destroy(spvc_context context); + +/* Frees all memory allocations and objects associated with the context and its child objects, but keeps the context alive. */ +SPVC_PUBLIC_API void spvc_context_release_allocations(spvc_context context); + +/* Get the string for the last error which was logged. */ +SPVC_PUBLIC_API const char *spvc_context_get_last_error_string(spvc_context context); + +/* Get notified in a callback when an error triggers. Useful for debugging. */ +typedef void (*spvc_error_callback)(void *userdata, const char *error); +SPVC_PUBLIC_API void spvc_context_set_error_callback(spvc_context context, spvc_error_callback cb, void *userdata); + +/* SPIR-V parsing interface. Maps to Parser which then creates a ParsedIR, and that IR is extracted into the handle. */ +SPVC_PUBLIC_API spvc_result spvc_context_parse_spirv(spvc_context context, const SpvId *spirv, size_t word_count, + spvc_parsed_ir *parsed_ir); + +/* + * Create a compiler backend. Capture mode controls if we construct by copy or move semantics. + * It is always recommended to use SPVC_CAPTURE_MODE_TAKE_OWNERSHIP if you only intend to cross-compile the IR once. + */ +SPVC_PUBLIC_API spvc_result spvc_context_create_compiler(spvc_context context, spvc_backend backend, + spvc_parsed_ir parsed_ir, spvc_capture_mode mode, + spvc_compiler *compiler); + +/* Maps directly to C++ API. */ +SPVC_PUBLIC_API unsigned spvc_compiler_get_current_id_bound(spvc_compiler compiler); + +/* Create compiler options, which will initialize defaults. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_create_compiler_options(spvc_compiler compiler, + spvc_compiler_options *options); +/* Override options. Will return error if e.g. MSL options are used for the HLSL backend, etc. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_bool(spvc_compiler_options options, + spvc_compiler_option option, spvc_bool value); +SPVC_PUBLIC_API spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, + spvc_compiler_option option, unsigned value); +/* Set compiler options. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_install_compiler_options(spvc_compiler compiler, + spvc_compiler_options options); + +/* Compile IR into a string. *source is owned by the context, and caller must not free it themselves. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source); + +/* Maps to C++ API. */ +SPVC_PUBLIC_API spvc_result spvc_compiler_add_header_line(spvc_compiler compiler, const char *line); +SPVC_PUBLIC_API spvc_result spvc_compiler_require_extension(spvc_compiler compiler, const char *ext); +SPVC_PUBLIC_API size_t spvc_compiler_get_num_required_extensions(spvc_compiler compiler); +SPVC_PUBLIC_API const char *spvc_compiler_get_required_extension(spvc_compiler compiler, size_t index); +SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_location(spvc_compiler compiler, + unsigned location, unsigned component); +SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_builtin(spvc_compiler compiler, SpvBuiltIn builtin); + +/* + * HLSL specifics. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler, + const spvc_hlsl_root_constants *constant_info, + size_t count); +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_vertex_attribute_remap(spvc_compiler compiler, + const spvc_hlsl_vertex_attribute_remap *remap, + size_t remaps); +SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin(spvc_compiler compiler); + +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, + spvc_hlsl_binding_flags flags); + +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding); +SPVC_PUBLIC_API spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); + +/* + * MSL specifics. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler); + +/* Obsolete. Renamed to needs_swizzle_buffer. */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_aux_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_swizzle_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_buffer_size_buffer(spvc_compiler compiler); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_output_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_patch_output_buffer(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_needs_input_threadgroup_mem(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_vertex_attribute(spvc_compiler compiler, + const spvc_msl_vertex_attribute *attrs); +/* Deprecated; use spvc_compiler_msl_add_resource_binding_2(). */ +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding(spvc_compiler compiler, + const spvc_msl_resource_binding *binding); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_resource_binding_2(spvc_compiler compiler, + const spvc_msl_resource_binding_2 *binding); +/* Deprecated; use spvc_compiler_msl_add_shader_input_2(). */ +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input(spvc_compiler compiler, + const spvc_msl_shader_interface_var *input); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_input_2(spvc_compiler compiler, + const spvc_msl_shader_interface_var_2 *input); +/* Deprecated; use spvc_compiler_msl_add_shader_output_2(). */ +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_output(spvc_compiler compiler, + const spvc_msl_shader_interface_var *output); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_shader_output_2(spvc_compiler compiler, + const spvc_msl_shader_interface_var_2 *output); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_discrete_descriptor_set(spvc_compiler compiler, unsigned desc_set); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_argument_buffer_device_address_space(spvc_compiler compiler, unsigned desc_set, spvc_bool device_address); + +/* Obsolete, use is_shader_input_used. */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_vertex_attribute_used(spvc_compiler compiler, unsigned location); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_shader_input_used(spvc_compiler compiler, unsigned location); +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_shader_output_used(spvc_compiler compiler, unsigned location); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_msl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_ycbcr(spvc_compiler compiler, spvc_variable_id id, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_remap_constexpr_sampler_by_binding_ycbcr(spvc_compiler compiler, unsigned desc_set, unsigned binding, const spvc_msl_constexpr_sampler *sampler, const spvc_msl_sampler_ycbcr_conversion *conv); +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_fragment_output_components(spvc_compiler compiler, unsigned location, unsigned components); + +SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API unsigned spvc_compiler_msl_get_automatic_resource_binding_secondary(spvc_compiler compiler, spvc_variable_id id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_dynamic_buffer(spvc_compiler compiler, unsigned desc_set, unsigned binding, unsigned index); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_add_inline_uniform_block(spvc_compiler compiler, unsigned desc_set, unsigned binding); + +SPVC_PUBLIC_API spvc_result spvc_compiler_msl_set_combined_sampler_suffix(spvc_compiler compiler, const char *suffix); +SPVC_PUBLIC_API const char *spvc_compiler_msl_get_combined_sampler_suffix(spvc_compiler compiler); + +/* + * Reflect resources. + * Maps almost 1:1 to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_interface_variables(spvc_compiler compiler, spvc_set *set); +SPVC_PUBLIC_API spvc_result spvc_compiler_set_enabled_interface_variables(spvc_compiler compiler, spvc_set set); +SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources(spvc_compiler compiler, spvc_resources *resources); +SPVC_PUBLIC_API spvc_result spvc_compiler_create_shader_resources_for_active_variables(spvc_compiler compiler, + spvc_resources *resources, + spvc_set active); +SPVC_PUBLIC_API spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources, spvc_resource_type type, + const spvc_reflected_resource **resource_list, + size_t *resource_size); + +SPVC_PUBLIC_API spvc_result spvc_resources_get_builtin_resource_list_for_type( + spvc_resources resources, spvc_builtin_resource_type type, + const spvc_reflected_builtin_resource **resource_list, + size_t *resource_size); + +/* + * Decorations. + * Maps to C++ API. + */ +SPVC_PUBLIC_API void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + unsigned argument); +SPVC_PUBLIC_API void spvc_compiler_set_decoration_string(spvc_compiler compiler, SpvId id, SpvDecoration decoration, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_name(spvc_compiler compiler, SpvId id, const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_decoration(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + SpvDecoration decoration, unsigned argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_decoration_string(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_set_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index, + const char *argument); +SPVC_PUBLIC_API void spvc_compiler_unset_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API void spvc_compiler_unset_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_name(spvc_compiler compiler, SpvId id); +SPVC_PUBLIC_API unsigned spvc_compiler_get_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_decoration_string(spvc_compiler compiler, SpvId id, + SpvDecoration decoration); +SPVC_PUBLIC_API unsigned spvc_compiler_get_member_decoration(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_member_decoration_string(spvc_compiler compiler, spvc_type_id id, + unsigned member_index, SpvDecoration decoration); +SPVC_PUBLIC_API const char *spvc_compiler_get_member_name(spvc_compiler compiler, spvc_type_id id, unsigned member_index); + +/* + * Entry points. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler, + const spvc_entry_point **entry_points, + size_t *num_entry_points); +SPVC_PUBLIC_API spvc_result spvc_compiler_set_entry_point(spvc_compiler compiler, const char *name, + SpvExecutionModel model); +SPVC_PUBLIC_API spvc_result spvc_compiler_rename_entry_point(spvc_compiler compiler, const char *old_name, + const char *new_name, SpvExecutionModel model); +SPVC_PUBLIC_API const char *spvc_compiler_get_cleansed_entry_point_name(spvc_compiler compiler, const char *name, + SpvExecutionModel model); +SPVC_PUBLIC_API void spvc_compiler_set_execution_mode(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API void spvc_compiler_unset_execution_mode(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API void spvc_compiler_set_execution_mode_with_arguments(spvc_compiler compiler, SpvExecutionMode mode, + unsigned arg0, unsigned arg1, unsigned arg2); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_execution_modes(spvc_compiler compiler, const SpvExecutionMode **modes, + size_t *num_modes); +SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler compiler, SpvExecutionMode mode); +SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler, + SpvExecutionMode mode, unsigned index); +SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler); +SPVC_PUBLIC_API void spvc_compiler_update_active_builtins(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_bool spvc_compiler_has_active_builtin(spvc_compiler compiler, SpvBuiltIn builtin, SpvStorageClass storage); + +/* + * Type query interface. + * Maps to C++ API, except it's read-only. + */ +SPVC_PUBLIC_API spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id); + +/* Pulls out SPIRType::self. This effectively gives the type ID without array or pointer qualifiers. + * This is necessary when reflecting decoration/name information on members of a struct, + * which are placed in the base type, not the qualified type. + * This is similar to spvc_reflected_resource::base_type_id. */ +SPVC_PUBLIC_API spvc_type_id spvc_type_get_base_type_id(spvc_type type); + +SPVC_PUBLIC_API spvc_basetype spvc_type_get_basetype(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_bit_width(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_vector_size(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_columns(spvc_type type); +SPVC_PUBLIC_API unsigned spvc_type_get_num_array_dimensions(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_array_dimension_is_literal(spvc_type type, unsigned dimension); +SPVC_PUBLIC_API SpvId spvc_type_get_array_dimension(spvc_type type, unsigned dimension); +SPVC_PUBLIC_API unsigned spvc_type_get_num_member_types(spvc_type type); +SPVC_PUBLIC_API spvc_type_id spvc_type_get_member_type(spvc_type type, unsigned index); +SPVC_PUBLIC_API SpvStorageClass spvc_type_get_storage_class(spvc_type type); + +/* Image type query. */ +SPVC_PUBLIC_API spvc_type_id spvc_type_get_image_sampled_type(spvc_type type); +SPVC_PUBLIC_API SpvDim spvc_type_get_image_dimension(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_depth(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_arrayed(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_multisampled(spvc_type type); +SPVC_PUBLIC_API spvc_bool spvc_type_get_image_is_storage(spvc_type type); +SPVC_PUBLIC_API SpvImageFormat spvc_type_get_image_storage_format(spvc_type type); +SPVC_PUBLIC_API SpvAccessQualifier spvc_type_get_image_access_qualifier(spvc_type type); + +/* + * Buffer layout query. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size(spvc_compiler compiler, spvc_type struct_type, size_t *size); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_size_runtime_array(spvc_compiler compiler, + spvc_type struct_type, size_t array_size, size_t *size); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_struct_member_size(spvc_compiler compiler, spvc_type type, unsigned index, size_t *size); + +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_offset(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *offset); +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_array_stride(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *stride); +SPVC_PUBLIC_API spvc_result spvc_compiler_type_struct_member_matrix_stride(spvc_compiler compiler, + spvc_type type, unsigned index, unsigned *stride); + +/* + * Workaround helper functions. + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_build_dummy_sampler_for_combined_images(spvc_compiler compiler, spvc_variable_id *id); +SPVC_PUBLIC_API spvc_result spvc_compiler_build_combined_image_samplers(spvc_compiler compiler); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler, + const spvc_combined_image_sampler **samplers, + size_t *num_samplers); + +/* + * Constants + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler, + const spvc_specialization_constant **constants, + size_t *num_constants); +SPVC_PUBLIC_API spvc_constant spvc_compiler_get_constant_handle(spvc_compiler compiler, + spvc_constant_id id); + +SPVC_PUBLIC_API spvc_constant_id spvc_compiler_get_work_group_size_specialization_constants(spvc_compiler compiler, + spvc_specialization_constant *x, + spvc_specialization_constant *y, + spvc_specialization_constant *z); + +/* + * Buffer ranges + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_result spvc_compiler_get_active_buffer_ranges(spvc_compiler compiler, + spvc_variable_id id, + const spvc_buffer_range **ranges, + size_t *num_ranges); + +/* + * No stdint.h until C99, sigh :( + * For smaller types, the result is sign or zero-extended as appropriate. + * Maps to C++ API. + * TODO: The SPIRConstant query interface and modification interface is not quite complete. + */ +SPVC_PUBLIC_API float spvc_constant_get_scalar_fp16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API float spvc_constant_get_scalar_fp32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API double spvc_constant_get_scalar_fp64(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i32(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i16(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API unsigned spvc_constant_get_scalar_u8(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API int spvc_constant_get_scalar_i8(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API void spvc_constant_get_subconstants(spvc_constant constant, const spvc_constant_id **constituents, size_t *count); +SPVC_PUBLIC_API unsigned long long spvc_constant_get_scalar_u64(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API long long spvc_constant_get_scalar_i64(spvc_constant constant, unsigned column, unsigned row); +SPVC_PUBLIC_API spvc_type_id spvc_constant_get_type(spvc_constant constant); + +/* + * C implementation of the C++ api. + */ +SPVC_PUBLIC_API void spvc_constant_set_scalar_fp16(spvc_constant constant, unsigned column, unsigned row, unsigned short value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_fp32(spvc_constant constant, unsigned column, unsigned row, float value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_fp64(spvc_constant constant, unsigned column, unsigned row, double value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u32(spvc_constant constant, unsigned column, unsigned row, unsigned value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i32(spvc_constant constant, unsigned column, unsigned row, int value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u64(spvc_constant constant, unsigned column, unsigned row, unsigned long long value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i64(spvc_constant constant, unsigned column, unsigned row, long long value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u16(spvc_constant constant, unsigned column, unsigned row, unsigned short value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i16(spvc_constant constant, unsigned column, unsigned row, signed short value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_u8(spvc_constant constant, unsigned column, unsigned row, unsigned char value); +SPVC_PUBLIC_API void spvc_constant_set_scalar_i8(spvc_constant constant, unsigned column, unsigned row, signed char value); + +/* + * Misc reflection + * Maps to C++ API. + */ +SPVC_PUBLIC_API spvc_bool spvc_compiler_get_binary_offset_for_decoration(spvc_compiler compiler, + spvc_variable_id id, + SpvDecoration decoration, + unsigned *word_offset); + +SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_is_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API spvc_bool spvc_compiler_buffer_get_hlsl_counter_buffer(spvc_compiler compiler, spvc_variable_id id, + spvc_variable_id *counter_id); + +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_capabilities(spvc_compiler compiler, + const SpvCapability **capabilities, + size_t *num_capabilities); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const char ***extensions, + size_t *num_extensions); + +SPVC_PUBLIC_API const char *spvc_compiler_get_remapped_declared_block_name(spvc_compiler compiler, spvc_variable_id id); +SPVC_PUBLIC_API spvc_result spvc_compiler_get_buffer_block_decorations(spvc_compiler compiler, spvc_variable_id id, + const SpvDecoration **decorations, + size_t *num_decorations); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cross_containers.hpp b/thirdparty/SPIRV-Cross/spirv_cross_containers.hpp new file mode 100644 index 000000000..c496cb75b --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_containers.hpp @@ -0,0 +1,756 @@ +/* + * Copyright 2019-2021 Hans-Kristian Arntzen + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_CONTAINERS_HPP +#define SPIRV_CROSS_CONTAINERS_HPP + +#include "spirv_cross_error_handling.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE +#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE +#else +#define SPIRV_CROSS_NAMESPACE spirv_cross +#endif + +namespace SPIRV_CROSS_NAMESPACE +{ +#ifndef SPIRV_CROSS_FORCE_STL_TYPES +// std::aligned_storage does not support size == 0, so roll our own. +template +class AlignedBuffer +{ +public: + T *data() + { +#if defined(_MSC_VER) && _MSC_VER < 1900 + // MSVC 2013 workarounds, sigh ... + // Only use this workaround on MSVC 2013 due to some confusion around default initialized unions. + // Spec seems to suggest the memory will be zero-initialized, which is *not* what we want. + return reinterpret_cast(u.aligned_char); +#else + return reinterpret_cast(aligned_char); +#endif + } + +private: +#if defined(_MSC_VER) && _MSC_VER < 1900 + // MSVC 2013 workarounds, sigh ... + union + { + char aligned_char[sizeof(T) * N]; + double dummy_aligner; + } u; +#else + alignas(T) char aligned_char[sizeof(T) * N]; +#endif +}; + +template +class AlignedBuffer +{ +public: + T *data() + { + return nullptr; + } +}; + +// An immutable version of SmallVector which erases type information about storage. +template +class VectorView +{ +public: + T &operator[](size_t i) SPIRV_CROSS_NOEXCEPT + { + return ptr[i]; + } + + const T &operator[](size_t i) const SPIRV_CROSS_NOEXCEPT + { + return ptr[i]; + } + + bool empty() const SPIRV_CROSS_NOEXCEPT + { + return buffer_size == 0; + } + + size_t size() const SPIRV_CROSS_NOEXCEPT + { + return buffer_size; + } + + T *data() SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + const T *data() const SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + T *begin() SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + T *end() SPIRV_CROSS_NOEXCEPT + { + return ptr + buffer_size; + } + + const T *begin() const SPIRV_CROSS_NOEXCEPT + { + return ptr; + } + + const T *end() const SPIRV_CROSS_NOEXCEPT + { + return ptr + buffer_size; + } + + T &front() SPIRV_CROSS_NOEXCEPT + { + return ptr[0]; + } + + const T &front() const SPIRV_CROSS_NOEXCEPT + { + return ptr[0]; + } + + T &back() SPIRV_CROSS_NOEXCEPT + { + return ptr[buffer_size - 1]; + } + + const T &back() const SPIRV_CROSS_NOEXCEPT + { + return ptr[buffer_size - 1]; + } + + // Makes it easier to consume SmallVector. +#if defined(_MSC_VER) && _MSC_VER < 1900 + explicit operator std::vector() const + { + // Another MSVC 2013 workaround. It does not understand lvalue/rvalue qualified operations. + return std::vector(ptr, ptr + buffer_size); + } +#else + // Makes it easier to consume SmallVector. + explicit operator std::vector() const & + { + return std::vector(ptr, ptr + buffer_size); + } + + // If we are converting as an r-value, we can pilfer our elements. + explicit operator std::vector() && + { + return std::vector(std::make_move_iterator(ptr), std::make_move_iterator(ptr + buffer_size)); + } +#endif + + // Avoid sliced copies. Base class should only be read as a reference. + VectorView(const VectorView &) = delete; + void operator=(const VectorView &) = delete; + +protected: + VectorView() = default; + T *ptr = nullptr; + size_t buffer_size = 0; +}; + +// Simple vector which supports up to N elements inline, without malloc/free. +// We use a lot of throwaway vectors all over the place which triggers allocations. +// This class only implements the subset of std::vector we need in SPIRV-Cross. +// It is *NOT* a drop-in replacement in general projects. +template +class SmallVector : public VectorView +{ +public: + SmallVector() SPIRV_CROSS_NOEXCEPT + { + this->ptr = stack_storage.data(); + buffer_capacity = N; + } + + template + SmallVector(const U *arg_list_begin, const U *arg_list_end) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + auto count = size_t(arg_list_end - arg_list_begin); + reserve(count); + for (size_t i = 0; i < count; i++, arg_list_begin++) + new (&this->ptr[i]) T(*arg_list_begin); + this->buffer_size = count; + } + + template + SmallVector(std::initializer_list init) SPIRV_CROSS_NOEXCEPT : SmallVector(init.begin(), init.end()) + { + } + + template + explicit SmallVector(const U (&init)[M]) SPIRV_CROSS_NOEXCEPT : SmallVector(init, init + M) + { + } + + SmallVector(SmallVector &&other) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + *this = std::move(other); + } + + SmallVector &operator=(SmallVector &&other) SPIRV_CROSS_NOEXCEPT + { + clear(); + if (other.ptr != other.stack_storage.data()) + { + // Pilfer allocated pointer. + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = other.ptr; + this->buffer_size = other.buffer_size; + buffer_capacity = other.buffer_capacity; + other.ptr = nullptr; + other.buffer_size = 0; + other.buffer_capacity = 0; + } + else + { + // Need to move the stack contents individually. + reserve(other.buffer_size); + for (size_t i = 0; i < other.buffer_size; i++) + { + new (&this->ptr[i]) T(std::move(other.ptr[i])); + other.ptr[i].~T(); + } + this->buffer_size = other.buffer_size; + other.buffer_size = 0; + } + return *this; + } + + SmallVector(const SmallVector &other) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + *this = other; + } + + SmallVector &operator=(const SmallVector &other) SPIRV_CROSS_NOEXCEPT + { + if (this == &other) + return *this; + + clear(); + reserve(other.buffer_size); + for (size_t i = 0; i < other.buffer_size; i++) + new (&this->ptr[i]) T(other.ptr[i]); + this->buffer_size = other.buffer_size; + return *this; + } + + explicit SmallVector(size_t count) SPIRV_CROSS_NOEXCEPT : SmallVector() + { + resize(count); + } + + ~SmallVector() + { + clear(); + if (this->ptr != stack_storage.data()) + free(this->ptr); + } + + void clear() SPIRV_CROSS_NOEXCEPT + { + for (size_t i = 0; i < this->buffer_size; i++) + this->ptr[i].~T(); + this->buffer_size = 0; + } + + void push_back(const T &t) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(t); + this->buffer_size++; + } + + void push_back(T &&t) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(std::move(t)); + this->buffer_size++; + } + + void pop_back() SPIRV_CROSS_NOEXCEPT + { + // Work around false positive warning on GCC 8.3. + // Calling pop_back on empty vector is undefined. + if (!this->empty()) + resize(this->buffer_size - 1); + } + + template + void emplace_back(Ts &&... ts) SPIRV_CROSS_NOEXCEPT + { + reserve(this->buffer_size + 1); + new (&this->ptr[this->buffer_size]) T(std::forward(ts)...); + this->buffer_size++; + } + + void reserve(size_t count) SPIRV_CROSS_NOEXCEPT + { + if ((count > (std::numeric_limits::max)() / sizeof(T)) || + (count > (std::numeric_limits::max)() / 2)) + { + // Only way this should ever happen is with garbage input, terminate. + std::terminate(); + } + + if (count > buffer_capacity) + { + size_t target_capacity = buffer_capacity; + if (target_capacity == 0) + target_capacity = 1; + + // Weird parens works around macro issues on Windows if NOMINMAX is not used. + target_capacity = (std::max)(target_capacity, N); + + // Need to ensure there is a POT value of target capacity which is larger than count, + // otherwise this will overflow. + while (target_capacity < count) + target_capacity <<= 1u; + + T *new_buffer = + target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. + if (!new_buffer) + std::terminate(); + + // In case for some reason two allocations both come from same stack. + if (new_buffer != this->ptr) + { + // We don't deal with types which can throw in move constructor. + for (size_t i = 0; i < this->buffer_size; i++) + { + new (&new_buffer[i]) T(std::move(this->ptr[i])); + this->ptr[i].~T(); + } + } + + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = new_buffer; + buffer_capacity = target_capacity; + } + } + + void insert(T *itr, const T *insert_begin, const T *insert_end) SPIRV_CROSS_NOEXCEPT + { + auto count = size_t(insert_end - insert_begin); + if (itr == this->end()) + { + reserve(this->buffer_size + count); + for (size_t i = 0; i < count; i++, insert_begin++) + new (&this->ptr[this->buffer_size + i]) T(*insert_begin); + this->buffer_size += count; + } + else + { + if (this->buffer_size + count > buffer_capacity) + { + auto target_capacity = this->buffer_size + count; + if (target_capacity == 0) + target_capacity = 1; + if (target_capacity < N) + target_capacity = N; + + while (target_capacity < count) + target_capacity <<= 1u; + + // Need to allocate new buffer. Move everything to a new buffer. + T *new_buffer = + target_capacity > N ? static_cast(malloc(target_capacity * sizeof(T))) : stack_storage.data(); + + // If we actually fail this malloc, we are hosed anyways, there is no reason to attempt recovery. + if (!new_buffer) + std::terminate(); + + // First, move elements from source buffer to new buffer. + // We don't deal with types which can throw in move constructor. + auto *target_itr = new_buffer; + auto *original_source_itr = this->begin(); + + if (new_buffer != this->ptr) + { + while (original_source_itr != itr) + { + new (target_itr) T(std::move(*original_source_itr)); + original_source_itr->~T(); + ++original_source_itr; + ++target_itr; + } + } + + // Copy-construct new elements. + for (auto *source_itr = insert_begin; source_itr != insert_end; ++source_itr, ++target_itr) + new (target_itr) T(*source_itr); + + // Move over the other half. + if (new_buffer != this->ptr || insert_begin != insert_end) + { + while (original_source_itr != this->end()) + { + new (target_itr) T(std::move(*original_source_itr)); + original_source_itr->~T(); + ++original_source_itr; + ++target_itr; + } + } + + if (this->ptr != stack_storage.data()) + free(this->ptr); + this->ptr = new_buffer; + buffer_capacity = target_capacity; + } + else + { + // Move in place, need to be a bit careful about which elements are constructed and which are not. + // Move the end and construct the new elements. + auto *target_itr = this->end() + count; + auto *source_itr = this->end(); + while (target_itr != this->end() && source_itr != itr) + { + --target_itr; + --source_itr; + new (target_itr) T(std::move(*source_itr)); + } + + // For already constructed elements we can move-assign. + std::move_backward(itr, source_itr, target_itr); + + // For the inserts which go to already constructed elements, we can do a plain copy. + while (itr != this->end() && insert_begin != insert_end) + *itr++ = *insert_begin++; + + // For inserts into newly allocated memory, we must copy-construct instead. + while (insert_begin != insert_end) + { + new (itr) T(*insert_begin); + ++itr; + ++insert_begin; + } + } + + this->buffer_size += count; + } + } + + void insert(T *itr, const T &value) SPIRV_CROSS_NOEXCEPT + { + insert(itr, &value, &value + 1); + } + + T *erase(T *itr) SPIRV_CROSS_NOEXCEPT + { + std::move(itr + 1, this->end(), itr); + this->ptr[--this->buffer_size].~T(); + return itr; + } + + void erase(T *start_erase, T *end_erase) SPIRV_CROSS_NOEXCEPT + { + if (end_erase == this->end()) + { + resize(size_t(start_erase - this->begin())); + } + else + { + auto new_size = this->buffer_size - (end_erase - start_erase); + std::move(end_erase, this->end(), start_erase); + resize(new_size); + } + } + + void resize(size_t new_size) SPIRV_CROSS_NOEXCEPT + { + if (new_size < this->buffer_size) + { + for (size_t i = new_size; i < this->buffer_size; i++) + this->ptr[i].~T(); + } + else if (new_size > this->buffer_size) + { + reserve(new_size); + for (size_t i = this->buffer_size; i < new_size; i++) + new (&this->ptr[i]) T(); + } + + this->buffer_size = new_size; + } + +private: + size_t buffer_capacity = 0; + AlignedBuffer stack_storage; +}; + +// A vector without stack storage. +// Could also be a typedef-ed to std::vector, +// but might as well use the one we have. +template +using Vector = SmallVector; + +#else // SPIRV_CROSS_FORCE_STL_TYPES + +template +using SmallVector = std::vector; +template +using Vector = std::vector; +template +using VectorView = std::vector; + +#endif // SPIRV_CROSS_FORCE_STL_TYPES + +// An object pool which we use for allocating IVariant-derived objects. +// We know we are going to allocate a bunch of objects of each type, +// so amortize the mallocs. +class ObjectPoolBase +{ +public: + virtual ~ObjectPoolBase() = default; + virtual void deallocate_opaque(void *ptr) = 0; +}; + +template +class ObjectPool : public ObjectPoolBase +{ +public: + explicit ObjectPool(unsigned start_object_count_ = 16) + : start_object_count(start_object_count_) + { + } + + template + T *allocate(P &&... p) + { + if (vacants.empty()) + { + unsigned num_objects = start_object_count << memory.size(); + T *ptr = static_cast(malloc(num_objects * sizeof(T))); + if (!ptr) + return nullptr; + + vacants.reserve(num_objects); + for (unsigned i = 0; i < num_objects; i++) + vacants.push_back(&ptr[i]); + + memory.emplace_back(ptr); + } + + T *ptr = vacants.back(); + vacants.pop_back(); + new (ptr) T(std::forward

(p)...); + return ptr; + } + + void deallocate(T *ptr) + { + ptr->~T(); + vacants.push_back(ptr); + } + + void deallocate_opaque(void *ptr) override + { + deallocate(static_cast(ptr)); + } + + void clear() + { + vacants.clear(); + memory.clear(); + } + +protected: + Vector vacants; + + struct MallocDeleter + { + void operator()(T *ptr) + { + ::free(ptr); + } + }; + + SmallVector> memory; + unsigned start_object_count; +}; + +template +class StringStream +{ +public: + StringStream() + { + reset(); + } + + ~StringStream() + { + reset(); + } + + // Disable copies and moves. Makes it easier to implement, and we don't need it. + StringStream(const StringStream &) = delete; + void operator=(const StringStream &) = delete; + + template ::value, int>::type = 0> + StringStream &operator<<(const T &t) + { + auto s = std::to_string(t); + append(s.data(), s.size()); + return *this; + } + + // Only overload this to make float/double conversions ambiguous. + StringStream &operator<<(uint32_t v) + { + auto s = std::to_string(v); + append(s.data(), s.size()); + return *this; + } + + StringStream &operator<<(char c) + { + append(&c, 1); + return *this; + } + + StringStream &operator<<(const std::string &s) + { + append(s.data(), s.size()); + return *this; + } + + StringStream &operator<<(const char *s) + { + append(s, strlen(s)); + return *this; + } + + template + StringStream &operator<<(const char (&s)[N]) + { + append(s, strlen(s)); + return *this; + } + + std::string str() const + { + std::string ret; + size_t target_size = 0; + for (auto &saved : saved_buffers) + target_size += saved.offset; + target_size += current_buffer.offset; + ret.reserve(target_size); + + for (auto &saved : saved_buffers) + ret.insert(ret.end(), saved.buffer, saved.buffer + saved.offset); + ret.insert(ret.end(), current_buffer.buffer, current_buffer.buffer + current_buffer.offset); + return ret; + } + + void reset() + { + for (auto &saved : saved_buffers) + if (saved.buffer != stack_buffer) + free(saved.buffer); + if (current_buffer.buffer != stack_buffer) + free(current_buffer.buffer); + + saved_buffers.clear(); + current_buffer.buffer = stack_buffer; + current_buffer.offset = 0; + current_buffer.size = sizeof(stack_buffer); + } + +private: + struct Buffer + { + char *buffer = nullptr; + size_t offset = 0; + size_t size = 0; + }; + Buffer current_buffer; + char stack_buffer[StackSize]; + SmallVector saved_buffers; + + void append(const char *s, size_t len) + { + size_t avail = current_buffer.size - current_buffer.offset; + if (avail < len) + { + if (avail > 0) + { + memcpy(current_buffer.buffer + current_buffer.offset, s, avail); + s += avail; + len -= avail; + current_buffer.offset += avail; + } + + saved_buffers.push_back(current_buffer); + size_t target_size = len > BlockSize ? len : BlockSize; + current_buffer.buffer = static_cast(malloc(target_size)); + if (!current_buffer.buffer) + SPIRV_CROSS_THROW("Out of memory."); + + memcpy(current_buffer.buffer, s, len); + current_buffer.offset = len; + current_buffer.size = target_size; + } + else + { + memcpy(current_buffer.buffer + current_buffer.offset, s, len); + current_buffer.offset += len; + } + } +}; + +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cross_error_handling.hpp b/thirdparty/SPIRV-Cross/spirv_cross_error_handling.hpp new file mode 100644 index 000000000..91e6cf4f8 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_error_handling.hpp @@ -0,0 +1,99 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_ERROR_HANDLING +#define SPIRV_CROSS_ERROR_HANDLING + +#include +#include +#include +#ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#include +#endif + +#ifdef SPIRV_CROSS_NAMESPACE_OVERRIDE +#define SPIRV_CROSS_NAMESPACE SPIRV_CROSS_NAMESPACE_OVERRIDE +#else +#define SPIRV_CROSS_NAMESPACE spirv_cross +#endif + +namespace SPIRV_CROSS_NAMESPACE +{ +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS +#if !defined(_MSC_VER) || defined(__clang__) +[[noreturn]] +#elif defined(_MSC_VER) +__declspec(noreturn) +#endif +inline void +report_and_abort(const std::string &msg) +{ +#ifdef NDEBUG + (void)msg; +#else + fprintf(stderr, "There was a compiler error: %s\n", msg.c_str()); +#endif + fflush(stderr); + abort(); +} + +#define SPIRV_CROSS_THROW(x) report_and_abort(x) +#else +class CompilerError : public std::runtime_error +{ +public: + explicit CompilerError(const std::string &str) + : std::runtime_error(str) + { + } + + explicit CompilerError(const char *str) + : std::runtime_error(str) + { + } +}; + +#define SPIRV_CROSS_THROW(x) throw CompilerError(x) +#endif + +// MSVC 2013 does not have noexcept. We need this for Variant to get move constructor to work correctly +// instead of copy constructor. +// MSVC 2013 ignores that move constructors cannot throw in std::vector, so just don't define it. +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define SPIRV_CROSS_NOEXCEPT +#else +#define SPIRV_CROSS_NOEXCEPT noexcept +#endif + +#if __cplusplus >= 201402l +#define SPIRV_CROSS_DEPRECATED(reason) [[deprecated(reason)]] +#elif defined(__GNUC__) +#define SPIRV_CROSS_DEPRECATED(reason) __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define SPIRV_CROSS_DEPRECATED(reason) __declspec(deprecated(reason)) +#else +#define SPIRV_CROSS_DEPRECATED(reason) +#endif +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp b/thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp new file mode 100644 index 000000000..24cea02ac --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.cpp @@ -0,0 +1,1149 @@ +/* + * Copyright 2018-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_cross_parsed_ir.hpp" +#include +#include + +using namespace std; +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; + +namespace SPIRV_CROSS_NAMESPACE +{ +ParsedIR::ParsedIR() +{ + // If we move ParsedIR, we need to make sure the pointer stays fixed since the child Variant objects consume a pointer to this group, + // so need an extra pointer here. + pool_group.reset(new ObjectPoolGroup); + + pool_group->pools[TypeType].reset(new ObjectPool); + pool_group->pools[TypeVariable].reset(new ObjectPool); + pool_group->pools[TypeConstant].reset(new ObjectPool); + pool_group->pools[TypeFunction].reset(new ObjectPool); + pool_group->pools[TypeFunctionPrototype].reset(new ObjectPool); + pool_group->pools[TypeBlock].reset(new ObjectPool); + pool_group->pools[TypeExtension].reset(new ObjectPool); + pool_group->pools[TypeExpression].reset(new ObjectPool); + pool_group->pools[TypeConstantOp].reset(new ObjectPool); + pool_group->pools[TypeCombinedImageSampler].reset(new ObjectPool); + pool_group->pools[TypeAccessChain].reset(new ObjectPool); + pool_group->pools[TypeUndef].reset(new ObjectPool); + pool_group->pools[TypeString].reset(new ObjectPool); + pool_group->pools[TypeDebugLocalVariable].reset(new ObjectPool); +} + +// Should have been default-implemented, but need this on MSVC 2013. +ParsedIR::ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT +{ + *this = std::move(other); +} + +ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT +{ + if (this != &other) + { + pool_group = std::move(other.pool_group); + spirv = std::move(other.spirv); + meta = std::move(other.meta); + for (int i = 0; i < TypeCount; i++) + ids_for_type[i] = std::move(other.ids_for_type[i]); + ids_for_constant_undef_or_type = std::move(other.ids_for_constant_undef_or_type); + ids_for_constant_or_variable = std::move(other.ids_for_constant_or_variable); + declared_capabilities = std::move(other.declared_capabilities); + declared_extensions = std::move(other.declared_extensions); + block_meta = std::move(other.block_meta); + continue_block_to_loop_header = std::move(other.continue_block_to_loop_header); + entry_points = std::move(other.entry_points); + ids = std::move(other.ids); + addressing_model = other.addressing_model; + memory_model = other.memory_model; + + default_entry_point = other.default_entry_point; + sources = std::move(other.sources); + loop_iteration_depth_hard = other.loop_iteration_depth_hard; + loop_iteration_depth_soft = other.loop_iteration_depth_soft; + + meta_needing_name_fixup = std::move(other.meta_needing_name_fixup); + load_type_width = std::move(other.load_type_width); + } + return *this; +} + +ParsedIR::ParsedIR(const ParsedIR &other) + : ParsedIR() +{ + *this = other; +} + +ParsedIR &ParsedIR::operator=(const ParsedIR &other) +{ + if (this != &other) + { + spirv = other.spirv; + meta = other.meta; + for (int i = 0; i < TypeCount; i++) + ids_for_type[i] = other.ids_for_type[i]; + ids_for_constant_undef_or_type = other.ids_for_constant_undef_or_type; + ids_for_constant_or_variable = other.ids_for_constant_or_variable; + declared_capabilities = other.declared_capabilities; + declared_extensions = other.declared_extensions; + block_meta = other.block_meta; + continue_block_to_loop_header = other.continue_block_to_loop_header; + entry_points = other.entry_points; + default_entry_point = other.default_entry_point; + sources = other.sources; + loop_iteration_depth_hard = other.loop_iteration_depth_hard; + loop_iteration_depth_soft = other.loop_iteration_depth_soft; + addressing_model = other.addressing_model; + memory_model = other.memory_model; + + + meta_needing_name_fixup = other.meta_needing_name_fixup; + load_type_width = other.load_type_width; + + // Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor. + // Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group. + ids.clear(); + ids.reserve(other.ids.size()); + for (size_t i = 0; i < other.ids.size(); i++) + { + ids.emplace_back(pool_group.get()); + ids.back() = other.ids[i]; + } + } + return *this; +} + +void ParsedIR::set_id_bounds(uint32_t bounds) +{ + ids.reserve(bounds); + while (ids.size() < bounds) + ids.emplace_back(pool_group.get()); + + block_meta.resize(bounds); +} + +// Roll our own versions of these functions to avoid potential locale shenanigans. +static bool is_alpha(char c) +{ + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static bool is_numeric(char c) +{ + return c >= '0' && c <= '9'; +} + +static bool is_alphanumeric(char c) +{ + return is_alpha(c) || is_numeric(c); +} + +static bool is_valid_identifier(const string &name) +{ + if (name.empty()) + return true; + + if (is_numeric(name[0])) + return false; + + for (auto c : name) + if (!is_alphanumeric(c) && c != '_') + return false; + + bool saw_underscore = false; + // Two underscores in a row is not a valid identifier either. + // Technically reserved, but it's easier to treat it as invalid. + for (auto c : name) + { + bool is_underscore = c == '_'; + if (is_underscore && saw_underscore) + return false; + saw_underscore = is_underscore; + } + + return true; +} + +static bool is_reserved_prefix(const string &name) +{ + // Generic reserved identifiers used by the implementation. + return name.compare(0, 3, "gl_", 3) == 0 || + // Ignore this case for now, might rewrite internal code to always use spv prefix. + //name.compare(0, 11, "SPIRV_Cross", 11) == 0 || + name.compare(0, 3, "spv", 3) == 0; +} + +static bool is_reserved_identifier(const string &name, bool member, bool allow_reserved_prefixes) +{ + if (!allow_reserved_prefixes && is_reserved_prefix(name)) + return true; + + if (member) + { + // Reserved member identifiers come in one form: + // _m[0-9]+$. + if (name.size() < 3) + return false; + + if (name.compare(0, 2, "_m", 2) != 0) + return false; + + size_t index = 2; + while (index < name.size() && is_numeric(name[index])) + index++; + + return index == name.size(); + } + else + { + // Reserved non-member identifiers come in two forms: + // _[0-9]+$, used for temporaries which map directly to a SPIR-V ID. + // _[0-9]+_, used for auxillary temporaries which derived from a SPIR-V ID. + if (name.size() < 2) + return false; + + if (name[0] != '_' || !is_numeric(name[1])) + return false; + + size_t index = 2; + while (index < name.size() && is_numeric(name[index])) + index++; + + return index == name.size() || (index < name.size() && name[index] == '_'); + } +} + +bool ParsedIR::is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes) +{ + return is_reserved_identifier(str, false, allow_reserved_prefixes); +} + +uint32_t ParsedIR::get_spirv_version() const +{ + return spirv[1]; +} + +static string make_unreserved_identifier(const string &name) +{ + if (is_reserved_prefix(name)) + return "_RESERVED_IDENTIFIER_FIXUP_" + name; + else + return "_RESERVED_IDENTIFIER_FIXUP" + name; +} + +void ParsedIR::sanitize_underscores(std::string &str) +{ + // Compact adjacent underscores to make it valid. + auto dst = str.begin(); + auto src = dst; + bool saw_underscore = false; + while (src != str.end()) + { + bool is_underscore = *src == '_'; + if (saw_underscore && is_underscore) + { + src++; + } + else + { + if (dst != src) + *dst = *src; + dst++; + src++; + saw_underscore = is_underscore; + } + } + str.erase(dst, str.end()); +} + +static string ensure_valid_identifier(const string &name) +{ + // Functions in glslangValidator are mangled with name( stuff. + // Normally, we would never see '(' in any legal identifiers, so just strip them out. + auto str = name.substr(0, name.find('(')); + + if (str.empty()) + return str; + + if (is_numeric(str[0])) + str[0] = '_'; + + for (auto &c : str) + if (!is_alphanumeric(c) && c != '_') + c = '_'; + + ParsedIR::sanitize_underscores(str); + return str; +} + +const string &ParsedIR::get_name(ID id) const +{ + auto *m = find_meta(id); + if (m) + return m->decoration.alias; + else + return empty_string; +} + +const string &ParsedIR::get_member_name(TypeID id, uint32_t index) const +{ + auto *m = find_meta(id); + if (m) + { + if (index >= m->members.size()) + return empty_string; + return m->members[index].alias; + } + else + return empty_string; +} + +void ParsedIR::sanitize_identifier(std::string &name, bool member, bool allow_reserved_prefixes) +{ + if (!is_valid_identifier(name)) + name = ensure_valid_identifier(name); + if (is_reserved_identifier(name, member, allow_reserved_prefixes)) + name = make_unreserved_identifier(name); +} + +void ParsedIR::fixup_reserved_names() +{ + for (uint32_t id : meta_needing_name_fixup) + { + // Don't rename remapped variables like 'gl_LastFragDepthARM'. + if (ids[id].get_type() == TypeVariable && get(id).remapped_variable) + continue; + + auto &m = meta[id]; + sanitize_identifier(m.decoration.alias, false, false); + for (auto &memb : m.members) + sanitize_identifier(memb.alias, true, false); + } + meta_needing_name_fixup.clear(); +} + +void ParsedIR::set_name(ID id, const string &name) +{ + auto &m = meta[id]; + m.decoration.alias = name; + if (!is_valid_identifier(name) || is_reserved_identifier(name, false, false)) + meta_needing_name_fixup.insert(id); +} + +void ParsedIR::set_member_name(TypeID id, uint32_t index, const string &name) +{ + auto &m = meta[id]; + m.members.resize(max(m.members.size(), size_t(index) + 1)); + m.members[index].alias = name; + if (!is_valid_identifier(name) || is_reserved_identifier(name, true, false)) + meta_needing_name_fixup.insert(id); +} + +void ParsedIR::set_decoration_string(ID id, Decoration decoration, const string &argument) +{ + auto &dec = meta[id].decoration; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationUserSemantic: + dec.user_semantic = argument; + break; + + case DecorationUserTypeGOOGLE: + dec.user_type = argument; + break; + + default: + break; + } +} + +void ParsedIR::set_decoration(ID id, Decoration decoration, uint32_t argument) +{ + auto &dec = meta[id].decoration; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = true; + dec.builtin_type = static_cast(argument); + break; + + case DecorationLocation: + dec.location = argument; + break; + + case DecorationComponent: + dec.component = argument; + break; + + case DecorationOffset: + dec.offset = argument; + break; + + case DecorationOffsetIdEXT: + dec.offset_id = argument; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = argument; + break; + + case DecorationXfbStride: + dec.xfb_stride = argument; + break; + + case DecorationStream: + dec.stream = argument; + break; + + case DecorationArrayStride: + dec.array_stride = argument; + break; + + case DecorationArrayStrideIdEXT: + dec.array_stride_id = argument; + break; + + case DecorationMatrixStride: + dec.matrix_stride = argument; + break; + + case DecorationBinding: + dec.binding = argument; + break; + + case DecorationDescriptorSet: + dec.set = argument; + break; + + case DecorationInputAttachmentIndex: + dec.input_attachment = argument; + break; + + case DecorationSpecId: + dec.spec_id = argument; + break; + + case DecorationIndex: + dec.index = argument; + break; + + case DecorationHlslCounterBufferGOOGLE: + meta[id].hlsl_magic_counter_buffer = argument; + meta[argument].hlsl_is_magic_counter_buffer = true; + break; + + case DecorationFPRoundingMode: + dec.fp_rounding_mode = static_cast(argument); + break; + + case DecorationFPFastMathMode: + dec.fp_fast_math_mode = static_cast(argument); + break; + + default: + break; + } +} + +void ParsedIR::set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument) +{ + auto &m = meta[id]; + m.members.resize(max(m.members.size(), size_t(index) + 1)); + auto &dec = m.members[index]; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = true; + dec.builtin_type = static_cast(argument); + break; + + case DecorationLocation: + dec.location = argument; + break; + + case DecorationComponent: + dec.component = argument; + break; + + case DecorationBinding: + dec.binding = argument; + break; + + case DecorationOffset: + dec.offset = argument; + break; + + case DecorationOffsetIdEXT: + dec.offset_id = argument; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = argument; + break; + + case DecorationXfbStride: + dec.xfb_stride = argument; + break; + + case DecorationStream: + dec.stream = argument; + break; + + case DecorationSpecId: + dec.spec_id = argument; + break; + + case DecorationMatrixStride: + dec.matrix_stride = argument; + break; + + case DecorationIndex: + dec.index = argument; + break; + + default: + break; + } +} + +// Recursively marks any constants referenced by the specified constant instruction as being used +// as an array length. The id must be a constant instruction (SPIRConstant or SPIRConstantOp). +void ParsedIR::mark_used_as_array_length(ID id) +{ + switch (ids[id].get_type()) + { + case TypeConstant: + { + auto &c = get(id); + c.is_used_as_array_length = true; + + // Mark composite dependencies as well. + for (auto &sub_id: c.m.id) + if (sub_id) + mark_used_as_array_length(sub_id); + + for (uint32_t col = 0; col < c.m.columns; col++) + { + for (auto &sub_id : c.m.c[col].id) + if (sub_id) + mark_used_as_array_length(sub_id); + } + + for (auto &sub_id : c.subconstants) + if (sub_id) + mark_used_as_array_length(sub_id); + break; + } + + case TypeConstantOp: + { + auto &cop = get(id); + if (cop.opcode == OpCompositeExtract) + mark_used_as_array_length(cop.arguments[0]); + else if (cop.opcode == OpCompositeInsert) + { + mark_used_as_array_length(cop.arguments[0]); + mark_used_as_array_length(cop.arguments[1]); + } + else + for (uint32_t arg_id : cop.arguments) + mark_used_as_array_length(arg_id); + break; + } + + case TypeUndef: + break; + + default: + assert(0); + } +} + +Bitset ParsedIR::get_buffer_block_type_flags(const SPIRType &type) const +{ + if (type.member_types.empty()) + return {}; + + Bitset all_members_flags = get_member_decoration_bitset(type.self, 0); + for (uint32_t i = 1; i < uint32_t(type.member_types.size()); i++) + all_members_flags.merge_and(get_member_decoration_bitset(type.self, i)); + return all_members_flags; +} + +Bitset ParsedIR::get_buffer_block_flags(const SPIRVariable &var) const +{ + auto &type = get(var.basetype); + if (type.basetype != SPIRType::Struct) + SPIRV_CROSS_THROW("Cannot get buffer block flags for non-buffer variable."); + + // Some flags like non-writable, non-readable are actually found + // as member decorations. If all members have a decoration set, propagate + // the decoration up as a regular variable decoration. + Bitset base_flags; + auto *m = find_meta(var.self); + if (m) + base_flags = m->decoration.decoration_flags; + + if (type.member_types.empty()) + return base_flags; + + auto all_members_flags = get_buffer_block_type_flags(type); + base_flags.merge_or(all_members_flags); + return base_flags; +} + +const Bitset &ParsedIR::get_member_decoration_bitset(TypeID id, uint32_t index) const +{ + auto *m = find_meta(id); + if (m) + { + if (index >= m->members.size()) + return cleared_bitset; + return m->members[index].decoration_flags; + } + else + return cleared_bitset; +} + +bool ParsedIR::has_decoration(ID id, Decoration decoration) const +{ + return get_decoration_bitset(id).get(decoration); +} + +uint32_t ParsedIR::get_decoration(ID id, Decoration decoration) const +{ + auto *m = find_meta(id); + if (!m) + return 0; + + auto &dec = m->decoration; + if (!dec.decoration_flags.get(decoration)) + return 0; + + switch (decoration) + { + case DecorationBuiltIn: + return dec.builtin_type; + case DecorationLocation: + return dec.location; + case DecorationComponent: + return dec.component; + case DecorationOffset: + return dec.offset; + case DecorationOffsetIdEXT: + return dec.offset_id; + case DecorationXfbBuffer: + return dec.xfb_buffer; + case DecorationXfbStride: + return dec.xfb_stride; + case DecorationStream: + return dec.stream; + case DecorationBinding: + return dec.binding; + case DecorationDescriptorSet: + return dec.set; + case DecorationInputAttachmentIndex: + return dec.input_attachment; + case DecorationSpecId: + return dec.spec_id; + case DecorationArrayStride: + return dec.array_stride; + case DecorationArrayStrideIdEXT: + return dec.array_stride_id; + case DecorationMatrixStride: + return dec.matrix_stride; + case DecorationIndex: + return dec.index; + case DecorationFPRoundingMode: + return dec.fp_rounding_mode; + case DecorationFPFastMathMode: + return dec.fp_fast_math_mode; + default: + return 1; + } +} + +const string &ParsedIR::get_decoration_string(ID id, Decoration decoration) const +{ + auto *m = find_meta(id); + if (!m) + return empty_string; + + auto &dec = m->decoration; + + if (!dec.decoration_flags.get(decoration)) + return empty_string; + + switch (decoration) + { + case DecorationUserSemantic: + return dec.user_semantic; + + case DecorationUserTypeGOOGLE: + return dec.user_type; + + default: + return empty_string; + } +} + +void ParsedIR::unset_decoration(ID id, Decoration decoration) +{ + auto &dec = meta[id].decoration; + dec.decoration_flags.clear(decoration); + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = false; + break; + + case DecorationLocation: + dec.location = 0; + break; + + case DecorationComponent: + dec.component = 0; + break; + + case DecorationOffset: + dec.offset = 0; + break; + + case DecorationOffsetIdEXT: + dec.offset_id = 0; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = 0; + break; + + case DecorationXfbStride: + dec.xfb_stride = 0; + break; + + case DecorationStream: + dec.stream = 0; + break; + + case DecorationBinding: + dec.binding = 0; + break; + + case DecorationDescriptorSet: + dec.set = 0; + break; + + case DecorationInputAttachmentIndex: + dec.input_attachment = 0; + break; + + case DecorationSpecId: + dec.spec_id = 0; + break; + + case DecorationUserSemantic: + dec.user_semantic.clear(); + break; + + case DecorationFPRoundingMode: + dec.fp_rounding_mode = FPRoundingModeMax; + break; + + case DecorationFPFastMathMode: + dec.fp_fast_math_mode = FPFastMathModeMaskNone; + break; + + case DecorationHlslCounterBufferGOOGLE: + { + auto &counter = meta[id].hlsl_magic_counter_buffer; + if (counter) + { + meta[counter].hlsl_is_magic_counter_buffer = false; + counter = 0; + } + break; + } + + default: + break; + } +} + +bool ParsedIR::has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + return get_member_decoration_bitset(id, index).get(decoration); +} + +uint32_t ParsedIR::get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const +{ + auto *m = find_meta(id); + if (!m) + return 0; + + if (index >= m->members.size()) + return 0; + + auto &dec = m->members[index]; + if (!dec.decoration_flags.get(decoration)) + return 0; + + switch (decoration) + { + case DecorationBuiltIn: + return dec.builtin_type; + case DecorationLocation: + return dec.location; + case DecorationComponent: + return dec.component; + case DecorationBinding: + return dec.binding; + case DecorationOffset: + return dec.offset; + case DecorationOffsetIdEXT: + return dec.offset_id; + case DecorationXfbBuffer: + return dec.xfb_buffer; + case DecorationXfbStride: + return dec.xfb_stride; + case DecorationStream: + return dec.stream; + case DecorationSpecId: + return dec.spec_id; + case DecorationMatrixStride: + return dec.matrix_stride; + case DecorationIndex: + return dec.index; + default: + return 1; + } +} + +const Bitset &ParsedIR::get_decoration_bitset(ID id) const +{ + auto *m = find_meta(id); + if (m) + { + auto &dec = m->decoration; + return dec.decoration_flags; + } + else + return cleared_bitset; +} + +void ParsedIR::set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, const string &argument) +{ + auto &m = meta[id]; + m.members.resize(max(m.members.size(), size_t(index) + 1)); + auto &dec = meta[id].members[index]; + dec.decoration_flags.set(decoration); + + switch (decoration) + { + case DecorationUserSemantic: + dec.user_semantic = argument; + break; + + default: + break; + } +} + +const string &ParsedIR::get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const +{ + auto *m = find_meta(id); + if (m) + { + if (!has_member_decoration(id, index, decoration)) + return empty_string; + + auto &dec = m->members[index]; + + switch (decoration) + { + case DecorationUserSemantic: + return dec.user_semantic; + + default: + return empty_string; + } + } + else + return empty_string; +} + +void ParsedIR::unset_member_decoration(TypeID id, uint32_t index, Decoration decoration) +{ + auto &m = meta[id]; + if (index >= m.members.size()) + return; + + auto &dec = m.members[index]; + + dec.decoration_flags.clear(decoration); + switch (decoration) + { + case DecorationBuiltIn: + dec.builtin = false; + break; + + case DecorationLocation: + dec.location = 0; + break; + + case DecorationComponent: + dec.component = 0; + break; + + case DecorationOffset: + dec.offset = 0; + break; + + case DecorationOffsetIdEXT: + dec.offset_id = 0; + break; + + case DecorationXfbBuffer: + dec.xfb_buffer = 0; + break; + + case DecorationXfbStride: + dec.xfb_stride = 0; + break; + + case DecorationStream: + dec.stream = 0; + break; + + case DecorationSpecId: + dec.spec_id = 0; + break; + + case DecorationUserSemantic: + dec.user_semantic.clear(); + break; + + default: + break; + } +} + +uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount) +{ + auto curr_bound = ids.size(); + auto new_bound = curr_bound + incr_amount; + + ids.reserve(ids.size() + incr_amount); + for (uint32_t i = 0; i < incr_amount; i++) + ids.emplace_back(pool_group.get()); + + block_meta.resize(new_bound); + return uint32_t(curr_bound); +} + +void ParsedIR::remove_typed_id(Types type, ID id) +{ + auto &type_ids = ids_for_type[type]; + type_ids.erase(remove(begin(type_ids), end(type_ids), id), end(type_ids)); +} + +void ParsedIR::reset_all_of_type(Types type) +{ + for (auto &id : ids_for_type[type]) + if (ids[id].get_type() == type) + ids[id].reset(); + + ids_for_type[type].clear(); +} + +void ParsedIR::add_typed_id(Types type, ID id) +{ + assert(id < ids.size()); + + if (loop_iteration_depth_hard != 0) + SPIRV_CROSS_THROW("Cannot add typed ID while looping over it."); + + if (loop_iteration_depth_soft != 0) + { + if (!ids[id].empty()) + SPIRV_CROSS_THROW("Cannot override IDs when loop is soft locked."); + return; + } + + if (ids[id].empty() || ids[id].get_type() != type) + { + switch (type) + { + case TypeConstant: + ids_for_constant_or_variable.push_back(id); + ids_for_constant_undef_or_type.push_back(id); + break; + + case TypeVariable: + ids_for_constant_or_variable.push_back(id); + break; + + case TypeType: + case TypeConstantOp: + case TypeUndef: + ids_for_constant_undef_or_type.push_back(id); + break; + + default: + break; + } + } + + if (ids[id].empty()) + { + ids_for_type[type].push_back(id); + } + else if (ids[id].get_type() != type) + { + remove_typed_id(ids[id].get_type(), id); + ids_for_type[type].push_back(id); + } +} + +const Meta *ParsedIR::find_meta(ID id) const +{ + auto itr = meta.find(id); + if (itr != end(meta)) + return &itr->second; + else + return nullptr; +} + +Meta *ParsedIR::find_meta(ID id) +{ + auto itr = meta.find(id); + if (itr != end(meta)) + return &itr->second; + else + return nullptr; +} + +ParsedIR::LoopLock ParsedIR::create_loop_hard_lock() const +{ + return ParsedIR::LoopLock(&loop_iteration_depth_hard); +} + +ParsedIR::LoopLock ParsedIR::create_loop_soft_lock() const +{ + return ParsedIR::LoopLock(&loop_iteration_depth_soft); +} + +ParsedIR::LoopLock::~LoopLock() +{ + if (lock) + (*lock)--; +} + +ParsedIR::LoopLock::LoopLock(uint32_t *lock_) + : lock(lock_) +{ + if (lock) + (*lock)++; +} + +ParsedIR::LoopLock::LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT +{ + *this = std::move(other); +} + +ParsedIR::LoopLock &ParsedIR::LoopLock::operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT +{ + if (lock) + (*lock)--; + lock = other.lock; + other.lock = nullptr; + return *this; +} + +void ParsedIR::make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set) +{ + assert(id < ids.size()); + + auto &constant_type = get(type); + + if (constant_type.pointer) + { + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + auto &constant = variant_set(ids[id], type); + constant.self = id; + constant.make_null(constant_type); + } + else if (!constant_type.array.empty()) + { + assert(constant_type.parent_type); + uint32_t parent_id = increase_bound_by(1); + make_constant_null(parent_id, constant_type.parent_type, add_to_typed_id_set); + + // The array size of OpConstantNull can be either literal or specialization constant. + // In the latter case, we cannot take the value as-is, as it can be changed to anything. + // Rather, we assume it to be *one* for the sake of initializer. + bool is_literal_array_size = constant_type.array_size_literal.back(); + uint32_t count = is_literal_array_size ? constant_type.array.back() : 1; + + SmallVector elements(count); + for (uint32_t i = 0; i < count; i++) + elements[i] = parent_id; + + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + auto& constant = variant_set(ids[id], type, elements.data(), uint32_t(elements.size()), false); + constant.self = id; + constant.is_null_array_specialized_length = !is_literal_array_size; + } + else if (!constant_type.member_types.empty()) + { + uint32_t member_ids = increase_bound_by(uint32_t(constant_type.member_types.size())); + SmallVector elements(constant_type.member_types.size()); + for (uint32_t i = 0; i < constant_type.member_types.size(); i++) + { + make_constant_null(member_ids + i, constant_type.member_types[i], add_to_typed_id_set); + elements[i] = member_ids + i; + } + + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + variant_set(ids[id], type, elements.data(), uint32_t(elements.size()), false).self = id; + } + else + { + if (add_to_typed_id_set) + add_typed_id(TypeConstant, id); + auto &constant = variant_set(ids[id], type); + constant.self = id; + constant.make_null(constant_type); + } +} + +} // namespace SPIRV_CROSS_NAMESPACE diff --git a/thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp b/thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp new file mode 100644 index 000000000..b1e76f205 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_parsed_ir.hpp @@ -0,0 +1,273 @@ +/* + * Copyright 2018-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_PARSED_IR_HPP +#define SPIRV_CROSS_PARSED_IR_HPP + +#include "spirv_common.hpp" +#include +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; + +// This data structure holds all information needed to perform cross-compilation and reflection. +// It is the output of the Parser, but any implementation could create this structure. +// It is intentionally very "open" and struct-like with some helper functions to deal with decorations. +// Parser is the reference implementation of how this data structure should be filled in. + +class ParsedIR +{ +private: + // This must be destroyed after the "ids" vector. + std::unique_ptr pool_group; + +public: + ParsedIR(); + + // Due to custom allocations from object pools, we cannot use a default copy constructor. + ParsedIR(const ParsedIR &other); + ParsedIR &operator=(const ParsedIR &other); + + // Moves are unproblematic, but we need to implement it anyways, since MSVC 2013 does not understand + // how to default-implement these. + ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; + ParsedIR &operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT; + + // Resizes ids, meta and block_meta. + void set_id_bounds(uint32_t bounds); + + // The raw SPIR-V, instructions and opcodes refer to this by offset + count. + std::vector spirv; + + // Holds various data structures which inherit from IVariant. + SmallVector ids; + + // Various meta data for IDs, decorations, names, etc. + std::unordered_map meta; + + // Holds all IDs which have a certain type. + // This is needed so we can iterate through a specific kind of resource quickly, + // and in-order of module declaration. + SmallVector ids_for_type[TypeCount]; + + // Special purpose lists which contain a union of types. + // This is needed so we can declare specialization constants and structs in an interleaved fashion, + // among other things. + // Constants can be undef or of struct type, and struct array sizes can use specialization constants. + SmallVector ids_for_constant_undef_or_type; + SmallVector ids_for_constant_or_variable; + + // We need to keep track of the width the Ops that contains a type for the + // OpSwitch instruction, since this one doesn't contains the type in the + // instruction itself. And in some case we need to cast the condition to + // wider types. We only need the width to do the branch fixup since the + // type check itself can be done at runtime + std::unordered_map load_type_width; + + // Declared capabilities and extensions in the SPIR-V module. + // Not really used except for reflection at the moment. + SmallVector declared_capabilities; + SmallVector declared_extensions; + + // Meta data about blocks. The cross-compiler needs to query if a block is either of these types. + // It is a bitset as there can be more than one tag per block. + enum BlockMetaFlagBits + { + BLOCK_META_LOOP_HEADER_BIT = 1 << 0, + BLOCK_META_CONTINUE_BIT = 1 << 1, + BLOCK_META_LOOP_MERGE_BIT = 1 << 2, + BLOCK_META_SELECTION_MERGE_BIT = 1 << 3, + BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4 + }; + using BlockMetaFlags = uint8_t; + SmallVector block_meta; + std::unordered_map continue_block_to_loop_header; + + // Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction. + // Entry points can therefore be seen as some sort of meta structure. + std::unordered_map entry_points; + FunctionID default_entry_point = 0; + + struct Source + { + SourceLanguage lang = SourceLanguageUnknown; + uint32_t version = 0; + bool es = false; + bool known = false; + bool hlsl = false; + + ID file_id = 0; // string + ID define_id = 0; // only non-zero for DebugSource + std::string source; + + struct Marker + { + ID line; // in source + ID col; // in source + ID offset; // in spirv stream + ID function_id; + ID block_id; + }; + + SmallVector line_markers; // sorted by line + + Source() = default; + }; + + std::vector sources; + + AddressingModel addressing_model = AddressingModelMax; + MemoryModel memory_model = MemoryModelMax; + + // Decoration handling methods. + // Can be useful for simple "raw" reflection. + // However, most members are here because the Parser needs most of these, + // and might as well just have the whole suite of decoration/name handling in one place. + void set_name(ID id, const std::string &name); + const std::string &get_name(ID id) const; + void set_decoration(ID id, Decoration decoration, uint32_t argument = 0); + void set_decoration_string(ID id, Decoration decoration, const std::string &argument); + bool has_decoration(ID id, Decoration decoration) const; + uint32_t get_decoration(ID id, Decoration decoration) const; + const std::string &get_decoration_string(ID id, Decoration decoration) const; + const Bitset &get_decoration_bitset(ID id) const; + void unset_decoration(ID id, Decoration decoration); + + // Decoration handling methods (for members of a struct). + void set_member_name(TypeID id, uint32_t index, const std::string &name); + const std::string &get_member_name(TypeID id, uint32_t index) const; + void set_member_decoration(TypeID id, uint32_t index, Decoration decoration, uint32_t argument = 0); + void set_member_decoration_string(TypeID id, uint32_t index, Decoration decoration, + const std::string &argument); + uint32_t get_member_decoration(TypeID id, uint32_t index, Decoration decoration) const; + const std::string &get_member_decoration_string(TypeID id, uint32_t index, Decoration decoration) const; + bool has_member_decoration(TypeID id, uint32_t index, Decoration decoration) const; + const Bitset &get_member_decoration_bitset(TypeID id, uint32_t index) const; + void unset_member_decoration(TypeID id, uint32_t index, Decoration decoration); + + void mark_used_as_array_length(ID id); + uint32_t increase_bound_by(uint32_t count); + Bitset get_buffer_block_flags(const SPIRVariable &var) const; + Bitset get_buffer_block_type_flags(const SPIRType &type) const; + + void add_typed_id(Types type, ID id); + void remove_typed_id(Types type, ID id); + + class LoopLock + { + public: + explicit LoopLock(uint32_t *counter); + LoopLock(const LoopLock &) = delete; + void operator=(const LoopLock &) = delete; + LoopLock(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; + LoopLock &operator=(LoopLock &&other) SPIRV_CROSS_NOEXCEPT; + ~LoopLock(); + + private: + uint32_t *lock = nullptr; + }; + + // This must be held while iterating over a type ID array. + // It is undefined if someone calls set<>() while we're iterating over a data structure, so we must + // make sure that this case is avoided. + + // If we have a hard lock, it is an error to call set<>(), and an exception is thrown. + // If we have a soft lock, we silently ignore any additions to the typed arrays. + // This should only be used for physical ID remapping where we need to create an ID, but we will never + // care about iterating over them. + LoopLock create_loop_hard_lock() const; + LoopLock create_loop_soft_lock() const; + + template + void for_each_typed_id(const Op &op) + { + auto loop_lock = create_loop_hard_lock(); + for (auto &id : ids_for_type[T::type]) + { + if (ids[id].get_type() == static_cast(T::type)) + op(id, get(id)); + } + } + + template + void for_each_typed_id(const Op &op) const + { + auto loop_lock = create_loop_hard_lock(); + for (auto &id : ids_for_type[T::type]) + { + if (ids[id].get_type() == static_cast(T::type)) + op(id, get(id)); + } + } + + template + void reset_all_of_type() + { + reset_all_of_type(static_cast(T::type)); + } + + void reset_all_of_type(Types type); + + Meta *find_meta(ID id); + const Meta *find_meta(ID id) const; + + const std::string &get_empty_string() const + { + return empty_string; + } + + void make_constant_null(uint32_t id, uint32_t type, bool add_to_typed_id_set); + + void fixup_reserved_names(); + + static void sanitize_underscores(std::string &str); + static void sanitize_identifier(std::string &str, bool member, bool allow_reserved_prefixes); + static bool is_globally_reserved_identifier(std::string &str, bool allow_reserved_prefixes); + + uint32_t get_spirv_version() const; + + template + T &get(uint32_t id) + { + return variant_get(ids[id]); + } + + template + const T &get(uint32_t id) const + { + return variant_get(ids[id]); + } + +private: + mutable uint32_t loop_iteration_depth_hard = 0; + mutable uint32_t loop_iteration_depth_soft = 0; + std::string empty_string; + Bitset cleared_bitset; + + std::unordered_set meta_needing_name_fixup; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_cross_util.cpp b/thirdparty/SPIRV-Cross/spirv_cross_util.cpp new file mode 100644 index 000000000..f30706f59 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_util.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_cross_util.hpp" +#include "spirv_common.hpp" + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; + +namespace spirv_cross_util +{ +void rename_interface_variable(Compiler &compiler, const SmallVector &resources, uint32_t location, + const std::string &name) +{ + for (auto &v : resources) + { + if (!compiler.has_decoration(v.id, DecorationLocation)) + continue; + + auto loc = compiler.get_decoration(v.id, DecorationLocation); + if (loc != location) + continue; + + auto &type = compiler.get_type(v.base_type_id); + + // This is more of a friendly variant. If we need to rename interface variables, we might have to rename + // structs as well and make sure all the names match up. + if (type.basetype == SPIRType::Struct) + { + compiler.set_name(v.base_type_id, join("SPIRV_Cross_Interface_Location", location)); + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + compiler.set_member_name(v.base_type_id, i, join("InterfaceMember", i)); + } + + compiler.set_name(v.id, name); + } +} + +void inherit_combined_sampler_bindings(Compiler &compiler) +{ + auto &samplers = compiler.get_combined_image_samplers(); + for (auto &s : samplers) + { + if (compiler.has_decoration(s.image_id, DecorationDescriptorSet)) + { + uint32_t set = compiler.get_decoration(s.image_id, DecorationDescriptorSet); + compiler.set_decoration(s.combined_id, DecorationDescriptorSet, set); + } + + if (compiler.has_decoration(s.image_id, DecorationBinding)) + { + uint32_t binding = compiler.get_decoration(s.image_id, DecorationBinding); + compiler.set_decoration(s.combined_id, DecorationBinding, binding); + } + } +} +} // namespace spirv_cross_util diff --git a/thirdparty/SPIRV-Cross/spirv_cross_util.hpp b/thirdparty/SPIRV-Cross/spirv_cross_util.hpp new file mode 100644 index 000000000..e6e3fcdb6 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_cross_util.hpp @@ -0,0 +1,37 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_UTIL_HPP +#define SPIRV_CROSS_UTIL_HPP + +#include "spirv_cross.hpp" + +namespace spirv_cross_util +{ +void rename_interface_variable(SPIRV_CROSS_NAMESPACE::Compiler &compiler, + const SPIRV_CROSS_NAMESPACE::SmallVector &resources, + uint32_t location, const std::string &name); +void inherit_combined_sampler_bindings(SPIRV_CROSS_NAMESPACE::Compiler &compiler); +} // namespace spirv_cross_util + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_glsl.cpp b/thirdparty/SPIRV-Cross/spirv_glsl.cpp new file mode 100644 index 000000000..15f1bc245 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_glsl.cpp @@ -0,0 +1,20865 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_glsl.hpp" +#include "GLSL.std.450.h" +#include "spirv_common.hpp" +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif +#include + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +namespace SPIRV_CROSS_NAMESPACE +{ + +enum ExtraSubExpressionType +{ + // Create masks above any legal ID range to allow multiple address spaces into the extra_sub_expressions map. + EXTRA_SUB_EXPRESSION_TYPE_STREAM_OFFSET = 0x10000000, + EXTRA_SUB_EXPRESSION_TYPE_AUX = 0x20000000 +}; + +struct GlslConstantNameMapping +{ + uint32_t value; + const char *alias; +}; + +#define DEF_GLSL_MAPPING(x) { x, "gl_" #x } +#define DEF_GLSL_MAPPING_EXT(x) { x##KHR, "gl_" #x } +static const GlslConstantNameMapping CoopVecComponentTypeNames[] = { + DEF_GLSL_MAPPING(ComponentTypeFloat16NV), + DEF_GLSL_MAPPING(ComponentTypeFloat32NV), + DEF_GLSL_MAPPING(ComponentTypeFloat64NV), + DEF_GLSL_MAPPING(ComponentTypeSignedInt8NV), + DEF_GLSL_MAPPING(ComponentTypeSignedInt16NV), + DEF_GLSL_MAPPING(ComponentTypeSignedInt32NV), + DEF_GLSL_MAPPING(ComponentTypeSignedInt64NV), + DEF_GLSL_MAPPING(ComponentTypeUnsignedInt8NV), + DEF_GLSL_MAPPING(ComponentTypeUnsignedInt16NV), + DEF_GLSL_MAPPING(ComponentTypeUnsignedInt32NV), + DEF_GLSL_MAPPING(ComponentTypeUnsignedInt64NV), + DEF_GLSL_MAPPING(ComponentTypeSignedInt8PackedNV), + DEF_GLSL_MAPPING(ComponentTypeUnsignedInt8PackedNV), + DEF_GLSL_MAPPING(ComponentTypeFloatE4M3NV), + DEF_GLSL_MAPPING(ComponentTypeFloatE5M2NV), +}; + +static const GlslConstantNameMapping CoopVecMatrixLayoutNames[] = { + DEF_GLSL_MAPPING(CooperativeVectorMatrixLayoutRowMajorNV), + DEF_GLSL_MAPPING(CooperativeVectorMatrixLayoutColumnMajorNV), + DEF_GLSL_MAPPING(CooperativeVectorMatrixLayoutInferencingOptimalNV), + DEF_GLSL_MAPPING(CooperativeVectorMatrixLayoutTrainingOptimalNV), +}; + +static const GlslConstantNameMapping CoopMatMatrixLayoutNames[] = { + DEF_GLSL_MAPPING_EXT(CooperativeMatrixLayoutRowMajor), + DEF_GLSL_MAPPING_EXT(CooperativeMatrixLayoutColumnMajor), +}; +#undef DEF_GLSL_MAPPING +#undef DEF_GLSL_MAPPING_EXT + +static bool is_unsigned_opcode(Op op) +{ + // Don't have to be exhaustive, only relevant for legacy target checking ... + switch (op) + { + case OpShiftRightLogical: + case OpUGreaterThan: + case OpUGreaterThanEqual: + case OpULessThan: + case OpULessThanEqual: + case OpUConvert: + case OpUDiv: + case OpUMod: + case OpUMulExtended: + case OpConvertUToF: + case OpConvertFToU: + return true; + + default: + return false; + } +} + +static bool is_unsigned_glsl_opcode(GLSLstd450 op) +{ + // Don't have to be exhaustive, only relevant for legacy target checking ... + switch (op) + { + case GLSLstd450UClamp: + case GLSLstd450UMin: + case GLSLstd450UMax: + case GLSLstd450FindUMsb: + return true; + + default: + return false; + } +} + +static bool packing_is_vec4_padded(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingHLSLCbuffer: + case BufferPackingHLSLCbufferPackOffset: + case BufferPackingStd140: + case BufferPackingStd140EnhancedLayout: + return true; + + default: + return false; + } +} + +static bool packing_is_hlsl(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingHLSLCbuffer: + case BufferPackingHLSLCbufferPackOffset: + return true; + + default: + return false; + } +} + +static bool packing_has_flexible_offset(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingStd140: + case BufferPackingStd430: + case BufferPackingScalar: + case BufferPackingHLSLCbuffer: + return false; + + default: + return true; + } +} + +static bool packing_is_scalar(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingScalar: + case BufferPackingScalarEnhancedLayout: + return true; + + default: + return false; + } +} + +static BufferPackingStandard packing_to_substruct_packing(BufferPackingStandard packing) +{ + switch (packing) + { + case BufferPackingStd140EnhancedLayout: + return BufferPackingStd140; + case BufferPackingStd430EnhancedLayout: + return BufferPackingStd430; + case BufferPackingHLSLCbufferPackOffset: + return BufferPackingHLSLCbuffer; + case BufferPackingScalarEnhancedLayout: + return BufferPackingScalar; + default: + return packing; + } +} +} + +void CompilerGLSL::init() +{ + if (!ir.sources.empty() && ir.sources.front().known) + { + options.es = ir.sources.front().es; + options.version = ir.sources.front().version; + } + + // Query the locale to see what the decimal point is. + // We'll rely on fixing it up ourselves in the rare case we have a comma-as-decimal locale + // rather than setting locales ourselves. Settings locales in a safe and isolated way is rather + // tricky. +#ifdef _WIN32 + // On Windows, localeconv uses thread-local storage, so it should be fine. + const struct lconv *conv = localeconv(); + if (conv && conv->decimal_point) + current_locale_radix_character = *conv->decimal_point; +#elif defined(__ANDROID__) && __ANDROID_API__ < 26 + // nl_langinfo is not supported on this platform, fall back to the worse alternative. + const struct lconv *conv = localeconv(); + if (conv && conv->decimal_point) + current_locale_radix_character = *conv->decimal_point; +#else + // localeconv, the portable function is not MT safe ... + const char *decimal_point = nl_langinfo(RADIXCHAR); + if (decimal_point && *decimal_point != '\0') + current_locale_radix_character = *decimal_point; +#endif +} + +static const char *to_pls_layout(PlsFormat format) +{ + switch (format) + { + case PlsR11FG11FB10F: + return "layout(r11f_g11f_b10f) "; + case PlsR32F: + return "layout(r32f) "; + case PlsRG16F: + return "layout(rg16f) "; + case PlsRGB10A2: + return "layout(rgb10_a2) "; + case PlsRGBA8: + return "layout(rgba8) "; + case PlsRG16: + return "layout(rg16) "; + case PlsRGBA8I: + return "layout(rgba8i)"; + case PlsRG16I: + return "layout(rg16i) "; + case PlsRGB10A2UI: + return "layout(rgb10_a2ui) "; + case PlsRGBA8UI: + return "layout(rgba8ui) "; + case PlsRG16UI: + return "layout(rg16ui) "; + case PlsR32UI: + return "layout(r32ui) "; + default: + return ""; + } +} + +static std::pair pls_format_to_basetype(PlsFormat format) +{ + switch (format) + { + default: + case PlsR11FG11FB10F: + case PlsR32F: + case PlsRG16F: + case PlsRGB10A2: + case PlsRGBA8: + case PlsRG16: + return std::make_pair(OpTypeFloat, SPIRType::Float); + + case PlsRGBA8I: + case PlsRG16I: + return std::make_pair(OpTypeInt, SPIRType::Int); + + case PlsRGB10A2UI: + case PlsRGBA8UI: + case PlsRG16UI: + case PlsR32UI: + return std::make_pair(OpTypeInt, SPIRType::UInt); + } +} + +static uint32_t pls_format_to_components(PlsFormat format) +{ + switch (format) + { + default: + case PlsR32F: + case PlsR32UI: + return 1; + + case PlsRG16F: + case PlsRG16: + case PlsRG16UI: + case PlsRG16I: + return 2; + + case PlsR11FG11FB10F: + return 3; + + case PlsRGB10A2: + case PlsRGBA8: + case PlsRGBA8I: + case PlsRGB10A2UI: + case PlsRGBA8UI: + return 4; + } +} + +const char *CompilerGLSL::vector_swizzle(int vecsize, int index) +{ + static const char *const swizzle[4][4] = { + { ".x", ".y", ".z", ".w" }, + { ".xy", ".yz", ".zw", nullptr }, + { ".xyz", ".yzw", nullptr, nullptr }, +#if defined(__GNUC__) && (__GNUC__ == 9) + // This works around a GCC 9 bug, see details in https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90947. + // This array ends up being compiled as all nullptrs, tripping the assertions below. + { "", nullptr, nullptr, "$" }, +#else + { "", nullptr, nullptr, nullptr }, +#endif + }; + + assert(vecsize >= 1 && vecsize <= 4); + assert(index >= 0 && index < 4); + assert(swizzle[vecsize - 1][index]); + + return swizzle[vecsize - 1][index]; +} + +void CompilerGLSL::reset(uint32_t iteration_count) +{ + // Sanity check the iteration count to be robust against a certain class of bugs where + // we keep forcing recompilations without making clear forward progress. + // In buggy situations we will loop forever, or loop for an unbounded number of iterations. + // Certain types of recompilations are considered to make forward progress, + // but in almost all situations, we'll never see more than 3 iterations. + // It is highly context-sensitive when we need to force recompilation, + // and it is not practical with the current architecture + // to resolve everything up front. + if (iteration_count >= options.force_recompile_max_debug_iterations && !is_force_recompile_forward_progress) + SPIRV_CROSS_THROW("Maximum compilation loops detected and no forward progress was made. Must be a SPIRV-Cross bug!"); + + // We do some speculative optimizations which should pretty much always work out, + // but just in case the SPIR-V is rather weird, recompile until it's happy. + // This typically only means one extra pass. + clear_force_recompile(); + + // Clear invalid expression tracking. + invalid_expressions.clear(); + composite_insert_overwritten.clear(); + current_function = nullptr; + + // Clear temporary usage tracking. + expression_usage_counts.clear(); + forwarded_temporaries.clear(); + suppressed_usage_tracking.clear(); + buffer_pointer_variables.clear(); + + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + flushed_phi_variables.clear(); + + current_emitting_switch_stack.clear(); + + reset_name_caches(); + + ir.for_each_typed_id([&](uint32_t, SPIRFunction &func) { + func.active = false; + func.flush_undeclared = true; + }); + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { var.dependees.clear(); }); + ir.for_each_typed_id([&](uint32_t, SPIRBlock &block) { block.rearm_dominated_variables.clear(); }); + + ir.reset_all_of_type(); + ir.reset_all_of_type(); + + statement_count = 0; + indent = 0; + current_loop_level = 0; +} + +void CompilerGLSL::remap_pls_variables() +{ + for (auto &input : pls_inputs) + { + auto &var = get(input.id); + + bool input_is_target = false; + if (var.storage == StorageClassUniformConstant) + { + auto &type = get(var.basetype); + input_is_target = type.image.dim == DimSubpassData; + } + + if (var.storage != StorageClassInput && !input_is_target) + SPIRV_CROSS_THROW("Can only use in and target variables for PLS inputs."); + var.remapped_variable = true; + } + + for (auto &output : pls_outputs) + { + auto &var = get(output.id); + if (var.storage != StorageClassOutput) + SPIRV_CROSS_THROW("Can only use out variables for PLS outputs."); + var.remapped_variable = true; + } +} + +void CompilerGLSL::remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location, bool coherent) +{ + subpass_to_framebuffer_fetch_attachment.push_back({ input_attachment_index, color_location }); + inout_color_attachments.push_back({ color_location, coherent }); +} + +bool CompilerGLSL::location_is_framebuffer_fetch(uint32_t location) const +{ + return std::find_if(begin(inout_color_attachments), end(inout_color_attachments), + [&](const std::pair &elem) { + return elem.first == location; + }) != end(inout_color_attachments); +} + +bool CompilerGLSL::location_is_non_coherent_framebuffer_fetch(uint32_t location) const +{ + return std::find_if(begin(inout_color_attachments), end(inout_color_attachments), + [&](const std::pair &elem) { + return elem.first == location && !elem.second; + }) != end(inout_color_attachments); +} + +void CompilerGLSL::find_static_extensions() +{ + ir.for_each_typed_id([&](uint32_t, const SPIRType &type) { + if (type.basetype == SPIRType::Double) + { + if (options.es) + SPIRV_CROSS_THROW("FP64 not supported in ES profile."); + if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_gpu_shader_fp64"); + } + else if (type.basetype == SPIRType::Int64 || type.basetype == SPIRType::UInt64) + { + if (options.es && options.version < 310) // GL_NV_gpu_shader5 fallback requires 310. + SPIRV_CROSS_THROW("64-bit integers not supported in ES profile before version 310."); + require_extension_internal("GL_ARB_gpu_shader_int64"); + } + else if (type.basetype == SPIRType::Half) + { + require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_float16"); + if (options.vulkan_semantics) + require_extension_internal("GL_EXT_shader_16bit_storage"); + } + else if (type.basetype == SPIRType::SByte || type.basetype == SPIRType::UByte) + { + require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_int8"); + if (options.vulkan_semantics) + require_extension_internal("GL_EXT_shader_8bit_storage"); + } + else if (type.basetype == SPIRType::Short || type.basetype == SPIRType::UShort) + { + require_extension_internal("GL_EXT_shader_explicit_arithmetic_types_int16"); + if (options.vulkan_semantics) + require_extension_internal("GL_EXT_shader_16bit_storage"); + } + }); + + auto &execution = get_entry_point(); + switch (execution.model) + { + case ExecutionModelGLCompute: + if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_compute_shader"); + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for compute shaders."); + break; + + case ExecutionModelGeometry: + if (options.es && options.version < 320) + require_extension_internal("GL_EXT_geometry_shader"); + if (!options.es && options.version < 150) + require_extension_internal("GL_ARB_geometry_shader4"); + + if (execution.flags.get(ExecutionModeInvocations) && execution.invocations != 1) + { + // Instanced GS is part of 400 core or this extension. + if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_gpu_shader5"); + } + break; + + case ExecutionModelTessellationEvaluation: + case ExecutionModelTessellationControl: + if (options.es && options.version < 320) + require_extension_internal("GL_EXT_tessellation_shader"); + if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_tessellation_shader"); + break; + + case ExecutionModelRayGenerationKHR: + case ExecutionModelIntersectionKHR: + case ExecutionModelAnyHitKHR: + case ExecutionModelClosestHitKHR: + case ExecutionModelMissKHR: + case ExecutionModelCallableKHR: + // NV enums are aliases. + if (options.es || options.version < 460) + SPIRV_CROSS_THROW("Ray tracing shaders require non-es profile with version 460 or above."); + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Ray tracing requires Vulkan semantics."); + + // Need to figure out if we should target KHR or NV extension based on capabilities. + for (auto &cap : ir.declared_capabilities) + { + if (cap == CapabilityRayTracingKHR || cap == CapabilityRayQueryKHR || + cap == CapabilityRayTraversalPrimitiveCullingKHR) + { + ray_tracing_is_khr = true; + break; + } + } + + if (ray_tracing_is_khr) + { + // In KHR ray tracing we pass payloads by pointer instead of location, + // so make sure we assign locations properly. + ray_tracing_khr_fixup_locations(); + require_extension_internal("GL_EXT_ray_tracing"); + } + else + require_extension_internal("GL_NV_ray_tracing"); + break; + + case ExecutionModelMeshEXT: + case ExecutionModelTaskEXT: + if (options.es || options.version < 450) + SPIRV_CROSS_THROW("Mesh shaders require GLSL 450 or above."); + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Mesh shaders require Vulkan semantics."); + require_extension_internal("GL_EXT_mesh_shader"); + break; + + default: + break; + } + + if (!pls_inputs.empty() || !pls_outputs.empty()) + { + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Can only use GL_EXT_shader_pixel_local_storage in fragment shaders."); + require_extension_internal("GL_EXT_shader_pixel_local_storage"); + } + + if (!inout_color_attachments.empty()) + { + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Can only use GL_EXT_shader_framebuffer_fetch in fragment shaders."); + if (options.vulkan_semantics) + SPIRV_CROSS_THROW("Cannot use EXT_shader_framebuffer_fetch in Vulkan GLSL."); + + bool has_coherent = false; + bool has_incoherent = false; + + for (auto &att : inout_color_attachments) + { + if (att.second) + has_coherent = true; + else + has_incoherent = true; + } + + if (has_coherent) + require_extension_internal("GL_EXT_shader_framebuffer_fetch"); + if (has_incoherent) + require_extension_internal("GL_EXT_shader_framebuffer_fetch_non_coherent"); + } + + if (options.separate_shader_objects && !options.es && options.version < 410) + require_extension_internal("GL_ARB_separate_shader_objects"); + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64) + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_buffer_reference is only supported in Vulkan GLSL."); + if (options.es && options.version < 320) + SPIRV_CROSS_THROW("GL_EXT_buffer_reference requires ESSL 320."); + else if (!options.es && options.version < 450) + SPIRV_CROSS_THROW("GL_EXT_buffer_reference requires GLSL 450."); + require_extension_internal("GL_EXT_buffer_reference2"); + } + else if (ir.addressing_model != AddressingModelLogical) + { + SPIRV_CROSS_THROW("Only Logical and PhysicalStorageBuffer64 addressing models are supported."); + } + + // Check for nonuniform qualifier and passthrough. + // Instead of looping over all decorations to find this, just look at capabilities. + for (auto &cap : ir.declared_capabilities) + { + switch (cap) + { + case CapabilityShaderNonUniform: + if (!options.vulkan_semantics) + require_extension_internal("GL_NV_gpu_shader5"); + else + require_extension_internal("GL_EXT_nonuniform_qualifier"); + break; + case CapabilityRuntimeDescriptorArrayEXT: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_nonuniform_qualifier is only supported in Vulkan GLSL."); + require_extension_internal("GL_EXT_nonuniform_qualifier"); + break; + + case CapabilityGeometryShaderPassthroughNV: + if (execution.model == ExecutionModelGeometry) + { + require_extension_internal("GL_NV_geometry_shader_passthrough"); + execution.geometry_passthrough = true; + } + break; + + case CapabilityVariablePointers: + case CapabilityVariablePointersStorageBuffer: + SPIRV_CROSS_THROW("VariablePointers capability is not supported in GLSL."); + + case CapabilityMultiView: + if (options.vulkan_semantics) + require_extension_internal("GL_EXT_multiview"); + else + { + require_extension_internal("GL_OVR_multiview2"); + if (options.ovr_multiview_view_count == 0) + SPIRV_CROSS_THROW("ovr_multiview_view_count must be non-zero when using GL_OVR_multiview2."); + if (get_execution_model() != ExecutionModelVertex) + SPIRV_CROSS_THROW("OVR_multiview2 can only be used with Vertex shaders."); + } + break; + + case CapabilityRayQueryKHR: + if (options.es || options.version < 460 || !options.vulkan_semantics) + SPIRV_CROSS_THROW("RayQuery requires Vulkan GLSL 460."); + require_extension_internal("GL_EXT_ray_query"); + ray_tracing_is_khr = true; + break; + + case CapabilityRayQueryPositionFetchKHR: + if (options.es || options.version < 460 || !options.vulkan_semantics) + SPIRV_CROSS_THROW("RayQuery Position Fetch requires Vulkan GLSL 460."); + require_extension_internal("GL_EXT_ray_tracing_position_fetch"); + ray_tracing_is_khr = true; + break; + + case CapabilityRayTracingPositionFetchKHR: + if (options.es || options.version < 460 || !options.vulkan_semantics) + SPIRV_CROSS_THROW("Ray Tracing Position Fetch requires Vulkan GLSL 460."); + require_extension_internal("GL_EXT_ray_tracing_position_fetch"); + ray_tracing_is_khr = true; + break; + + case CapabilityRayTraversalPrimitiveCullingKHR: + if (options.es || options.version < 460 || !options.vulkan_semantics) + SPIRV_CROSS_THROW("RayQuery requires Vulkan GLSL 460."); + require_extension_internal("GL_EXT_ray_flags_primitive_culling"); + ray_tracing_is_khr = true; + break; + + case CapabilityRayTracingClusterAccelerationStructureNV: + if (options.es || options.version < 460 || !options.vulkan_semantics) + SPIRV_CROSS_THROW("Cluster AS requires Vulkan GLSL 460."); + require_extension_internal("GL_NV_cluster_acceleration_structure"); + ray_tracing_is_khr = true; + break; + + case CapabilityTensorsARM: + if (options.es || options.version < 460 || !options.vulkan_semantics) + SPIRV_CROSS_THROW("Tensor requires Vulkan GLSL 460."); + require_extension_internal("GL_ARM_tensors"); + break; + + case CapabilityDescriptorHeapEXT: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("DescriptorHeapEXT requires Vulkan semantics."); + require_extension_internal("GL_EXT_descriptor_heap"); + require_extension_internal("GL_EXT_nonuniform_qualifier"); + // We lose information about writeonly/readonly in SPIR-V. Just pre-empt this to avoid complicating code later. + require_extension_internal("GL_EXT_shader_image_load_formatted"); + break; + + default: + break; + } + } + + if (options.ovr_multiview_view_count) + { + if (options.vulkan_semantics) + SPIRV_CROSS_THROW("OVR_multiview2 cannot be used with Vulkan semantics."); + if (get_execution_model() != ExecutionModelVertex) + SPIRV_CROSS_THROW("OVR_multiview2 can only be used with Vertex shaders."); + require_extension_internal("GL_OVR_multiview2"); + } + + if (execution.flags.get(ExecutionModeQuadDerivativesKHR) || + (execution.flags.get(ExecutionModeRequireFullQuadsKHR) && get_execution_model() == ExecutionModelFragment)) + { + require_extension_internal("GL_EXT_shader_quad_control"); + } + + // KHR one is likely to get promoted at some point, so if we don't see an explicit SPIR-V extension, assume KHR. + for (auto &ext : ir.declared_extensions) + if (ext == "SPV_NV_fragment_shader_barycentric") + barycentric_is_nv = true; +} + +void CompilerGLSL::require_polyfill(Polyfill polyfill, bool relaxed) +{ + uint32_t &polyfills = (relaxed && (options.es || options.vulkan_semantics)) ? + required_polyfills_relaxed : required_polyfills; + + if ((polyfills & polyfill) == 0) + { + polyfills |= polyfill; + force_recompile(); + } +} + +void CompilerGLSL::ray_tracing_khr_fixup_locations() +{ + uint32_t location = 0; + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + // Incoming payload storage can also be used for tracing. + if (var.storage != StorageClassRayPayloadKHR && var.storage != StorageClassCallableDataKHR && + var.storage != StorageClassIncomingRayPayloadKHR && var.storage != StorageClassIncomingCallableDataKHR) + return; + if (is_hidden_variable(var)) + return; + set_decoration(var.self, DecorationLocation, location++); + }); +} + +string CompilerGLSL::compile() +{ + ir.fixup_reserved_names(); + + if (!options.vulkan_semantics) + { + // only NV_gpu_shader5 supports divergent indexing on OpenGL, and it does so without extra qualifiers + backend.nonuniform_qualifier = ""; + backend.needs_row_major_load_workaround = options.enable_row_major_load_workaround; + } + backend.allow_precision_qualifiers = options.vulkan_semantics || options.es; + backend.force_gl_in_out_block = true; + backend.supports_extensions = true; + backend.use_array_constructor = true; + backend.workgroup_size_is_hidden = true; + backend.requires_relaxed_precision_analysis = options.es || options.vulkan_semantics; + backend.support_precise_qualifier = + (!options.es && options.version >= 400) || (options.es && options.version >= 320); + backend.constant_null_initializer = "{ }"; + backend.requires_matching_array_initializer = true; + + if (is_legacy_es()) + backend.support_case_fallthrough = false; + + // Scan the SPIR-V to find trivial uses of extensions. + fixup_anonymous_struct_names(); + fixup_type_alias(); + reorder_type_alias(); + build_function_control_flow_graphs_and_analyze(); + find_static_extensions(); + fixup_image_load_store_access(); + update_active_builtins(); + analyze_image_and_sampler_usage(); + analyze_interlocked_resource_usage(); + if (!inout_color_attachments.empty()) + emit_inout_fragment_outputs_copy_to_subpass_inputs(); + + // Shaders might cast unrelated data to pointers of non-block types. + // Find all such instances and make sure we can cast the pointers to a synthesized block type. + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64) + analyze_non_block_pointer_types(); + + if (std::find(ir.declared_capabilities.begin(), ir.declared_capabilities.end(), + CapabilityDescriptorHeapEXT) != ir.declared_capabilities.end()) + { + // Need to figure out all the aliased types that view the heap. + // In GLSL, each unique type must be declared with layout(descriptor_heap) type-decl spvSomeIdentResourceHeap[]; + // During untyped access chain traversal, we prefix the name to match the aliases. + // HLSL has more direct native support and will not need these, but we still need to call this function + // to verify that descriptor strides make sense. + analyze_descriptor_heap_types(); + } + + uint32_t pass_count = 0; + do + { + reset(pass_count); + + buffer.reset(); + + emit_header(); + emit_resources(); + emit_extension_workarounds(get_execution_model()); + + if (required_polyfills != 0) + emit_polyfills(required_polyfills, false); + if ((options.es || options.vulkan_semantics) && required_polyfills_relaxed != 0) + emit_polyfills(required_polyfills_relaxed, true); + + emit_function(get(ir.default_entry_point), Bitset()); + + pass_count++; + } while (is_forcing_recompilation()); + + // Implement the interlocked wrapper function at the end. + // The body was implemented in lieu of main(). + if (interlocked_is_complex) + { + if (options.use_entry_point_name) + statement("void ", get_entry_point().name, "()"); + else + statement("void main()"); + begin_scope(); + statement("// Interlocks were used in a way not compatible with GLSL, this is very slow."); + statement("SPIRV_Cross_beginInvocationInterlock();"); + statement("spvMainInterlockedBody();"); + statement("SPIRV_Cross_endInvocationInterlock();"); + end_scope(); + } + + // Entry point in GLSL is always main(). + if (!options.use_entry_point_name) + get_entry_point().name = "main"; + + return buffer.str(); +} + +std::string CompilerGLSL::get_partial_source() +{ + return buffer.str(); +} + +void CompilerGLSL::build_workgroup_size(SmallVector &arguments, const SpecializationConstant &wg_x, + const SpecializationConstant &wg_y, const SpecializationConstant &wg_z) +{ + auto &execution = get_entry_point(); + bool builtin_workgroup = execution.workgroup_size.constant != 0; + bool use_local_size_id = !builtin_workgroup && execution.flags.get(ExecutionModeLocalSizeId); + + if (wg_x.id) + { + if (options.vulkan_semantics) + arguments.push_back(join("local_size_x_id = ", wg_x.constant_id)); + else + arguments.push_back(join("local_size_x = ", get(wg_x.id).specialization_constant_macro_name)); + } + else if (use_local_size_id && execution.workgroup_size.id_x) + arguments.push_back(join("local_size_x = ", get(execution.workgroup_size.id_x).scalar())); + else + arguments.push_back(join("local_size_x = ", execution.workgroup_size.x)); + + if (wg_y.id) + { + if (options.vulkan_semantics) + arguments.push_back(join("local_size_y_id = ", wg_y.constant_id)); + else + arguments.push_back(join("local_size_y = ", get(wg_y.id).specialization_constant_macro_name)); + } + else if (use_local_size_id && execution.workgroup_size.id_y) + arguments.push_back(join("local_size_y = ", get(execution.workgroup_size.id_y).scalar())); + else + arguments.push_back(join("local_size_y = ", execution.workgroup_size.y)); + + if (wg_z.id) + { + if (options.vulkan_semantics) + arguments.push_back(join("local_size_z_id = ", wg_z.constant_id)); + else + arguments.push_back(join("local_size_z = ", get(wg_z.id).specialization_constant_macro_name)); + } + else if (use_local_size_id && execution.workgroup_size.id_z) + arguments.push_back(join("local_size_z = ", get(execution.workgroup_size.id_z).scalar())); + else + arguments.push_back(join("local_size_z = ", execution.workgroup_size.z)); +} + +void CompilerGLSL::request_subgroup_feature(ShaderSubgroupSupportHelper::Feature feature) +{ + if (options.vulkan_semantics) + { + auto khr_extension = ShaderSubgroupSupportHelper::get_KHR_extension_for_feature(feature); + require_extension_internal(ShaderSubgroupSupportHelper::get_extension_name(khr_extension)); + } + else + { + if (!shader_subgroup_supporter.is_feature_requested(feature)) + force_recompile(); + shader_subgroup_supporter.request_feature(feature); + } +} + +void CompilerGLSL::emit_header() +{ + auto &execution = get_entry_point(); + statement("#version ", options.version, options.es && options.version > 100 ? " es" : ""); + + if (!options.es && options.version < 420) + { + // Needed for binding = # on UBOs, etc. + if (options.enable_420pack_extension) + { + statement("#ifdef GL_ARB_shading_language_420pack"); + statement("#extension GL_ARB_shading_language_420pack : require"); + statement("#endif"); + } + // Needed for: layout(early_fragment_tests) in; + if (execution.flags.get(ExecutionModeEarlyFragmentTests)) + require_extension_internal("GL_ARB_shader_image_load_store"); + } + + // Needed for: layout(post_depth_coverage) in; + if (execution.flags.get(ExecutionModePostDepthCoverage)) + require_extension_internal("GL_ARB_post_depth_coverage"); + + // Needed for: layout({pixel,sample}_interlock_[un]ordered) in; + bool interlock_used = execution.flags.get(ExecutionModePixelInterlockOrderedEXT) || + execution.flags.get(ExecutionModePixelInterlockUnorderedEXT) || + execution.flags.get(ExecutionModeSampleInterlockOrderedEXT) || + execution.flags.get(ExecutionModeSampleInterlockUnorderedEXT); + + if (interlock_used) + { + if (options.es) + { + if (options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for fragment shader interlock."); + require_extension_internal("GL_NV_fragment_shader_interlock"); + } + else + { + if (options.version < 420) + require_extension_internal("GL_ARB_shader_image_load_store"); + require_extension_internal("GL_ARB_fragment_shader_interlock"); + } + } + + for (auto &ext : forced_extensions) + { + if (ext == "GL_ARB_gpu_shader_int64") + { + statement("#if defined(GL_ARB_gpu_shader_int64)"); + statement("#extension GL_ARB_gpu_shader_int64 : require"); + if (!options.vulkan_semantics || options.es) + { + statement("#elif defined(GL_NV_gpu_shader5)"); + statement("#extension GL_NV_gpu_shader5 : require"); + } + statement("#else"); + statement("#error No extension available for 64-bit integers."); + statement("#endif"); + } + else if (ext == "GL_EXT_shader_explicit_arithmetic_types_float16") + { + // Special case, this extension has a potential fallback to another vendor extension in normal GLSL. + // GL_AMD_gpu_shader_half_float is a superset, so try that first. + statement("#if defined(GL_AMD_gpu_shader_half_float)"); + statement("#extension GL_AMD_gpu_shader_half_float : require"); + if (!options.vulkan_semantics) + { + statement("#elif defined(GL_NV_gpu_shader5)"); + statement("#extension GL_NV_gpu_shader5 : require"); + } + else + { + statement("#elif defined(GL_EXT_shader_explicit_arithmetic_types_float16)"); + statement("#extension GL_EXT_shader_explicit_arithmetic_types_float16 : require"); + } + statement("#else"); + statement("#error No extension available for FP16."); + statement("#endif"); + } + else if (ext == "GL_EXT_shader_explicit_arithmetic_types_int8") + { + if (options.vulkan_semantics) + statement("#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require"); + else + { + statement("#if defined(GL_EXT_shader_explicit_arithmetic_types_int8)"); + statement("#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require"); + statement("#elif defined(GL_NV_gpu_shader5)"); + statement("#extension GL_NV_gpu_shader5 : require"); + statement("#else"); + statement("#error No extension available for Int8."); + statement("#endif"); + } + } + else if (ext == "GL_EXT_shader_explicit_arithmetic_types_int16") + { + if (options.vulkan_semantics) + statement("#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require"); + else + { + statement("#if defined(GL_EXT_shader_explicit_arithmetic_types_int16)"); + statement("#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require"); + statement("#elif defined(GL_AMD_gpu_shader_int16)"); + statement("#extension GL_AMD_gpu_shader_int16 : require"); + statement("#elif defined(GL_NV_gpu_shader5)"); + statement("#extension GL_NV_gpu_shader5 : require"); + statement("#else"); + statement("#error No extension available for Int16."); + statement("#endif"); + } + } + else if (ext == "GL_ARB_post_depth_coverage") + { + if (options.es) + statement("#extension GL_EXT_post_depth_coverage : require"); + else + { + statement("#if defined(GL_ARB_post_depth_coverge)"); + statement("#extension GL_ARB_post_depth_coverage : require"); + statement("#else"); + statement("#extension GL_EXT_post_depth_coverage : require"); + statement("#endif"); + } + } + else if (!options.vulkan_semantics && ext == "GL_ARB_shader_draw_parameters") + { + // Soft-enable this extension on plain GLSL. + statement("#ifdef ", ext); + statement("#extension ", ext, " : enable"); + statement("#endif"); + } + else if (ext == "GL_EXT_control_flow_attributes") + { + // These are just hints so we can conditionally enable and fallback in the shader. + statement("#if defined(GL_EXT_control_flow_attributes)"); + statement("#extension GL_EXT_control_flow_attributes : require"); + statement("#define SPIRV_CROSS_FLATTEN [[flatten]]"); + statement("#define SPIRV_CROSS_BRANCH [[dont_flatten]]"); + statement("#define SPIRV_CROSS_UNROLL [[unroll]]"); + statement("#define SPIRV_CROSS_LOOP [[dont_unroll]]"); + statement("#else"); + statement("#define SPIRV_CROSS_FLATTEN"); + statement("#define SPIRV_CROSS_BRANCH"); + statement("#define SPIRV_CROSS_UNROLL"); + statement("#define SPIRV_CROSS_LOOP"); + statement("#endif"); + } + else if (ext == "GL_NV_fragment_shader_interlock") + { + statement("#extension GL_NV_fragment_shader_interlock : require"); + statement("#define SPIRV_Cross_beginInvocationInterlock() beginInvocationInterlockNV()"); + statement("#define SPIRV_Cross_endInvocationInterlock() endInvocationInterlockNV()"); + } + else if (ext == "GL_ARB_fragment_shader_interlock") + { + statement("#ifdef GL_ARB_fragment_shader_interlock"); + statement("#extension GL_ARB_fragment_shader_interlock : enable"); + statement("#define SPIRV_Cross_beginInvocationInterlock() beginInvocationInterlockARB()"); + statement("#define SPIRV_Cross_endInvocationInterlock() endInvocationInterlockARB()"); + statement("#elif defined(GL_INTEL_fragment_shader_ordering)"); + statement("#extension GL_INTEL_fragment_shader_ordering : enable"); + statement("#define SPIRV_Cross_beginInvocationInterlock() beginFragmentShaderOrderingINTEL()"); + statement("#define SPIRV_Cross_endInvocationInterlock()"); + statement("#endif"); + } + else + statement("#extension ", ext, " : require"); + } + + if (!options.vulkan_semantics) + { + using Supp = ShaderSubgroupSupportHelper; + auto result = shader_subgroup_supporter.resolve(); + + for (uint32_t feature_index = 0; feature_index < Supp::FeatureCount; feature_index++) + { + auto feature = static_cast(feature_index); + if (!shader_subgroup_supporter.is_feature_requested(feature)) + continue; + + auto exts = Supp::get_candidates_for_feature(feature, result); + if (exts.empty()) + continue; + + statement(""); + + for (auto &ext : exts) + { + const char *name = Supp::get_extension_name(ext); + const char *extra_predicate = Supp::get_extra_required_extension_predicate(ext); + auto extra_names = Supp::get_extra_required_extension_names(ext); + statement(&ext != &exts.front() ? "#elif" : "#if", " defined(", name, ")", + (*extra_predicate != '\0' ? " && " : ""), extra_predicate); + for (const auto &e : extra_names) + statement("#extension ", e, " : enable"); + statement("#extension ", name, " : require"); + } + + if (!Supp::can_feature_be_implemented_without_extensions(feature)) + { + statement("#else"); + statement("#error No extensions available to emulate requested subgroup feature."); + } + + statement("#endif"); + } + } + + for (auto &header : header_lines) + statement(header); + + SmallVector inputs; + SmallVector outputs; + + switch (execution.model) + { + case ExecutionModelVertex: + if (options.ovr_multiview_view_count) + inputs.push_back(join("num_views = ", options.ovr_multiview_view_count)); + break; + case ExecutionModelGeometry: + if ((execution.flags.get(ExecutionModeInvocations)) && execution.invocations != 1) + inputs.push_back(join("invocations = ", execution.invocations)); + if (execution.flags.get(ExecutionModeInputPoints)) + inputs.push_back("points"); + if (execution.flags.get(ExecutionModeInputLines)) + inputs.push_back("lines"); + if (execution.flags.get(ExecutionModeInputLinesAdjacency)) + inputs.push_back("lines_adjacency"); + if (execution.flags.get(ExecutionModeTriangles)) + inputs.push_back("triangles"); + if (execution.flags.get(ExecutionModeInputTrianglesAdjacency)) + inputs.push_back("triangles_adjacency"); + + if (!execution.geometry_passthrough) + { + // For passthrough, these are implies and cannot be declared in shader. + outputs.push_back(join("max_vertices = ", execution.output_vertices)); + if (execution.flags.get(ExecutionModeOutputTriangleStrip)) + outputs.push_back("triangle_strip"); + if (execution.flags.get(ExecutionModeOutputPoints)) + outputs.push_back("points"); + if (execution.flags.get(ExecutionModeOutputLineStrip)) + outputs.push_back("line_strip"); + } + break; + + case ExecutionModelTessellationControl: + if (execution.flags.get(ExecutionModeOutputVertices)) + outputs.push_back(join("vertices = ", execution.output_vertices)); + break; + + case ExecutionModelTessellationEvaluation: + if (execution.flags.get(ExecutionModeQuads)) + inputs.push_back("quads"); + if (execution.flags.get(ExecutionModeTriangles)) + inputs.push_back("triangles"); + if (execution.flags.get(ExecutionModeIsolines)) + inputs.push_back("isolines"); + if (execution.flags.get(ExecutionModePointMode)) + inputs.push_back("point_mode"); + + if (!execution.flags.get(ExecutionModeIsolines)) + { + if (execution.flags.get(ExecutionModeVertexOrderCw)) + inputs.push_back("cw"); + if (execution.flags.get(ExecutionModeVertexOrderCcw)) + inputs.push_back("ccw"); + } + + if (execution.flags.get(ExecutionModeSpacingFractionalEven)) + inputs.push_back("fractional_even_spacing"); + if (execution.flags.get(ExecutionModeSpacingFractionalOdd)) + inputs.push_back("fractional_odd_spacing"); + if (execution.flags.get(ExecutionModeSpacingEqual)) + inputs.push_back("equal_spacing"); + break; + + case ExecutionModelGLCompute: + case ExecutionModelTaskEXT: + case ExecutionModelMeshEXT: + { + if (execution.workgroup_size.constant != 0 || execution.flags.get(ExecutionModeLocalSizeId)) + { + SpecializationConstant wg_x, wg_y, wg_z; + get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + // If there are any spec constants on legacy GLSL, defer declaration, we need to set up macro + // declarations before we can emit the work group size. + if (options.vulkan_semantics || + ((wg_x.id == ConstantID(0)) && (wg_y.id == ConstantID(0)) && (wg_z.id == ConstantID(0)))) + build_workgroup_size(inputs, wg_x, wg_y, wg_z); + } + else + { + inputs.push_back(join("local_size_x = ", execution.workgroup_size.x)); + inputs.push_back(join("local_size_y = ", execution.workgroup_size.y)); + inputs.push_back(join("local_size_z = ", execution.workgroup_size.z)); + } + + if (execution.model == ExecutionModelMeshEXT) + { + outputs.push_back(join("max_vertices = ", execution.output_vertices)); + outputs.push_back(join("max_primitives = ", execution.output_primitives)); + if (execution.flags.get(ExecutionModeOutputTrianglesEXT)) + outputs.push_back("triangles"); + else if (execution.flags.get(ExecutionModeOutputLinesEXT)) + outputs.push_back("lines"); + else if (execution.flags.get(ExecutionModeOutputPoints)) + outputs.push_back("points"); + } + break; + } + + case ExecutionModelFragment: + if (options.es) + { + switch (options.fragment.default_float_precision) + { + case Options::Lowp: + statement("precision lowp float;"); + break; + + case Options::Mediump: + statement("precision mediump float;"); + break; + + case Options::Highp: + statement("precision highp float;"); + break; + + default: + break; + } + + switch (options.fragment.default_int_precision) + { + case Options::Lowp: + statement("precision lowp int;"); + break; + + case Options::Mediump: + statement("precision mediump int;"); + break; + + case Options::Highp: + statement("precision highp int;"); + break; + + default: + break; + } + } + + if (execution.flags.get(ExecutionModeEarlyFragmentTests)) + inputs.push_back("early_fragment_tests"); + if (execution.flags.get(ExecutionModePostDepthCoverage)) + inputs.push_back("post_depth_coverage"); + + if (interlock_used) + statement("#if defined(GL_ARB_fragment_shader_interlock)"); + + if (execution.flags.get(ExecutionModePixelInterlockOrderedEXT)) + statement("layout(pixel_interlock_ordered) in;"); + else if (execution.flags.get(ExecutionModePixelInterlockUnorderedEXT)) + statement("layout(pixel_interlock_unordered) in;"); + else if (execution.flags.get(ExecutionModeSampleInterlockOrderedEXT)) + statement("layout(sample_interlock_ordered) in;"); + else if (execution.flags.get(ExecutionModeSampleInterlockUnorderedEXT)) + statement("layout(sample_interlock_unordered) in;"); + + if (interlock_used) + { + statement("#elif !defined(GL_INTEL_fragment_shader_ordering)"); + statement("#error Fragment Shader Interlock/Ordering extension missing!"); + statement("#endif"); + } + + if (!options.es && execution.flags.get(ExecutionModeDepthGreater)) + statement("layout(depth_greater) out float gl_FragDepth;"); + else if (!options.es && execution.flags.get(ExecutionModeDepthLess)) + statement("layout(depth_less) out float gl_FragDepth;"); + + if (execution.flags.get(ExecutionModeRequireFullQuadsKHR)) + statement("layout(full_quads) in;"); + + break; + + default: + break; + } + + for (auto &cap : ir.declared_capabilities) + if (cap == CapabilityRayTraversalPrimitiveCullingKHR) + statement("layout(primitive_culling);"); + + if (execution.flags.get(ExecutionModeQuadDerivativesKHR)) + statement("layout(quad_derivatives) in;"); + + if (!inputs.empty()) + statement("layout(", merge(inputs), ") in;"); + if (!outputs.empty()) + statement("layout(", merge(outputs), ") out;"); + + statement(""); +} + +bool CompilerGLSL::type_is_empty(const SPIRType &type) +{ + return type.basetype == SPIRType::Struct && type.member_types.empty(); +} + +void CompilerGLSL::emit_struct(SPIRType &type) +{ + // Struct types can be stamped out multiple times + // with just different offsets, matrix layouts, etc ... + // Type-punning with these types is legal, which complicates things + // when we are storing struct and array types in an SSBO for example. + // If the type master is packed however, we can no longer assume that the struct declaration will be redundant. + if (type.type_alias != TypeID(0) && + !has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + return; + + add_resource_name(type.self); + auto name = type_to_glsl(type); + + statement(!backend.explicit_struct_type ? "struct " : "", name); + begin_scope(); + + type.member_name_cache.clear(); + + uint32_t i = 0; + bool emitted = false; + for (auto &member : type.member_types) + { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + emitted = true; + } + + // Don't declare empty structs in GLSL, this is not allowed. + if (type_is_empty(type) && !backend.supports_empty_struct) + { + statement("int empty_struct_member;"); + emitted = true; + } + + end_scope_decl(); + + if (emitted) + statement(""); +} + +string CompilerGLSL::to_interpolation_qualifiers(const Bitset &flags) +{ + string res; + //if (flags & (1ull << DecorationSmooth)) + // res += "smooth "; + if (flags.get(DecorationFlat)) + res += "flat "; + if (flags.get(DecorationNoPerspective)) + { + if (options.es) + { + if (options.version < 300) + SPIRV_CROSS_THROW("noperspective requires ESSL 300."); + require_extension_internal("GL_NV_shader_noperspective_interpolation"); + } + else if (is_legacy_desktop()) + require_extension_internal("GL_EXT_gpu_shader4"); + res += "noperspective "; + } + if (flags.get(DecorationCentroid)) + res += "centroid "; + if (flags.get(DecorationPatch)) + res += "patch "; + if (flags.get(DecorationSample)) + { + if (options.es) + { + if (options.version < 300) + SPIRV_CROSS_THROW("sample requires ESSL 300."); + else if (options.version < 320) + require_extension_internal("GL_OES_shader_multisample_interpolation"); + } + res += "sample "; + } + if (flags.get(DecorationInvariant) && (options.es || options.version >= 120)) + res += "invariant "; + if (flags.get(DecorationPerPrimitiveEXT)) + { + res += "perprimitiveEXT "; + require_extension_internal("GL_EXT_mesh_shader"); + } + + if (flags.get(DecorationExplicitInterpAMD)) + { + require_extension_internal("GL_AMD_shader_explicit_vertex_parameter"); + res += "__explicitInterpAMD "; + } + + if (flags.get(DecorationPerVertexKHR)) + { + if (options.es && options.version < 320) + SPIRV_CROSS_THROW("pervertexEXT requires ESSL 320."); + else if (!options.es && options.version < 450) + SPIRV_CROSS_THROW("pervertexEXT requires GLSL 450."); + + if (barycentric_is_nv) + { + require_extension_internal("GL_NV_fragment_shader_barycentric"); + res += "pervertexNV "; + } + else + { + require_extension_internal("GL_EXT_fragment_shader_barycentric"); + res += "pervertexEXT "; + } + } + + return res; +} + +string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index) +{ + if (is_legacy()) + return ""; + + bool is_block = has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + if (!is_block) + return ""; + + auto &memb = ir.meta[type.self].members; + if (index >= memb.size()) + return ""; + auto &dec = memb[index]; + + SmallVector attr; + + if (has_member_decoration(type.self, index, DecorationPassthroughNV)) + attr.push_back("passthrough"); + + // We can only apply layouts on members in block interfaces. + // This is a bit problematic because in SPIR-V decorations are applied on the struct types directly. + // This is not supported on GLSL, so we have to make the assumption that if a struct within our buffer block struct + // has a decoration, it was originally caused by a top-level layout() qualifier in GLSL. + // + // We would like to go from (SPIR-V style): + // + // struct Foo { layout(row_major) mat4 matrix; }; + // buffer UBO { Foo foo; }; + // + // to + // + // struct Foo { mat4 matrix; }; // GLSL doesn't support any layout shenanigans in raw struct declarations. + // buffer UBO { layout(row_major) Foo foo; }; // Apply the layout on top-level. + auto flags = combined_decoration_for_member(type, index); + + if (flags.get(DecorationRowMajor)) + attr.push_back("row_major"); + // We don't emit any global layouts, so column_major is default. + //if (flags & (1ull << DecorationColMajor)) + // attr.push_back("column_major"); + + if (dec.decoration_flags.get(DecorationLocation) && can_use_io_location(type.storage, true)) + attr.push_back(join("location = ", dec.location)); + + // Can only declare component if we can declare location. + if (dec.decoration_flags.get(DecorationComponent) && can_use_io_location(type.storage, true)) + { + if (!options.es) + { + if (options.version < 440 && options.version >= 140) + require_extension_internal("GL_ARB_enhanced_layouts"); + else if (options.version < 140) + SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40."); + attr.push_back(join("component = ", dec.component)); + } + else + SPIRV_CROSS_THROW("Component decoration is not supported in ES targets."); + } + + // SPIRVCrossDecorationPacked is set by layout_for_variable earlier to mark that we need to emit offset qualifiers. + // This is only done selectively in GLSL as needed. + if (has_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset) && + dec.decoration_flags.get(DecorationOffset)) + attr.push_back(join("offset = ", dec.offset)); + else if (type.storage == StorageClassOutput && dec.decoration_flags.get(DecorationOffset)) + attr.push_back(join("xfb_offset = ", dec.offset)); + + if (attr.empty()) + return ""; + + string res = "layout("; + res += merge(attr); + res += ") "; + return res; +} + +const char *CompilerGLSL::format_to_glsl(ImageFormat format) +{ + if (options.es && is_desktop_only_format(format)) + SPIRV_CROSS_THROW("Attempting to use image format not supported in ES profile."); + + switch (format) + { + case ImageFormatRgba32f: + return "rgba32f"; + case ImageFormatRgba16f: + return "rgba16f"; + case ImageFormatR32f: + return "r32f"; + case ImageFormatRgba8: + return "rgba8"; + case ImageFormatRgba8Snorm: + return "rgba8_snorm"; + case ImageFormatRg32f: + return "rg32f"; + case ImageFormatRg16f: + return "rg16f"; + case ImageFormatRgba32i: + return "rgba32i"; + case ImageFormatRgba16i: + return "rgba16i"; + case ImageFormatR32i: + return "r32i"; + case ImageFormatRgba8i: + return "rgba8i"; + case ImageFormatRg32i: + return "rg32i"; + case ImageFormatRg16i: + return "rg16i"; + case ImageFormatRgba32ui: + return "rgba32ui"; + case ImageFormatRgba16ui: + return "rgba16ui"; + case ImageFormatR32ui: + return "r32ui"; + case ImageFormatRgba8ui: + return "rgba8ui"; + case ImageFormatRg32ui: + return "rg32ui"; + case ImageFormatRg16ui: + return "rg16ui"; + case ImageFormatR11fG11fB10f: + return "r11f_g11f_b10f"; + case ImageFormatR16f: + return "r16f"; + case ImageFormatRgb10A2: + return "rgb10_a2"; + case ImageFormatR8: + return "r8"; + case ImageFormatRg8: + return "rg8"; + case ImageFormatR16: + return "r16"; + case ImageFormatRg16: + return "rg16"; + case ImageFormatRgba16: + return "rgba16"; + case ImageFormatR16Snorm: + return "r16_snorm"; + case ImageFormatRg16Snorm: + return "rg16_snorm"; + case ImageFormatRgba16Snorm: + return "rgba16_snorm"; + case ImageFormatR8Snorm: + return "r8_snorm"; + case ImageFormatRg8Snorm: + return "rg8_snorm"; + case ImageFormatR8ui: + return "r8ui"; + case ImageFormatRg8ui: + return "rg8ui"; + case ImageFormatR16ui: + return "r16ui"; + case ImageFormatRgb10a2ui: + return "rgb10_a2ui"; + case ImageFormatR8i: + return "r8i"; + case ImageFormatRg8i: + return "rg8i"; + case ImageFormatR16i: + return "r16i"; + case ImageFormatR64i: + return "r64i"; + case ImageFormatR64ui: + return "r64ui"; + default: + case ImageFormatUnknown: + return nullptr; + } +} + +uint32_t CompilerGLSL::type_to_packed_base_size(const SPIRType &type, BufferPackingStandard) +{ + switch (type.basetype) + { + case SPIRType::Double: + case SPIRType::Int64: + case SPIRType::UInt64: + return 8; + case SPIRType::Float: + case SPIRType::Int: + case SPIRType::UInt: + return 4; + case SPIRType::Half: + case SPIRType::Short: + case SPIRType::UShort: + case SPIRType::BFloat16: + return 2; + case SPIRType::SByte: + case SPIRType::UByte: + case SPIRType::FloatE4M3: + case SPIRType::FloatE5M2: + return 1; + + default: + SPIRV_CROSS_THROW("Unrecognized type in type_to_packed_base_size."); + } +} + +uint32_t CompilerGLSL::type_to_packed_alignment(const SPIRType &type, const Bitset &flags, + BufferPackingStandard packing) +{ + // If using PhysicalStorageBuffer storage class, this is a pointer, + // and is 64-bit. + if (is_physical_pointer(type)) + { + if (!type.pointer) + SPIRV_CROSS_THROW("Types in PhysicalStorageBuffer must be pointers."); + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64) + { + if (packing_is_vec4_padded(packing) && type_is_array_of_pointers(type)) + return 16; + else + return 8; + } + else + SPIRV_CROSS_THROW("AddressingModelPhysicalStorageBuffer64 must be used for PhysicalStorageBuffer."); + } + else if (is_array(type)) + { + uint32_t minimum_alignment = 1; + if (packing_is_vec4_padded(packing)) + minimum_alignment = 16; + + auto *tmp = &get(type.parent_type); + while (!tmp->array.empty()) + tmp = &get(tmp->parent_type); + + // Get the alignment of the base type, then maybe round up. + return max(minimum_alignment, type_to_packed_alignment(*tmp, flags, packing)); + } + + if (type.basetype == SPIRType::Struct) + { + // Rule 9. Structs alignments are maximum alignment of its members. + uint32_t alignment = 1; + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + auto member_flags = ir.meta[type.self].members[i].decoration_flags; + alignment = + max(alignment, type_to_packed_alignment(get(type.member_types[i]), member_flags, packing)); + } + + // In std140, struct alignment is rounded up to 16. + if (packing_is_vec4_padded(packing)) + alignment = max(alignment, 16u); + + return alignment; + } + else + { + const uint32_t base_alignment = type_to_packed_base_size(type, packing); + + // Alignment requirement for scalar block layout is always the alignment for the most basic component. + if (packing_is_scalar(packing)) + return base_alignment; + + // Vectors are *not* aligned in HLSL, but there's an extra rule where vectors cannot straddle + // a vec4, this is handled outside since that part knows our current offset. + if (type.columns == 1 && packing_is_hlsl(packing)) + return base_alignment; + + // From 7.6.2.2 in GL 4.5 core spec. + // Rule 1 + if (type.vecsize == 1 && type.columns == 1) + return base_alignment; + + // Rule 2 + if ((type.vecsize == 2 || type.vecsize == 4) && type.columns == 1) + return type.vecsize * base_alignment; + + // Rule 3 + if (type.vecsize == 3 && type.columns == 1) + return 4 * base_alignment; + + // Rule 4 implied. Alignment does not change in std430. + + // Rule 5. Column-major matrices are stored as arrays of + // vectors. + if (flags.get(DecorationColMajor) && type.columns > 1) + { + if (packing_is_vec4_padded(packing)) + return 4 * base_alignment; + else if (type.vecsize == 3) + return 4 * base_alignment; + else + return type.vecsize * base_alignment; + } + + // Rule 6 implied. + + // Rule 7. + if (flags.get(DecorationRowMajor) && type.vecsize > 1) + { + if (packing_is_vec4_padded(packing)) + return 4 * base_alignment; + else if (type.columns == 3) + return 4 * base_alignment; + else + return type.columns * base_alignment; + } + + // Rule 8 implied. + } + + SPIRV_CROSS_THROW("Did not find suitable rule for type. Bogus decorations?"); +} + +uint32_t CompilerGLSL::type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, + BufferPackingStandard packing) +{ + // Array stride is equal to aligned size of the underlying type. + uint32_t parent = type.parent_type; + assert(parent); + + auto &tmp = get(parent); + + uint32_t size = type_to_packed_size(tmp, flags, packing); + uint32_t alignment = type_to_packed_alignment(type, flags, packing); + return (size + alignment - 1) & ~(alignment - 1); +} + +uint32_t CompilerGLSL::type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing) +{ + // If using PhysicalStorageBuffer storage class, this is a pointer, + // and is 64-bit. + if (is_physical_pointer(type)) + { + if (!type.pointer) + SPIRV_CROSS_THROW("Types in PhysicalStorageBuffer must be pointers."); + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64) + return 8; + else + SPIRV_CROSS_THROW("AddressingModelPhysicalStorageBuffer64 must be used for PhysicalStorageBuffer."); + } + else if (is_array(type)) + { + uint32_t packed_size = to_array_size_literal(type) * type_to_packed_array_stride(type, flags, packing); + + if (packing_is_hlsl(packing)) + { + // For arrays of vectors and matrices in HLSL, the last element has a size which depends on its vector size, + // so that it is possible to pack other vectors into the last element. + if (type.basetype != SPIRType::Struct) + { + if (flags.get(DecorationRowMajor) && type.columns > 1) + packed_size -= (4 - type.columns) * (type.width / 8); + else + packed_size -= (4 - type.vecsize) * (type.width / 8); + } + else + { + const auto *base_type = &type; + while (is_array(*base_type)) + { + auto &new_type = get(base_type->parent_type); + if (!is_array(new_type)) + break; + base_type = &new_type; + } + + packed_size -= type_to_packed_array_stride(*base_type, flags, packing); + packed_size += type_to_packed_size(get(base_type->parent_type), flags, packing); + } + } + + return packed_size; + } + + uint32_t size = 0; + + if (type.basetype == SPIRType::Struct) + { + uint32_t pad_alignment = 1; + + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + auto member_flags = ir.meta[type.self].members[i].decoration_flags; + auto &member_type = get(type.member_types[i]); + + uint32_t packed_alignment = type_to_packed_alignment(member_type, member_flags, packing); + uint32_t alignment = max(packed_alignment, pad_alignment); + + uint32_t element_size = type_to_packed_size(member_type, member_flags, packing); + pad_alignment = 1; + + if (packing_is_hlsl(packing)) + { + // HLSL is primarily a "cannot-straddle-vec4" language. + uint32_t begin_word = size / 16; + uint32_t end_word = (size + element_size - 1) / 16; + if (begin_word != end_word) + alignment = max(alignment, 16u); + } + else + { + // The next member following a struct member is aligned to the base alignment of the struct that came before. + // GL 4.5 spec, 7.6.2.2. + if (member_type.basetype == SPIRType::Struct) + pad_alignment = packed_alignment; + } + + size = (size + alignment - 1) & ~(alignment - 1); + size += element_size; + } + } + else + { + const uint32_t base_alignment = type_to_packed_base_size(type, packing); + + if (packing_is_scalar(packing)) + { + size = type.vecsize * type.columns * base_alignment; + } + else + { + if (type.columns == 1) + size = type.vecsize * base_alignment; + + if (flags.get(DecorationColMajor) && type.columns > 1) + { + if (packing_is_vec4_padded(packing) || type.vecsize == 3) + size = type.columns * 4 * base_alignment; + else + size = type.columns * type.vecsize * base_alignment; + } + + if (flags.get(DecorationRowMajor) && type.vecsize > 1) + { + if (packing_is_vec4_padded(packing) || type.columns == 3) + size = type.vecsize * 4 * base_alignment; + else + size = type.vecsize * type.columns * base_alignment; + } + + // For matrices in HLSL, the last element has a size which depends on its vector size, + // so that it is possible to pack other vectors into the last element. + if (packing_is_hlsl(packing) && type.columns > 1) + { + if (flags.get(DecorationRowMajor)) + size -= (4 - type.columns) * (type.width / 8); + else + size -= (4 - type.vecsize) * (type.width / 8); + } + } + } + + return size; +} + +bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, + uint32_t *failed_validation_index, uint32_t start_offset, + uint32_t end_offset) +{ + // This is very tricky and error prone, but try to be exhaustive and correct here. + // SPIR-V doesn't directly say if we're using std430 or std140. + // SPIR-V communicates this using Offset and ArrayStride decorations (which is what really matters), + // so we have to try to infer whether or not the original GLSL source was std140 or std430 based on this information. + // We do not have to consider shared or packed since these layouts are not allowed in Vulkan SPIR-V (they are useless anyways, and custom offsets would do the same thing). + // + // It is almost certain that we're using std430, but it gets tricky with arrays in particular. + // We will assume std430, but infer std140 if we can prove the struct is not compliant with std430. + // + // The only two differences between std140 and std430 are related to padding alignment/array stride + // in arrays and structs. In std140 they take minimum vec4 alignment. + // std430 only removes the vec4 requirement. + + uint32_t offset = 0; + uint32_t pad_alignment = 1; + + bool is_top_level_block = + has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + auto &memb_type = get(type.member_types[i]); + + auto *type_meta = ir.find_meta(type.self); + auto member_flags = type_meta ? type_meta->members[i].decoration_flags : Bitset{}; + + // Verify alignment rules. + uint32_t packed_alignment = type_to_packed_alignment(memb_type, member_flags, packing); + + // This is a rather dirty workaround to deal with some cases of OpSpecConstantOp used as array size, e.g: + // layout(constant_id = 0) const int s = 10; + // const int S = s + 5; // SpecConstantOp + // buffer Foo { int data[S]; }; // <-- Very hard for us to deduce a fixed value here, + // we would need full implementation of compile-time constant folding. :( + // If we are the last member of a struct, there might be cases where the actual size of that member is irrelevant + // for our analysis (e.g. unsized arrays). + // This lets us simply ignore that there are spec constant op sized arrays in our buffers. + // Querying size of this member will fail, so just don't call it unless we have to. + // + // This is likely "best effort" we can support without going into unacceptably complicated workarounds. + bool member_can_be_unsized = + is_top_level_block && size_t(i + 1) == type.member_types.size() && !memb_type.array.empty(); + + uint32_t packed_size = 0; + if (!member_can_be_unsized || packing_is_hlsl(packing)) + packed_size = type_to_packed_size(memb_type, member_flags, packing); + + // We only need to care about this if we have non-array types which can straddle the vec4 boundary. + uint32_t actual_offset = type_struct_member_offset(type, i); + + if (packing_is_hlsl(packing)) + { + // If a member straddles across a vec4 boundary, alignment is actually vec4. + uint32_t target_offset; + + // If we intend to use explicit packing, we must check for improper straddle with that offset. + // In implicit packing, we must check with implicit offset, since the explicit offset + // might have already accounted for the straddle, and we'd miss the alignment promotion to vec4. + // This is important when packing sub-structs that don't support packoffset(). + if (packing_has_flexible_offset(packing)) + target_offset = actual_offset; + else + target_offset = offset; + + uint32_t begin_word = target_offset / 16; + uint32_t end_word = (target_offset + packed_size - 1) / 16; + + if (begin_word != end_word) + packed_alignment = max(packed_alignment, 16u); + } + + // Field is not in the specified range anymore and we can ignore any further fields. + if (actual_offset >= end_offset) + break; + + uint32_t alignment = max(packed_alignment, pad_alignment); + offset = (offset + alignment - 1) & ~(alignment - 1); + + // The next member following a struct member is aligned to the base alignment of the struct that came before. + // GL 4.5 spec, 7.6.2.2. + if (!packing_is_hlsl(packing) && memb_type.basetype == SPIRType::Struct && !memb_type.pointer) + pad_alignment = packed_alignment; + else + pad_alignment = 1; + + // Only care about packing if we are in the given range + if (actual_offset >= start_offset) + { + // We only care about offsets in std140, std430, etc ... + // For EnhancedLayout variants, we have the flexibility to choose our own offsets. + if (!packing_has_flexible_offset(packing)) + { + if (actual_offset != offset) // This cannot be the packing we're looking for. + { + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + } + else if ((actual_offset & (alignment - 1)) != 0) + { + // We still need to verify that alignment rules are observed, even if we have explicit offset. + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + + // Verify array stride rules. + if (is_array(memb_type)) + { + auto packed_array_stride = type_to_packed_array_stride(memb_type, member_flags, packing); + auto member_array_stride = type_struct_member_array_stride(type, i); + if (packed_array_stride != member_array_stride) + { + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + } + + // Verify that sub-structs also follow packing rules. + // We cannot use enhanced layouts on substructs, so they better be up to spec. + auto substruct_packing = packing_to_substruct_packing(packing); + + if (!memb_type.pointer && !memb_type.member_types.empty() && + !buffer_is_packing_standard(memb_type, substruct_packing)) + { + if (failed_validation_index) + *failed_validation_index = i; + return false; + } + } + + // Bump size. + offset = actual_offset + packed_size; + } + + return true; +} + +bool CompilerGLSL::can_use_io_location(StorageClass storage, bool block) +{ + // Location specifiers are must have in SPIR-V, but they aren't really supported in earlier versions of GLSL. + // Be very explicit here about how to solve the issue. + if ((get_execution_model() != ExecutionModelVertex && storage == StorageClassInput) || + (get_execution_model() != ExecutionModelFragment && storage == StorageClassOutput)) + { + uint32_t minimum_desktop_version = block ? 440 : 410; + // ARB_enhanced_layouts vs ARB_separate_shader_objects ... + + if (!options.es && options.version < minimum_desktop_version && !options.separate_shader_objects) + return false; + else if (options.es && options.version < 310) + return false; + } + + if ((get_execution_model() == ExecutionModelVertex && storage == StorageClassInput) || + (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput)) + { + if (options.es && options.version < 300) + return false; + else if (!options.es && options.version < 330) + return false; + } + + if (storage == StorageClassUniform || storage == StorageClassUniformConstant || storage == StorageClassPushConstant) + { + if (options.es && options.version < 310) + return false; + else if (!options.es && options.version < 430) + return false; + } + + return true; +} + +string CompilerGLSL::layout_for_variable(const SPIRVariable &var) +{ + // FIXME: Come up with a better solution for when to disable layouts. + // Having layouts depend on extensions as well as which types + // of layouts are used. For now, the simple solution is to just disable + // layouts for legacy versions. + if (is_legacy()) + return ""; + + if (subpass_input_is_framebuffer_fetch(var.self)) + return ""; + + SmallVector attr; + + auto &type = get(var.basetype); + auto &flags = get_decoration_bitset(var.self); + auto &typeflags = get_decoration_bitset(type.self); + + if (flags.get(DecorationPassthroughNV)) + attr.push_back("passthrough"); + + if (options.vulkan_semantics && var.storage == StorageClassPushConstant) + attr.push_back("push_constant"); + else if (var.storage == StorageClassShaderRecordBufferKHR) + attr.push_back(ray_tracing_is_khr ? "shaderRecordEXT" : "shaderRecordNV"); + + if (flags.get(DecorationRowMajor)) + attr.push_back("row_major"); + if (flags.get(DecorationColMajor)) + attr.push_back("column_major"); + + if (options.vulkan_semantics) + { + if (flags.get(DecorationInputAttachmentIndex)) + attr.push_back(join("input_attachment_index = ", get_decoration(var.self, DecorationInputAttachmentIndex))); + } + + bool is_block = has_decoration(type.self, DecorationBlock); + if (flags.get(DecorationLocation) && can_use_io_location(var.storage, is_block)) + { + Bitset combined_decoration; + for (uint32_t i = 0; i < ir.meta[type.self].members.size(); i++) + combined_decoration.merge_or(combined_decoration_for_member(type, i)); + + // If our members have location decorations, we don't need to + // emit location decorations at the top as well (looks weird). + if (!combined_decoration.get(DecorationLocation)) + attr.push_back(join("location = ", get_decoration(var.self, DecorationLocation))); + } + + if (get_execution_model() == ExecutionModelFragment && var.storage == StorageClassOutput && + location_is_non_coherent_framebuffer_fetch(get_decoration(var.self, DecorationLocation))) + { + attr.push_back("noncoherent"); + } + + // Transform feedback + bool uses_enhanced_layouts = false; + if (is_block && var.storage == StorageClassOutput) + { + // For blocks, there is a restriction where xfb_stride/xfb_buffer must only be declared on the block itself, + // since all members must match the same xfb_buffer. The only thing we will declare for members of the block + // is the xfb_offset. + uint32_t member_count = uint32_t(type.member_types.size()); + bool have_xfb_buffer_stride = false; + bool have_any_xfb_offset = false; + bool have_geom_stream = false; + uint32_t xfb_stride = 0, xfb_buffer = 0, geom_stream = 0; + + if (flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride)) + { + have_xfb_buffer_stride = true; + xfb_buffer = get_decoration(var.self, DecorationXfbBuffer); + xfb_stride = get_decoration(var.self, DecorationXfbStride); + } + + if (flags.get(DecorationStream)) + { + have_geom_stream = true; + geom_stream = get_decoration(var.self, DecorationStream); + } + + // Verify that none of the members violate our assumption. + for (uint32_t i = 0; i < member_count; i++) + { + if (has_member_decoration(type.self, i, DecorationStream)) + { + uint32_t member_geom_stream = get_member_decoration(type.self, i, DecorationStream); + if (have_geom_stream && member_geom_stream != geom_stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = member_geom_stream; + } + + // Only members with an Offset decoration participate in XFB. + if (!has_member_decoration(type.self, i, DecorationOffset)) + continue; + have_any_xfb_offset = true; + + if (has_member_decoration(type.self, i, DecorationXfbBuffer)) + { + uint32_t buffer_index = get_member_decoration(type.self, i, DecorationXfbBuffer); + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + } + + if (has_member_decoration(type.self, i, DecorationXfbStride)) + { + uint32_t stride = get_member_decoration(type.self, i, DecorationXfbStride); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbStride mismatch."); + have_xfb_buffer_stride = true; + xfb_stride = stride; + } + } + + if (have_xfb_buffer_stride && have_any_xfb_offset) + { + attr.push_back(join("xfb_buffer = ", xfb_buffer)); + attr.push_back(join("xfb_stride = ", xfb_stride)); + uses_enhanced_layouts = true; + } + + if (have_geom_stream) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", get_decoration(var.self, DecorationStream))); + } + } + else if (var.storage == StorageClassOutput) + { + if (flags.get(DecorationXfbBuffer) && flags.get(DecorationXfbStride) && flags.get(DecorationOffset)) + { + // XFB for standalone variables, we can emit all decorations. + attr.push_back(join("xfb_buffer = ", get_decoration(var.self, DecorationXfbBuffer))); + attr.push_back(join("xfb_stride = ", get_decoration(var.self, DecorationXfbStride))); + attr.push_back(join("xfb_offset = ", get_decoration(var.self, DecorationOffset))); + uses_enhanced_layouts = true; + } + + if (flags.get(DecorationStream)) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", get_decoration(var.self, DecorationStream))); + } + } + + // Can only declare Component if we can declare location. + if (flags.get(DecorationComponent) && can_use_io_location(var.storage, is_block)) + { + uses_enhanced_layouts = true; + attr.push_back(join("component = ", get_decoration(var.self, DecorationComponent))); + } + + if (uses_enhanced_layouts) + { + if (!options.es) + { + if (options.version < 440 && options.version >= 140) + require_extension_internal("GL_ARB_enhanced_layouts"); + else if (options.version < 140) + SPIRV_CROSS_THROW("GL_ARB_enhanced_layouts is not supported in targets below GLSL 1.40."); + if (!options.es && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + } + else if (options.es) + SPIRV_CROSS_THROW("GL_ARB_enhanced_layouts is not supported in ESSL."); + } + + if (flags.get(DecorationIndex)) + attr.push_back(join("index = ", get_decoration(var.self, DecorationIndex))); + + // Do not emit set = decoration in regular GLSL output, but + // we need to preserve it in Vulkan GLSL mode. + if (var.storage != StorageClassPushConstant && var.storage != StorageClassShaderRecordBufferKHR) + { + if (flags.get(DecorationDescriptorSet) && options.vulkan_semantics) + attr.push_back(join("set = ", get_decoration(var.self, DecorationDescriptorSet))); + } + + bool push_constant_block = options.vulkan_semantics && var.storage == StorageClassPushConstant; + bool ssbo_block = var.storage == StorageClassStorageBuffer || var.storage == StorageClassShaderRecordBufferKHR || + (var.storage == StorageClassUniform && typeflags.get(DecorationBufferBlock)); + bool emulated_ubo = var.storage == StorageClassPushConstant && options.emit_push_constant_as_uniform_buffer; + bool ubo_block = var.storage == StorageClassUniform && typeflags.get(DecorationBlock); + + // GL 3.0/GLSL 1.30 is not considered legacy, but it doesn't have UBOs ... + bool can_use_buffer_blocks = (options.es && options.version >= 300) || (!options.es && options.version >= 140); + + // pretend no UBOs when options say so + if (ubo_block && options.emit_uniform_buffer_as_plain_uniforms) + can_use_buffer_blocks = false; + + bool can_use_binding; + if (options.es) + can_use_binding = options.version >= 310; + else + can_use_binding = options.enable_420pack_extension || (options.version >= 420); + + // Make sure we don't emit binding layout for a classic uniform on GLSL 1.30. + if (!can_use_buffer_blocks && var.storage == StorageClassUniform) + can_use_binding = false; + + if (var.storage == StorageClassShaderRecordBufferKHR) + can_use_binding = false; + + if (can_use_binding && flags.get(DecorationBinding)) + attr.push_back(join("binding = ", get_decoration(var.self, DecorationBinding))); + + if (var.storage != StorageClassOutput && flags.get(DecorationOffset)) + attr.push_back(join("offset = ", get_decoration(var.self, DecorationOffset))); + + // Instead of adding explicit offsets for every element here, just assume we're using std140 or std430. + // If SPIR-V does not comply with either layout, we cannot really work around it. + if (can_use_buffer_blocks && (ubo_block || emulated_ubo)) + { + attr.push_back(buffer_to_packing_standard(type, false, true)); + } + else if (can_use_buffer_blocks && (push_constant_block || ssbo_block)) + { + attr.push_back(buffer_to_packing_standard(type, true, true)); + } + + // 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) + attr.push_back(fmt); + } + + if (attr.empty()) + return ""; + + string res = "layout("; + res += merge(attr); + res += ") "; + return res; +} + +string CompilerGLSL::buffer_to_packing_standard(const SPIRType &type, + bool support_std430_without_scalar_layout, + bool support_enhanced_layouts) +{ + if (support_std430_without_scalar_layout && buffer_is_packing_standard(type, BufferPackingStd430)) + return "std430"; + else if (buffer_is_packing_standard(type, BufferPackingStd140)) + return "std140"; + else if (options.vulkan_semantics && buffer_is_packing_standard(type, BufferPackingScalar)) + { + require_extension_internal("GL_EXT_scalar_block_layout"); + return "scalar"; + } + else if (support_std430_without_scalar_layout && + support_enhanced_layouts && + buffer_is_packing_standard(type, BufferPackingStd430EnhancedLayout)) + { + if (options.es && !options.vulkan_semantics) + SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do " + "not support GL_ARB_enhanced_layouts."); + if (!options.es && !options.vulkan_semantics && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + return "std430"; + } + else if (support_enhanced_layouts && + buffer_is_packing_standard(type, BufferPackingStd140EnhancedLayout)) + { + // Fallback time. We might be able to use the ARB_enhanced_layouts to deal with this difference, + // however, we can only use layout(offset) on the block itself, not any substructs, so the substructs better be the appropriate layout. + // Enhanced layouts seem to always work in Vulkan GLSL, so no need for extensions there. + if (options.es && !options.vulkan_semantics) + SPIRV_CROSS_THROW("Push constant block cannot be expressed as neither std430 nor std140. ES-targets do " + "not support GL_ARB_enhanced_layouts."); + if (!options.es && !options.vulkan_semantics && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + return "std140"; + } + else if (options.vulkan_semantics && + support_enhanced_layouts && + buffer_is_packing_standard(type, BufferPackingScalarEnhancedLayout)) + { + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + require_extension_internal("GL_EXT_scalar_block_layout"); + return "scalar"; + } + else if (!support_std430_without_scalar_layout && options.vulkan_semantics && + buffer_is_packing_standard(type, BufferPackingStd430)) + { + // UBOs can support std430 with GL_EXT_scalar_block_layout. + require_extension_internal("GL_EXT_scalar_block_layout"); + return "std430"; + } + else if (!support_std430_without_scalar_layout && options.vulkan_semantics && + support_enhanced_layouts && + buffer_is_packing_standard(type, BufferPackingStd430EnhancedLayout)) + { + // UBOs can support std430 with GL_EXT_scalar_block_layout. + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + require_extension_internal("GL_EXT_scalar_block_layout"); + return "std430"; + } + else + { + SPIRV_CROSS_THROW("Buffer block cannot be expressed as any of std430, std140, scalar, even with enhanced " + "layouts. You can try flattening this block to support a more flexible layout."); + } +} + +void CompilerGLSL::emit_push_constant_block(const SPIRVariable &var) +{ + if (flattened_buffer_blocks.count(var.self)) + emit_buffer_block_flattened(var); + else if (options.vulkan_semantics) + emit_push_constant_block_vulkan(var); + else if (options.emit_push_constant_as_uniform_buffer) + emit_buffer_block_native(&var, nullptr); + else + emit_push_constant_block_glsl(var); +} + +void CompilerGLSL::emit_push_constant_block_vulkan(const SPIRVariable &var) +{ + emit_buffer_block(var); +} + +void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var) +{ + // OpenGL has no concept of push constant blocks, implement it as a uniform struct. + auto &type = get(var.basetype); + + unset_decoration(var.self, DecorationBinding); + unset_decoration(var.self, DecorationDescriptorSet); + +#if 0 + if (flags & ((1ull << DecorationBinding) | (1ull << DecorationDescriptorSet))) + SPIRV_CROSS_THROW("Push constant blocks cannot be compiled to GLSL with Binding or Set syntax. " + "Remap to location with reflection API first or disable these decorations."); +#endif + + // We're emitting the push constant block as a regular struct, so disable the block qualifier temporarily. + // Otherwise, we will end up emitting layout() qualifiers on naked structs which is not allowed. + bool block_flag = has_decoration(type.self, DecorationBlock); + unset_decoration(type.self, DecorationBlock); + + emit_struct(type); + + if (block_flag) + set_decoration(type.self, DecorationBlock); + + emit_uniform(var); + statement(""); +} + +void CompilerGLSL::emit_buffer_block(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + bool ubo_block = var.storage == StorageClassUniform && has_decoration(type.self, DecorationBlock); + + if (flattened_buffer_blocks.count(var.self)) + emit_buffer_block_flattened(var); + else if (is_legacy() || (!options.es && options.version == 130) || + (ubo_block && options.emit_uniform_buffer_as_plain_uniforms)) + emit_buffer_block_legacy(var); + else + emit_buffer_block_native(&var, nullptr); +} + +void CompilerGLSL::emit_buffer_block_legacy(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + bool ssbo = var.storage == StorageClassStorageBuffer || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + if (ssbo) + SPIRV_CROSS_THROW("SSBOs not supported in legacy targets."); + + // We're emitting the push constant block as a regular struct, so disable the block qualifier temporarily. + // Otherwise, we will end up emitting layout() qualifiers on naked structs which is not allowed. + auto &block_flags = ir.meta[type.self].decoration.decoration_flags; + bool block_flag = block_flags.get(DecorationBlock); + block_flags.clear(DecorationBlock); + emit_struct(type); + if (block_flag) + block_flags.set(DecorationBlock); + emit_uniform(var); + statement(""); +} + +void CompilerGLSL::emit_buffer_reference_block(uint32_t type_id, bool forward_declaration) +{ + auto &type = get(type_id); + string buffer_name; + + if (forward_declaration && is_physical_pointer_to_buffer_block(type)) + { + // Block names should never alias, but from HLSL input they kind of can because block types are reused for UAVs ... + // Allow aliased name since we might be declaring the block twice. Once with buffer reference (forward declared) and one proper declaration. + // The names must match up. + buffer_name = to_name(type.self, false); + + // Shaders never use the block by interface name, so we don't + // have to track this other than updating name caches. + // If we have a collision for any reason, just fallback immediately. + if (ir.meta[type.self].decoration.alias.empty() || + block_ssbo_names.find(buffer_name) != end(block_ssbo_names) || + resource_names.find(buffer_name) != end(resource_names)) + { + buffer_name = join("_", type.self); + } + + // Make sure we get something unique for both global name scope and block name scope. + // See GLSL 4.5 spec: section 4.3.9 for details. + add_variable(block_ssbo_names, resource_names, buffer_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + // We cannot reuse this fallback name in neither global scope (blocked by block_names) nor block name scope. + if (buffer_name.empty()) + buffer_name = join("_", type.self); + + block_names.insert(buffer_name); + block_ssbo_names.insert(buffer_name); + + // Ensure we emit the correct name when emitting non-forward pointer type. + ir.meta[type.self].decoration.alias = buffer_name; + } + else + { + buffer_name = type_to_glsl(type); + } + + if (!forward_declaration) + { + auto itr = physical_storage_type_to_alignment.find(type_id); + uint32_t alignment = 0; + if (itr != physical_storage_type_to_alignment.end()) + alignment = itr->second.alignment; + + if (is_physical_pointer_to_buffer_block(type)) + { + SmallVector attributes; + attributes.push_back("buffer_reference"); + if (alignment) + attributes.push_back(join("buffer_reference_align = ", alignment)); + attributes.push_back(buffer_to_packing_standard(type, true, true)); + + auto flags = ir.get_buffer_block_type_flags(type); + string decorations; + if (flags.get(DecorationRestrict)) + decorations += " restrict"; + if (flags.get(DecorationCoherent)) + decorations += " coherent"; + if (flags.get(DecorationNonReadable)) + decorations += " writeonly"; + if (flags.get(DecorationNonWritable)) + decorations += " readonly"; + + statement("layout(", merge(attributes), ")", decorations, " buffer ", buffer_name); + } + else + { + string packing_standard; + if (type.basetype == SPIRType::Struct) + { + // The non-block type is embedded in a block, so we cannot use enhanced layouts :( + packing_standard = buffer_to_packing_standard(type, true, false) + ", "; + } + else if (is_array(get_pointee_type(type))) + { + SPIRType wrap_type{OpTypeStruct}; + wrap_type.self = ir.increase_bound_by(1); + wrap_type.member_types.push_back(get_pointee_type_id(type_id)); + ir.set_member_decoration(wrap_type.self, 0, DecorationOffset, 0); + packing_standard = buffer_to_packing_standard(wrap_type, true, false) + ", "; + } + + if (alignment) + statement("layout(", packing_standard, "buffer_reference, buffer_reference_align = ", alignment, ") buffer ", buffer_name); + else + statement("layout(", packing_standard, "buffer_reference) buffer ", buffer_name); + } + + begin_scope(); + + if (is_physical_pointer_to_buffer_block(type)) + { + type.member_name_cache.clear(); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + } + } + else + { + auto &pointee_type = get_pointee_type(type); + statement(type_to_glsl(pointee_type), " value", type_to_array_glsl(pointee_type, 0), ";"); + } + + end_scope_decl(); + statement(""); + } + else + { + statement("layout(buffer_reference) buffer ", buffer_name, ";"); + } +} + +std::string CompilerGLSL::heap_meta_to_prefix(const DescriptorHeapMeta &meta) +{ + std::string prefix; + + if (meta.nonreadable) + prefix += "NoRead"; + if (meta.nonwritable) + prefix += "NoWrite"; + if (meta.coherent) + prefix += "Coherent"; + if (meta.is_volatile) + prefix += "Volatile"; + if (meta.is_restrict) + prefix += "Restrict"; + + return prefix; +} + +std::string CompilerGLSL::to_buffer_pointer_name_prefix(uint32_t ptr_id) const +{ + auto itr = std::find_if(descriptor_heap_types.begin(), descriptor_heap_types.end(), + [&](const DescriptorHeapMeta &meta) { return meta.buffer_pointer_id == ptr_id; }); + + assert(itr != descriptor_heap_types.end()); + + auto name = to_name(itr->type); + + // The same block type can be instantiated with different read-write decorations. + name += heap_meta_to_prefix(*itr); + + // Disambiguate since we can create multiple buffer pointers with same types. + name += to_name(itr->buffer_pointer_id); + + return join("spv", name); +} + +void CompilerGLSL::emit_buffer_block_native(const SPIRVariable *var, const DescriptorHeapMeta *heap_meta) +{ + assert(var || heap_meta); + + SPIRType *type; + if (var) + type = &get(var->basetype); + else + type = &get(heap_meta->type); + + Bitset flags = var ? ir.get_buffer_block_flags(*var) : ir.get_buffer_block_type_flags(*type); + auto storage = var ? var->storage : heap_meta->storage; + + if (heap_meta) + { + if (heap_meta->nonreadable) + flags.set(DecorationNonReadable); + if (heap_meta->nonwritable) + flags.set(DecorationNonWritable); + if (heap_meta->coherent) + flags.set(DecorationCoherent); + if (heap_meta->is_volatile) + flags.set(DecorationVolatile); + if (heap_meta->is_restrict) + flags.set(DecorationRestrict); + } + + bool ssbo = storage == StorageClassStorageBuffer || storage == StorageClassShaderRecordBufferKHR || + has_decoration(type->self, DecorationBufferBlock); + + bool is_restrict = ssbo && flags.get(DecorationRestrict); + bool is_writeonly = ssbo && flags.get(DecorationNonReadable); + bool is_readonly = ssbo && flags.get(DecorationNonWritable); + bool is_coherent = ssbo && flags.get(DecorationCoherent); + + // Block names should never alias, but from HLSL input they kind of can because block types are reused for UAVs ... + auto buffer_name = to_name(type->self, false); + + if (heap_meta) + { + // The same block type can be instantiated with different read-write decorations. + buffer_name += heap_meta_to_prefix(*heap_meta); + } + + auto &block_namespace = ssbo ? block_ssbo_names : block_ubo_names; + + // Shaders never use the block by interface name, so we don't + // have to track this other than updating name caches. + // If we have a collision for any reason, just fallback immediately. + if (var) + { + if (ir.meta[type->self].decoration.alias.empty() || block_namespace.find(buffer_name) != end(block_namespace) || + resource_names.find(buffer_name) != end(resource_names)) + { + buffer_name = get_block_fallback_name(var->self); + } + } + + // Make sure we get something unique for both global name scope and block name scope. + // See GLSL 4.5 spec: section 4.3.9 for details. + add_variable(block_namespace, resource_names, buffer_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + // We cannot reuse this fallback name in neither global scope (blocked by block_names) nor block name scope. + if (buffer_name.empty()) + { + if (var) + buffer_name = join("_", get(var->basetype).self, "_", var->self); + else + buffer_name = join("_", type->self); + } + + block_names.insert(buffer_name); + block_namespace.insert(buffer_name); + + // Save for post-reflection later. + if (var) + declared_block_names[var->self] = buffer_name; + + string layout; + + if (var) + { + layout = layout_for_variable(*var); + } + else + { + auto packing_standard = buffer_to_packing_standard(*type, ssbo, true); + layout = join("layout(", + to_descriptor_heap_layout(*type, ssbo ? StorageClassStorageBuffer : StorageClassUniform), + ", ", packing_standard, ") "); + } + + statement(layout, is_coherent ? "coherent " : "", is_restrict ? "restrict " : "", + is_writeonly ? "writeonly " : "", is_readonly ? "readonly " : "", ssbo ? "buffer " : "uniform ", + buffer_name); + + begin_scope(); + + type->member_name_cache.clear(); + + uint32_t i = 0; + for (auto &member : type->member_types) + { + add_member_name(*type, i); + emit_struct_member(*type, member, i); + i++; + } + + // Don't declare empty blocks in GLSL, this is not allowed. + if (type_is_empty(*type) && !backend.supports_empty_struct) + statement("int empty_struct_member;"); + + // var.self can be used as a backup name for the block name, + // so we need to make sure we don't disturb the name here on a recompile. + // It will need to be reset if we have to recompile. + if (var) + { + preserve_alias_on_reset(var->self); + add_resource_name(var->self); + end_scope_decl(to_name(var->self) + type_to_array_glsl(*type, var->self)); + } + else + { + end_scope_decl(join(to_buffer_pointer_name_prefix(heap_meta->buffer_pointer_id), "ResourceHeap[]")); + } + + statement(""); +} + +void CompilerGLSL::emit_buffer_block_flattened(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + + // Block names should never alias. + auto buffer_name = to_name(type.self, false); + size_t buffer_size = (get_declared_struct_size(type) + 15) / 16; + + SPIRType::BaseType basic_type; + if (get_common_basic_type(type, basic_type)) + { + SPIRType tmp { OpTypeVector }; + tmp.basetype = basic_type; + tmp.vecsize = 4; + if (basic_type != SPIRType::Float && basic_type != SPIRType::Int && basic_type != SPIRType::UInt) + SPIRV_CROSS_THROW("Basic types in a flattened UBO must be float, int or uint."); + + auto flags = ir.get_buffer_block_flags(var); + statement("uniform ", flags_to_qualifiers_glsl(tmp, 0, flags), type_to_glsl(tmp), " ", buffer_name, "[", + buffer_size, "];"); + } + else + SPIRV_CROSS_THROW("All basic types in a flattened block must be the same."); +} + +const char *CompilerGLSL::to_storage_qualifiers_glsl(const SPIRVariable &var) +{ + auto &execution = get_entry_point(); + + if (subpass_input_is_framebuffer_fetch(var.self)) + return ""; + + if (var.storage == StorageClassInput || var.storage == StorageClassOutput) + { + if (is_legacy() && execution.model == ExecutionModelVertex) + return var.storage == StorageClassInput ? "attribute " : "varying "; + else if (is_legacy() && execution.model == ExecutionModelFragment) + return "varying "; // Fragment outputs are renamed so they never hit this case. + else if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput) + { + uint32_t loc = get_decoration(var.self, DecorationLocation); + bool is_inout = location_is_framebuffer_fetch(loc); + if (is_inout) + return "inout "; + else + return "out "; + } + else + return var.storage == StorageClassInput ? "in " : "out "; + } + else if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform || + var.storage == StorageClassPushConstant || var.storage == StorageClassAtomicCounter) + { + return "uniform "; + } + else if (var.storage == StorageClassRayPayloadKHR) + { + return ray_tracing_is_khr ? "rayPayloadEXT " : "rayPayloadNV "; + } + else if (var.storage == StorageClassIncomingRayPayloadKHR) + { + return ray_tracing_is_khr ? "rayPayloadInEXT " : "rayPayloadInNV "; + } + else if (var.storage == StorageClassHitAttributeKHR) + { + return ray_tracing_is_khr ? "hitAttributeEXT " : "hitAttributeNV "; + } + else if (var.storage == StorageClassCallableDataKHR) + { + return ray_tracing_is_khr ? "callableDataEXT " : "callableDataNV "; + } + else if (var.storage == StorageClassIncomingCallableDataKHR) + { + return ray_tracing_is_khr ? "callableDataInEXT " : "callableDataInNV "; + } + + return ""; +} + +void CompilerGLSL::emit_flattened_io_block_member(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices) +{ + uint32_t member_type_id = type.self; + const SPIRType *member_type = &type; + const SPIRType *parent_type = nullptr; + auto flattened_name = basename; + for (auto &index : indices) + { + flattened_name += "_"; + flattened_name += to_member_name(*member_type, index); + parent_type = member_type; + member_type_id = member_type->member_types[index]; + member_type = &get(member_type_id); + } + + assert(member_type->basetype != SPIRType::Struct); + + // We're overriding struct member names, so ensure we do so on the primary type. + if (parent_type->type_alias) + parent_type = &get(parent_type->type_alias); + + // Sanitize underscores because joining the two identifiers might create more than 1 underscore in a row, + // which is not allowed. + ParsedIR::sanitize_underscores(flattened_name); + + uint32_t last_index = indices.back(); + + // Pass in the varying qualifier here so it will appear in the correct declaration order. + // Replace member name while emitting it so it encodes both struct name and member name. + auto backup_name = get_member_name(parent_type->self, last_index); + auto member_name = to_member_name(*parent_type, last_index); + set_member_name(parent_type->self, last_index, flattened_name); + emit_struct_member(*parent_type, member_type_id, last_index, qual); + // Restore member name. + set_member_name(parent_type->self, last_index, member_name); +} + +void CompilerGLSL::emit_flattened_io_block_struct(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices) +{ + auto sub_indices = indices; + sub_indices.push_back(0); + + const SPIRType *member_type = &type; + for (auto &index : indices) + member_type = &get(member_type->member_types[index]); + + assert(member_type->basetype == SPIRType::Struct); + + if (!member_type->array.empty()) + SPIRV_CROSS_THROW("Cannot flatten array of structs in I/O blocks."); + + for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++) + { + sub_indices.back() = i; + if (get(member_type->member_types[i]).basetype == SPIRType::Struct) + emit_flattened_io_block_struct(basename, type, qual, sub_indices); + else + emit_flattened_io_block_member(basename, type, qual, sub_indices); + } +} + +void CompilerGLSL::emit_flattened_io_block(const SPIRVariable &var, const char *qual) +{ + auto &var_type = get(var.basetype); + if (!var_type.array.empty()) + SPIRV_CROSS_THROW("Array of varying structs cannot be flattened to legacy-compatible varyings."); + + // Emit flattened types based on the type alias. Normally, we are never supposed to emit + // struct declarations for aliased types. + auto &type = var_type.type_alias ? get(var_type.type_alias) : var_type; + + auto old_flags = ir.meta[type.self].decoration.decoration_flags; + // Emit the members as if they are part of a block to get all qualifiers. + ir.meta[type.self].decoration.decoration_flags.set(DecorationBlock); + + type.member_name_cache.clear(); + + SmallVector member_indices; + member_indices.push_back(0); + auto basename = to_name(var.self); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + auto &membertype = get(member); + + member_indices.back() = i; + if (membertype.basetype == SPIRType::Struct) + emit_flattened_io_block_struct(basename, type, qual, member_indices); + else + emit_flattened_io_block_member(basename, type, qual, member_indices); + i++; + } + + ir.meta[type.self].decoration.decoration_flags = old_flags; + + // Treat this variable as fully flattened from now on. + flattened_structs[var.self] = true; +} + +void CompilerGLSL::emit_interface_block(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + + if (var.storage == StorageClassInput && type.basetype == SPIRType::Double && + !options.es && options.version < 410) + { + require_extension_internal("GL_ARB_vertex_attrib_64bit"); + } + + // Either make it plain in/out or in/out blocks depending on what shader is doing ... + bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + const char *qual = to_storage_qualifiers_glsl(var); + + if (block) + { + // ESSL earlier than 310 and GLSL earlier than 150 did not support + // I/O variables which are struct types. + // To support this, flatten the struct into separate varyings instead. + if (options.force_flattened_io_blocks || (options.es && options.version < 310) || + (!options.es && options.version < 150)) + { + // I/O blocks on ES require version 310 with Android Extension Pack extensions, or core version 320. + // On desktop, I/O blocks were introduced with geometry shaders in GL 3.2 (GLSL 150). + emit_flattened_io_block(var, qual); + } + else + { + if (options.es && options.version < 320) + { + // Geometry and tessellation extensions imply this extension. + if (!has_extension("GL_EXT_geometry_shader") && !has_extension("GL_EXT_tessellation_shader")) + require_extension_internal("GL_EXT_shader_io_blocks"); + } + + // Workaround to make sure we can emit "patch in/out" correctly. + fixup_io_block_patch_primitive_qualifiers(var); + + // Block names should never alias. + auto block_name = to_name(type.self, false); + + // The namespace for I/O blocks is separate from other variables in GLSL. + auto &block_namespace = type.storage == StorageClassInput ? block_input_names : block_output_names; + + // Shaders never use the block by interface name, so we don't + // have to track this other than updating name caches. + if (block_name.empty() || block_namespace.find(block_name) != end(block_namespace)) + block_name = get_fallback_name(type.self); + else + block_namespace.insert(block_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + if (block_name.empty()) + block_name = join("_", get(var.basetype).self, "_", var.self); + + // Instance names cannot alias block names. + resource_names.insert(block_name); + + const char *block_qualifier; + if (has_decoration(var.self, DecorationPatch)) + block_qualifier = "patch "; + else if (has_decoration(var.self, DecorationPerPrimitiveEXT)) + block_qualifier = "perprimitiveEXT "; + else if (has_decoration(var.self, DecorationPerVertexKHR)) + block_qualifier = "pervertexEXT "; + else + block_qualifier = ""; + + statement(layout_for_variable(var), block_qualifier, qual, block_name); + begin_scope(); + + type.member_name_cache.clear(); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + } + + add_resource_name(var.self); + end_scope_decl(join(to_name(var.self), type_to_array_glsl(type, var.self))); + statement(""); + } + } + else + { + // ESSL earlier than 310 and GLSL earlier than 150 did not support + // I/O variables which are struct types. + // To support this, flatten the struct into separate varyings instead. + if (type.basetype == SPIRType::Struct && + (options.force_flattened_io_blocks || (options.es && options.version < 310) || + (!options.es && options.version < 150))) + { + emit_flattened_io_block(var, qual); + } + else + { + add_resource_name(var.self); + + // Legacy GLSL did not support int attributes, we automatically + // declare them as float and cast them on load/store + SPIRType newtype = type; + if (is_legacy() && var.storage == StorageClassInput && type.basetype == SPIRType::Int) + newtype.basetype = SPIRType::Float; + + // Tessellation control and evaluation shaders must have either + // gl_MaxPatchVertices or unsized arrays for input arrays. + // Opt for unsized as it's the more "correct" variant to use. + if (type.storage == StorageClassInput && !type.array.empty() && + !has_decoration(var.self, DecorationPatch) && + (get_entry_point().model == ExecutionModelTessellationControl || + get_entry_point().model == ExecutionModelTessellationEvaluation)) + { + newtype.array.back() = 0; + newtype.array_size_literal.back() = true; + } + + statement(layout_for_variable(var), to_qualifiers_glsl(var.self), + variable_decl(newtype, to_name(var.self), var.self), ";"); + } + } +} + +void CompilerGLSL::emit_uniform(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.dim != DimSubpassData) + { + if (!options.es && options.version < 420) + require_extension_internal("GL_ARB_shader_image_load_store"); + else if (options.es && options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for shader image load store."); + } + + add_resource_name(var.self); + statement(layout_for_variable(var), variable_decl(var), ";"); +} + +string CompilerGLSL::constant_value_macro_name(uint32_t id) const +{ + return join("SPIRV_CROSS_CONSTANT_ID_", id); +} + +void CompilerGLSL::emit_specialization_constant_op(const SPIRConstantOp &constant) +{ + auto &type = get(constant.basetype); + // This will break. It is bogus and should not be legal. + if (type_is_top_level_block(type)) + return; + add_resource_name(constant.self); + auto name = to_name(constant.self); + statement("const ", variable_decl(type, name), " = ", constant_op_expression(constant), ";"); +} + +int CompilerGLSL::get_constant_mapping_to_workgroup_component(const SPIRConstant &c) const +{ + auto &entry_point = get_entry_point(); + int index = -1; + + // Need to redirect specialization constants which are used as WorkGroupSize to the builtin, + // since the spec constant declarations are never explicitly declared. + if (entry_point.workgroup_size.constant == 0 && entry_point.flags.get(ExecutionModeLocalSizeId)) + { + if (c.self == entry_point.workgroup_size.id_x) + index = 0; + else if (c.self == entry_point.workgroup_size.id_y) + index = 1; + else if (c.self == entry_point.workgroup_size.id_z) + index = 2; + } + + return index; +} + +void CompilerGLSL::emit_constant(const SPIRConstant &constant) +{ + auto &type = get(constant.constant_type); + + // This will break. It is bogus and should not be legal. + if (type_is_top_level_block(type)) + return; + + SpecializationConstant wg_x, wg_y, wg_z; + ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + // This specialization constant is implicitly declared by emitting layout() in; + if (constant.self == workgroup_size_id) + return; + + // These specialization constants are implicitly declared by emitting layout() in; + // In legacy GLSL, we will still need to emit macros for these, so a layout() in; declaration + // later can use macro overrides for work group size. + bool is_workgroup_size_constant = ConstantID(constant.self) == wg_x.id || ConstantID(constant.self) == wg_y.id || + ConstantID(constant.self) == wg_z.id; + + if (options.vulkan_semantics && is_workgroup_size_constant) + { + // Vulkan GLSL does not need to declare workgroup spec constants explicitly, it is handled in layout(). + return; + } + else if (!options.vulkan_semantics && is_workgroup_size_constant && + !has_decoration(constant.self, DecorationSpecId)) + { + // Only bother declaring a workgroup size if it is actually a specialization constant, because we need macros. + return; + } + + add_resource_name(constant.self); + auto name = to_name(constant.self); + + // Only scalars have constant IDs. + if (has_decoration(constant.self, DecorationSpecId)) + { + if (options.vulkan_semantics) + { + statement("layout(constant_id = ", get_decoration(constant.self, DecorationSpecId), ") const ", + variable_decl(type, name), " = ", constant_expression(constant), ";"); + } + else + { + const string ¯o_name = constant.specialization_constant_macro_name; + statement("#ifndef ", macro_name); + statement("#define ", macro_name, " ", constant_expression(constant)); + statement("#endif"); + + // For workgroup size constants, only emit the macros. + if (!is_workgroup_size_constant) + statement("const ", variable_decl(type, name), " = ", macro_name, ";"); + } + } + else + { + statement("const ", variable_decl(type, name), " = ", constant_expression(constant), ";"); + } +} + +void CompilerGLSL::emit_entry_point_declarations() +{ +} + +void CompilerGLSL::replace_illegal_names(const unordered_set &keywords) +{ + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (is_hidden_variable(var)) + return; + + auto *meta = ir.find_meta(var.self); + if (!meta) + return; + + auto &m = meta->decoration; + if (keywords.find(m.alias) != end(keywords)) + m.alias = join("_", m.alias); + }); + + ir.for_each_typed_id([&](uint32_t, const SPIRFunction &func) { + auto *meta = ir.find_meta(func.self); + if (!meta) + return; + + auto &m = meta->decoration; + if (keywords.find(m.alias) != end(keywords)) + m.alias = join("_", m.alias); + }); + + ir.for_each_typed_id([&](uint32_t, const SPIRType &type) { + auto *meta = ir.find_meta(type.self); + if (!meta) + return; + + auto &m = meta->decoration; + if (keywords.find(m.alias) != end(keywords)) + m.alias = join("_", m.alias); + + for (auto &memb : meta->members) + if (keywords.find(memb.alias) != end(keywords)) + memb.alias = join("_", memb.alias); + }); +} + +void CompilerGLSL::replace_illegal_names() +{ + // clang-format off + static const unordered_set keywords = { + "abs", "acos", "acosh", "all", "any", "asin", "asinh", "atan", "atanh", + "atomicAdd", "atomicCompSwap", "atomicCounter", "atomicCounterDecrement", "atomicCounterIncrement", + "atomicExchange", "atomicMax", "atomicMin", "atomicOr", "atomicXor", + "bitCount", "bitfieldExtract", "bitfieldInsert", "bitfieldReverse", + "ceil", "cos", "cosh", "cross", "degrees", + "dFdx", "dFdxCoarse", "dFdxFine", + "dFdy", "dFdyCoarse", "dFdyFine", + "distance", "dot", "EmitStreamVertex", "EmitVertex", "EndPrimitive", "EndStreamPrimitive", "equal", "exp", "exp2", + "faceforward", "findLSB", "findMSB", "float16BitsToInt16", "float16BitsToUint16", "floatBitsToInt", "floatBitsToUint", "floor", "fma", "fract", + "frexp", "fwidth", "fwidthCoarse", "fwidthFine", + "greaterThan", "greaterThanEqual", "groupMemoryBarrier", + "imageAtomicAdd", "imageAtomicAnd", "imageAtomicCompSwap", "imageAtomicExchange", "imageAtomicMax", "imageAtomicMin", "imageAtomicOr", "imageAtomicXor", + "imageLoad", "imageSamples", "imageSize", "imageStore", "imulExtended", "int16BitsToFloat16", "intBitsToFloat", "interpolateAtOffset", "interpolateAtCentroid", "interpolateAtSample", + "inverse", "inversesqrt", "isinf", "isnan", "ldexp", "length", "lessThan", "lessThanEqual", "log", "log2", + "matrixCompMult", "max", "memoryBarrier", "memoryBarrierAtomicCounter", "memoryBarrierBuffer", "memoryBarrierImage", "memoryBarrierShared", + "min", "mix", "mod", "modf", "noise", "noise1", "noise2", "noise3", "noise4", "normalize", "not", "notEqual", + "outerProduct", "packDouble2x32", "packHalf2x16", "packInt2x16", "packInt4x16", "packSnorm2x16", "packSnorm4x8", + "packUint2x16", "packUint4x16", "packUnorm2x16", "packUnorm4x8", "pow", + "radians", "reflect", "refract", "round", "roundEven", "sign", "sin", "sinh", "smoothstep", "sqrt", "step", + "tan", "tanh", "texelFetch", "texelFetchOffset", "texture", "textureGather", "textureGatherOffset", "textureGatherOffsets", + "textureGrad", "textureGradOffset", "textureLod", "textureLodOffset", "textureOffset", "textureProj", "textureProjGrad", + "textureProjGradOffset", "textureProjLod", "textureProjLodOffset", "textureProjOffset", "textureQueryLevels", "textureQueryLod", "textureSamples", "textureSize", + "transpose", "trunc", "uaddCarry", "uint16BitsToFloat16", "uintBitsToFloat", "umulExtended", "unpackDouble2x32", "unpackHalf2x16", "unpackInt2x16", "unpackInt4x16", + "unpackSnorm2x16", "unpackSnorm4x8", "unpackUint2x16", "unpackUint4x16", "unpackUnorm2x16", "unpackUnorm4x8", "usubBorrow", + + "active", "asm", "atomic_uint", "attribute", "bool", "break", "buffer", + "bvec2", "bvec3", "bvec4", "case", "cast", "centroid", "class", "coherent", "common", "const", "continue", "default", "discard", + "dmat2", "dmat2x2", "dmat2x3", "dmat2x4", "dmat3", "dmat3x2", "dmat3x3", "dmat3x4", "dmat4", "dmat4x2", "dmat4x3", "dmat4x4", + "do", "double", "dvec2", "dvec3", "dvec4", "else", "enum", "extern", "external", "false", "filter", "fixed", "flat", "float", + "for", "fvec2", "fvec3", "fvec4", "goto", "half", "highp", "hvec2", "hvec3", "hvec4", "if", "iimage1D", "iimage1DArray", + "iimage2D", "iimage2DArray", "iimage2DMS", "iimage2DMSArray", "iimage2DRect", "iimage3D", "iimageBuffer", "iimageCube", + "iimageCubeArray", "image1D", "image1DArray", "image2D", "image2DArray", "image2DMS", "image2DMSArray", "image2DRect", + "image3D", "imageBuffer", "imageCube", "imageCubeArray", "in", "inline", "inout", "input", "int", "interface", "invariant", + "isampler1D", "isampler1DArray", "isampler2D", "isampler2DArray", "isampler2DMS", "isampler2DMSArray", "isampler2DRect", + "isampler3D", "isamplerBuffer", "isamplerCube", "isamplerCubeArray", "ivec2", "ivec3", "ivec4", "layout", "long", "lowp", + "mat2", "mat2x2", "mat2x3", "mat2x4", "mat3", "mat3x2", "mat3x3", "mat3x4", "mat4", "mat4x2", "mat4x3", "mat4x4", "mediump", + "namespace", "noinline", "noperspective", "out", "output", "packed", "partition", "patch", "precise", "precision", "public", "readonly", + "resource", "restrict", "return", "sample", "sampler1D", "sampler1DArray", "sampler1DArrayShadow", + "sampler1DShadow", "sampler2D", "sampler2DArray", "sampler2DArrayShadow", "sampler2DMS", "sampler2DMSArray", + "sampler2DRect", "sampler2DRectShadow", "sampler2DShadow", "sampler3D", "sampler3DRect", "samplerBuffer", + "samplerCube", "samplerCubeArray", "samplerCubeArrayShadow", "samplerCubeShadow", "shared", "short", "sizeof", "smooth", "static", + "struct", "subroutine", "superp", "switch", "template", "this", "true", "typedef", "uimage1D", "uimage1DArray", "uimage2D", + "uimage2DArray", "uimage2DMS", "uimage2DMSArray", "uimage2DRect", "uimage3D", "uimageBuffer", "uimageCube", + "uimageCubeArray", "uint", "uniform", "union", "unsigned", "usampler1D", "usampler1DArray", "usampler2D", "usampler2DArray", + "usampler2DMS", "usampler2DMSArray", "usampler2DRect", "usampler3D", "usamplerBuffer", "usamplerCube", + "usamplerCubeArray", "using", "uvec2", "uvec3", "uvec4", "varying", "vec2", "vec3", "vec4", "void", "volatile", + "while", "writeonly", + }; + // clang-format on + + replace_illegal_names(keywords); +} + +void CompilerGLSL::replace_fragment_output(SPIRVariable &var) +{ + auto &m = ir.meta[var.self].decoration; + uint32_t location = 0; + if (m.decoration_flags.get(DecorationLocation)) + location = m.location; + + // If our variable is arrayed, we must not emit the array part of this as the SPIR-V will + // do the access chain part of this for us. + auto &type = get(var.basetype); + + if (type.array.empty()) + { + // Redirect the write to a specific render target in legacy GLSL. + m.alias = join("gl_FragData[", location, "]"); + + if (is_legacy_es() && location != 0) + require_extension_internal("GL_EXT_draw_buffers"); + } + else if (type.array.size() == 1) + { + // If location is non-zero, we probably have to add an offset. + // This gets really tricky since we'd have to inject an offset in the access chain. + // FIXME: This seems like an extremely odd-ball case, so it's probably fine to leave it like this for now. + m.alias = "gl_FragData"; + if (location != 0) + SPIRV_CROSS_THROW("Arrayed output variable used, but location is not 0. " + "This is unimplemented in SPIRV-Cross."); + + if (is_legacy_es()) + require_extension_internal("GL_EXT_draw_buffers"); + } + else + SPIRV_CROSS_THROW("Array-of-array output variable used. This cannot be implemented in legacy GLSL."); + + var.compat_builtin = true; // We don't want to declare this variable, but use the name as-is. +} + +void CompilerGLSL::replace_fragment_outputs() +{ + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + if (!is_builtin_variable(var) && !var.remapped_variable && type.pointer && var.storage == StorageClassOutput) + replace_fragment_output(var); + }); +} + +string CompilerGLSL::remap_swizzle(const SPIRType &out_type, uint32_t input_components, const string &expr) +{ + if (out_type.vecsize == input_components) + return expr; + else if (input_components == 1 && !backend.can_swizzle_scalar) + return join(type_to_glsl(out_type), "(", expr, ")"); + else + { + // FIXME: This will not work with packed expressions. + auto e = enclose_expression(expr) + "."; + // Just clamp the swizzle index if we have more outputs than inputs. + for (uint32_t c = 0; c < out_type.vecsize; c++) + e += index_to_swizzle(min(c, input_components - 1)); + if (backend.swizzle_is_function && out_type.vecsize > 1) + e += "()"; + + remove_duplicate_swizzle(e); + return e; + } +} + +void CompilerGLSL::emit_pls() +{ + auto &execution = get_entry_point(); + if (execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Pixel local storage only supported in fragment shaders."); + + if (!options.es) + SPIRV_CROSS_THROW("Pixel local storage only supported in OpenGL ES."); + + if (options.version < 300) + SPIRV_CROSS_THROW("Pixel local storage only supported in ESSL 3.0 and above."); + + if (!pls_inputs.empty()) + { + statement("__pixel_local_inEXT _PLSIn"); + begin_scope(); + for (auto &input : pls_inputs) + statement(pls_decl(input), ";"); + end_scope_decl(); + statement(""); + } + + if (!pls_outputs.empty()) + { + statement("__pixel_local_outEXT _PLSOut"); + begin_scope(); + for (auto &output : pls_outputs) + statement(pls_decl(output), ";"); + end_scope_decl(); + statement(""); + } +} + +void CompilerGLSL::fixup_image_load_store_access() +{ + if (!options.enable_storage_image_qualifier_deduction) + return; + + ir.for_each_typed_id([&](uint32_t var, const SPIRVariable &) { + auto &vartype = expression_type(var); + if (vartype.basetype == SPIRType::Image && vartype.image.sampled == 2) + { + // Very old glslangValidator and HLSL compilers do not emit required qualifiers here. + // Solve this by making the image access as restricted as possible and loosen up if we need to. + // If any no-read/no-write flags are actually set, assume that the compiler knows what it's doing. + + if (!has_decoration(var, DecorationNonWritable) && !has_decoration(var, DecorationNonReadable)) + { + set_decoration(var, DecorationNonWritable); + set_decoration(var, DecorationNonReadable); + } + } + }); +} + +static bool is_block_builtin(BuiltIn builtin) +{ + return builtin == BuiltInPosition || builtin == BuiltInPointSize || builtin == BuiltInClipDistance || + builtin == BuiltInCullDistance; +} + +bool CompilerGLSL::should_force_emit_builtin_block(StorageClass storage) +{ + // If the builtin block uses XFB, we need to force explicit redeclaration of the builtin block. + + if (storage != StorageClassOutput) + return false; + bool should_force = false; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (should_force) + return; + + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + if (var.storage == storage && block && is_builtin_variable(var)) + { + uint32_t member_count = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + if (has_member_decoration(type.self, i, DecorationBuiltIn) && + is_block_builtin(BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn))) && + has_member_decoration(type.self, i, DecorationOffset)) + { + should_force = true; + } + } + } + else if (var.storage == storage && !block && is_builtin_variable(var)) + { + if (is_block_builtin(BuiltIn(get_decoration(type.self, DecorationBuiltIn))) && + has_decoration(var.self, DecorationOffset)) + { + should_force = true; + } + } + }); + + // If we're declaring clip/cull planes with control points we need to force block declaration. + if ((get_execution_model() == ExecutionModelTessellationControl || + get_execution_model() == ExecutionModelMeshEXT) && + (clip_distance_count || cull_distance_count)) + { + should_force = true; + } + + // Either glslang bug or oversight, but global invariant position does not work in mesh shaders. + if (get_execution_model() == ExecutionModelMeshEXT && position_invariant) + should_force = true; + + return should_force; +} + +void CompilerGLSL::fixup_implicit_builtin_block_names(ExecutionModel model) +{ + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + if ((var.storage == StorageClassOutput || var.storage == StorageClassInput) && block && + is_builtin_variable(var)) + { + if (model != ExecutionModelMeshEXT) + { + // Make sure the array has a supported name in the code. + if (var.storage == StorageClassOutput) + set_name(var.self, "gl_out"); + else if (var.storage == StorageClassInput) + set_name(var.self, "gl_in"); + } + else + { + auto flags = get_buffer_block_flags(var.self); + if (flags.get(DecorationPerPrimitiveEXT)) + { + set_name(var.self, "gl_MeshPrimitivesEXT"); + set_name(type.self, "gl_MeshPerPrimitiveEXT"); + } + else + { + set_name(var.self, "gl_MeshVerticesEXT"); + set_name(type.self, "gl_MeshPerVertexEXT"); + } + } + } + + if (model == ExecutionModelMeshEXT && var.storage == StorageClassOutput && !block) + { + auto *m = ir.find_meta(var.self); + if (m && m->decoration.builtin) + { + auto builtin_type = m->decoration.builtin_type; + if (builtin_type == BuiltInPrimitivePointIndicesEXT) + set_name(var.self, "gl_PrimitivePointIndicesEXT"); + else if (builtin_type == BuiltInPrimitiveLineIndicesEXT) + set_name(var.self, "gl_PrimitiveLineIndicesEXT"); + else if (builtin_type == BuiltInPrimitiveTriangleIndicesEXT) + set_name(var.self, "gl_PrimitiveTriangleIndicesEXT"); + } + } + }); +} + +void CompilerGLSL::emit_declared_builtin_block(StorageClass storage, ExecutionModel model) +{ + Bitset emitted_builtins; + Bitset global_builtins; + const SPIRVariable *block_var = nullptr; + bool emitted_block = false; + + // Need to use declared size in the type. + // These variables might have been declared, but not statically used, so we haven't deduced their size yet. + uint32_t cull_distance_size = 0; + uint32_t clip_distance_size = 0; + + bool have_xfb_buffer_stride = false; + bool have_geom_stream = false; + bool have_any_xfb_offset = false; + uint32_t xfb_stride = 0, xfb_buffer = 0, geom_stream = 0; + std::unordered_map builtin_xfb_offsets; + + const auto builtin_is_per_vertex_set = [](BuiltIn builtin) -> bool { + return builtin == BuiltInPosition || builtin == BuiltInPointSize || + builtin == BuiltInClipDistance || builtin == BuiltInCullDistance; + }; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + Bitset builtins; + + if (var.storage == storage && block && is_builtin_variable(var)) + { + uint32_t index = 0; + for (auto &m : ir.meta[type.self].members) + { + if (m.builtin && builtin_is_per_vertex_set(m.builtin_type)) + { + builtins.set(m.builtin_type); + if (m.builtin_type == BuiltInCullDistance) + cull_distance_size = to_array_size_literal(this->get(type.member_types[index])); + else if (m.builtin_type == BuiltInClipDistance) + clip_distance_size = to_array_size_literal(this->get(type.member_types[index])); + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationOffset)) + { + have_any_xfb_offset = true; + builtin_xfb_offsets[m.builtin_type] = m.offset; + } + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationStream)) + { + uint32_t stream = m.stream; + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } + } + index++; + } + + if (storage == StorageClassOutput && has_decoration(var.self, DecorationXfbBuffer) && + has_decoration(var.self, DecorationXfbStride)) + { + uint32_t buffer_index = get_decoration(var.self, DecorationXfbBuffer); + uint32_t stride = get_decoration(var.self, DecorationXfbStride); + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + xfb_stride = stride; + } + + if (storage == StorageClassOutput && has_decoration(var.self, DecorationStream)) + { + uint32_t stream = get_decoration(var.self, DecorationStream); + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } + } + else if (var.storage == storage && !block && is_builtin_variable(var)) + { + // While we're at it, collect all declared global builtins (HLSL mostly ...). + auto &m = ir.meta[var.self].decoration; + if (m.builtin && builtin_is_per_vertex_set(m.builtin_type)) + { + // For mesh/tesc output, Clip/Cull is an array-of-array. Look at innermost array type + // for correct result. + global_builtins.set(m.builtin_type); + if (m.builtin_type == BuiltInCullDistance) + cull_distance_size = to_array_size_literal(type, 0); + else if (m.builtin_type == BuiltInClipDistance) + clip_distance_size = to_array_size_literal(type, 0); + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationXfbStride) && + m.decoration_flags.get(DecorationXfbBuffer) && m.decoration_flags.get(DecorationOffset)) + { + have_any_xfb_offset = true; + builtin_xfb_offsets[m.builtin_type] = m.offset; + uint32_t buffer_index = m.xfb_buffer; + uint32_t stride = m.xfb_stride; + if (have_xfb_buffer_stride && buffer_index != xfb_buffer) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + if (have_xfb_buffer_stride && stride != xfb_stride) + SPIRV_CROSS_THROW("IO block member XfbBuffer mismatch."); + have_xfb_buffer_stride = true; + xfb_buffer = buffer_index; + xfb_stride = stride; + } + + if (is_block_builtin(m.builtin_type) && m.decoration_flags.get(DecorationStream)) + { + uint32_t stream = get_decoration(var.self, DecorationStream); + if (have_geom_stream && geom_stream != stream) + SPIRV_CROSS_THROW("IO block member Stream mismatch."); + have_geom_stream = true; + geom_stream = stream; + } + } + } + + if (builtins.empty()) + return; + + if (emitted_block) + SPIRV_CROSS_THROW("Cannot use more than one builtin I/O block."); + + emitted_builtins = builtins; + emitted_block = true; + block_var = &var; + }); + + global_builtins = + Bitset(global_builtins.get_lower() & ((1ull << BuiltInPosition) | (1ull << BuiltInPointSize) | + (1ull << BuiltInClipDistance) | (1ull << BuiltInCullDistance))); + + // Try to collect all other declared builtins. + if (!emitted_block) + emitted_builtins = global_builtins; + + // Can't declare an empty interface block. + if (emitted_builtins.empty()) + return; + + if (storage == StorageClassOutput) + { + SmallVector attr; + if (have_xfb_buffer_stride && have_any_xfb_offset) + { + if (!options.es) + { + if (options.version < 440 && options.version >= 140) + require_extension_internal("GL_ARB_enhanced_layouts"); + else if (options.version < 140) + SPIRV_CROSS_THROW("Component decoration is not supported in targets below GLSL 1.40."); + if (!options.es && options.version < 440) + require_extension_internal("GL_ARB_enhanced_layouts"); + } + else if (options.es) + SPIRV_CROSS_THROW("Need GL_ARB_enhanced_layouts for xfb_stride or xfb_buffer."); + attr.push_back(join("xfb_buffer = ", xfb_buffer, ", xfb_stride = ", xfb_stride)); + } + + if (have_geom_stream) + { + if (get_execution_model() != ExecutionModelGeometry) + SPIRV_CROSS_THROW("Geometry streams can only be used in geometry shaders."); + if (options.es) + SPIRV_CROSS_THROW("Multiple geometry streams not supported in ESSL."); + if (options.version < 400) + require_extension_internal("GL_ARB_transform_feedback3"); + attr.push_back(join("stream = ", geom_stream)); + } + + if (model == ExecutionModelMeshEXT) + statement("out gl_MeshPerVertexEXT"); + else if (!attr.empty()) + statement("layout(", merge(attr), ") out gl_PerVertex"); + else + statement("out gl_PerVertex"); + } + else + { + // If we have passthrough, there is no way PerVertex cannot be passthrough. + if (get_entry_point().geometry_passthrough) + statement("layout(passthrough) in gl_PerVertex"); + else + statement("in gl_PerVertex"); + } + + begin_scope(); + if (emitted_builtins.get(BuiltInPosition)) + { + auto itr = builtin_xfb_offsets.find(BuiltInPosition); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") vec4 gl_Position;"); + else if (position_invariant) + statement("invariant vec4 gl_Position;"); + else + statement("vec4 gl_Position;"); + } + + if (emitted_builtins.get(BuiltInPointSize)) + { + auto itr = builtin_xfb_offsets.find(BuiltInPointSize); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_PointSize;"); + else + statement("float gl_PointSize;"); + } + + if (emitted_builtins.get(BuiltInClipDistance)) + { + auto itr = builtin_xfb_offsets.find(BuiltInClipDistance); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_ClipDistance[", clip_distance_size, "];"); + else + statement("float gl_ClipDistance[", clip_distance_size, "];"); + } + + if (emitted_builtins.get(BuiltInCullDistance)) + { + auto itr = builtin_xfb_offsets.find(BuiltInCullDistance); + if (itr != end(builtin_xfb_offsets)) + statement("layout(xfb_offset = ", itr->second, ") float gl_CullDistance[", cull_distance_size, "];"); + else + statement("float gl_CullDistance[", cull_distance_size, "];"); + } + + bool builtin_array = model == ExecutionModelTessellationControl || + (model == ExecutionModelMeshEXT && storage == StorageClassOutput) || + (model == ExecutionModelGeometry && storage == StorageClassInput) || + (model == ExecutionModelTessellationEvaluation && storage == StorageClassInput); + + if (builtin_array) + { + const char *instance_name; + if (model == ExecutionModelMeshEXT) + instance_name = "gl_MeshVerticesEXT"; // Per primitive is never synthesized. + else + instance_name = storage == StorageClassInput ? "gl_in" : "gl_out"; + + if (model == ExecutionModelTessellationControl && storage == StorageClassOutput) + end_scope_decl(join(instance_name, "[", get_entry_point().output_vertices, "]")); + else + end_scope_decl(join(instance_name, "[]")); + } + else + end_scope_decl(); + statement(""); +} + +bool CompilerGLSL::variable_is_lut(const SPIRVariable &var) const +{ + bool statically_assigned = var.statically_assigned && var.static_expression != ID(0) && var.remapped_variable; + + if (statically_assigned) + { + auto *constant = maybe_get(var.static_expression); + if (constant && constant->is_used_as_lut) + return true; + } + + return false; +} + +void CompilerGLSL::emit_resources() +{ + auto &execution = get_entry_point(); + + replace_illegal_names(); + + // Legacy GL uses gl_FragData[], redeclare all fragment outputs + // with builtins. + if (execution.model == ExecutionModelFragment && is_legacy()) + replace_fragment_outputs(); + + // Emit PLS blocks if we have such variables. + if (!pls_inputs.empty() || !pls_outputs.empty()) + emit_pls(); + + switch (execution.model) + { + case ExecutionModelGeometry: + case ExecutionModelTessellationControl: + case ExecutionModelTessellationEvaluation: + case ExecutionModelMeshEXT: + fixup_implicit_builtin_block_names(execution.model); + break; + + default: + break; + } + + bool global_invariant_position = position_invariant && (options.es || options.version >= 120); + + // Emit custom gl_PerVertex for SSO compatibility. + if (options.separate_shader_objects && !options.es && execution.model != ExecutionModelFragment) + { + switch (execution.model) + { + case ExecutionModelGeometry: + case ExecutionModelTessellationControl: + case ExecutionModelTessellationEvaluation: + emit_declared_builtin_block(StorageClassInput, execution.model); + emit_declared_builtin_block(StorageClassOutput, execution.model); + global_invariant_position = false; + break; + + case ExecutionModelVertex: + case ExecutionModelMeshEXT: + emit_declared_builtin_block(StorageClassOutput, execution.model); + global_invariant_position = false; + break; + + default: + break; + } + } + else if (should_force_emit_builtin_block(StorageClassOutput)) + { + emit_declared_builtin_block(StorageClassOutput, execution.model); + global_invariant_position = false; + } + else if (execution.geometry_passthrough) + { + // Need to declare gl_in with Passthrough. + // If we're doing passthrough, we cannot emit an output block, so the output block test above will never pass. + emit_declared_builtin_block(StorageClassInput, execution.model); + } + else + { + // Need to redeclare clip/cull distance with explicit size to use them. + // SPIR-V mandates these builtins have a size declared. + const char *storage = execution.model == ExecutionModelFragment ? "in" : "out"; + if (clip_distance_count != 0) + statement(storage, " float gl_ClipDistance[", clip_distance_count, "];"); + if (cull_distance_count != 0) + statement(storage, " float gl_CullDistance[", cull_distance_count, "];"); + if (clip_distance_count != 0 || cull_distance_count != 0) + statement(""); + } + + if (global_invariant_position) + { + statement("invariant gl_Position;"); + statement(""); + } + + bool emitted = false; + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64) + { + // Output buffer reference block forward declarations. + ir.for_each_typed_id([&](uint32_t id, SPIRType &type) + { + if (is_physical_pointer(type)) + { + bool emit_type = true; + if (!is_physical_pointer_to_buffer_block(type)) + { + // Only forward-declare if we intend to emit it in the non_block_pointer types. + // Otherwise, these are just "benign" pointer types that exist as a result of access chains. + emit_type = std::find(physical_storage_non_block_pointer_types.begin(), + physical_storage_non_block_pointer_types.end(), + id) != physical_storage_non_block_pointer_types.end(); + } + + if (emit_type) + { + emit_buffer_reference_block(id, true); + emitted = true; + } + } + }); + } + + if (emitted) + statement(""); + emitted = false; + + SmallVector spec_const_dependencies; + bool legacy_spec_constant_workgroup = execution.model == ExecutionModelGLCompute && !options.vulkan_semantics && + (execution.workgroup_size.constant != 0 || execution.flags.get( + ExecutionModeLocalSizeId)); + if (legacy_spec_constant_workgroup) + { + SpecializationConstant wg_x, wg_y, wg_z; + get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + if (wg_x.id != ConstantID(0)) + spec_const_dependencies.push_back(wg_x.id); + if (wg_y.id != ConstantID(0)) + spec_const_dependencies.push_back(wg_y.id); + if (wg_z.id != ConstantID(0)) + spec_const_dependencies.push_back(wg_z.id); + } + + const auto notify_spec_constant = [&](ConstantID id) + { + if (legacy_spec_constant_workgroup) + { + auto itr = std::find(spec_const_dependencies.begin(), spec_const_dependencies.end(), id); + + if (itr == spec_const_dependencies.end()) + return; + + spec_const_dependencies.erase(itr); + if (spec_const_dependencies.empty()) + { + SpecializationConstant wg_x, wg_y, wg_z; + // We have declared all dependencies. We must delcare the workgroup size immediately + // as subsequent spec constant ops may depend on the declaration. + // Newer glslang does not allow gl_WorkGroupSize to be accessed before layout(local_size) in; + get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + SmallVector inputs; + build_workgroup_size(inputs, wg_x, wg_y, wg_z); + statement("layout(", merge(inputs), ") in;"); + statement(""); + legacy_spec_constant_workgroup = false; + } + } + }; + + // If emitted Vulkan GLSL, + // emit specialization constants as actual floats, + // spec op expressions will redirect to the constant name. + // + { + auto loop_lock = ir.create_loop_hard_lock(); + for (auto &id_ : ir.ids_for_constant_undef_or_type) + { + auto &id = ir.ids[id_]; + + // Skip declaring any bogus constants or undefs which use block types. + // We don't declare block types directly, so this will never work. + // Should not be legal SPIR-V, so this is considered a workaround. + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + bool needs_declaration = c.specialization || c.is_used_as_lut; + + if (needs_declaration) + { + if (!options.vulkan_semantics && c.specialization) + { + c.specialization_constant_macro_name = + constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); + } + emit_constant(c); + emitted = true; + } + + if (c.specialization) + notify_spec_constant(ConstantID(c.self)); + } + else if (id.get_type() == TypeConstantOp) + { + emit_specialization_constant_op(id.get()); + emitted = true; + notify_spec_constant(ConstantID(id.get_id())); + } + else if (id.get_type() == TypeType) + { + auto *type = &id.get(); + + bool is_natural_struct = type->basetype == SPIRType::Struct && type->array.empty() && !type->pointer && + (!has_decoration(type->self, DecorationBlock) && + !has_decoration(type->self, DecorationBufferBlock)); + + // Special case, ray payload and hit attribute blocks are not really blocks, just regular structs. + if (type->basetype == SPIRType::Struct && type->pointer && + has_decoration(type->self, DecorationBlock) && + (type->storage == StorageClassRayPayloadKHR || type->storage == StorageClassIncomingRayPayloadKHR || + type->storage == StorageClassHitAttributeKHR)) + { + type = &get(type->parent_type); + is_natural_struct = true; + } + + if (is_natural_struct) + { + if (emitted) + statement(""); + emitted = false; + + emit_struct(*type); + } + } + else if (id.get_type() == TypeUndef) + { + auto &undef = id.get(); + auto &type = this->get(undef.basetype); + // OpUndef can be void for some reason ... + if (type.basetype == SPIRType::Void) + continue; + + // This will break. It is bogus and should not be legal. + if (type_is_top_level_block(type)) + continue; + + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(undef.basetype)); + + // FIXME: If used in a constant, we must declare it as one. + statement(variable_decl(type, to_name(undef.self), undef.self), initializer, ";"); + emitted = true; + } + } + } + + if (emitted) + statement(""); + + emitted = false; + + if (ir.addressing_model == AddressingModelPhysicalStorageBuffer64) + { + // Output buffer reference blocks. + // Buffer reference blocks can reference themselves to support things like linked lists. + for (auto type : physical_storage_non_block_pointer_types) + emit_buffer_reference_block(type, false); + + ir.for_each_typed_id([&](uint32_t id, SPIRType &type) { + if (is_physical_pointer_to_buffer_block(type)) + emit_buffer_reference_block(id, false); + }); + } + + // Output UBOs and SSBOs + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform || + type.storage == StorageClassShaderRecordBufferKHR; + bool has_block_flags = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + + if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) && + has_block_flags) + { + emit_buffer_block(var); + } + }); + + // Output push constant blocks + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant && + !is_hidden_variable(var)) + { + emit_push_constant_block(var); + } + }); + + bool skip_separate_image_sampler = !combined_image_samplers.empty() || !options.vulkan_semantics; + + // Output Uniform Constants (values, samplers, images, etc). + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + // If we're remapping separate samplers and images, only emit the combined samplers. + if (skip_separate_image_sampler) + { + // Sampler buffers are always used without a sampler, and they will also work in regular GL. + bool sampler_buffer = type.basetype == SPIRType::Image && type.image.dim == DimBuffer; + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + if (!sampler_buffer && (separate_image || separate_sampler)) + return; + } + + if (var.storage != StorageClassFunction && type.pointer && + (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter || + type.storage == StorageClassRayPayloadKHR || type.storage == StorageClassIncomingRayPayloadKHR || + type.storage == StorageClassCallableDataKHR || type.storage == StorageClassIncomingCallableDataKHR || + type.storage == StorageClassHitAttributeKHR) && + !is_hidden_variable(var)) + { + emit_uniform(var); + emitted = true; + } + }); + + if (emitted) + statement(""); + emitted = false; + + bool emitted_base_instance = false; + + // Output in/out interfaces. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + bool is_hidden = is_hidden_variable(var); + + // Unused output I/O variables might still be required to implement framebuffer fetch. + if (var.storage == StorageClassOutput && !is_legacy() && + location_is_framebuffer_fetch(get_decoration(var.self, DecorationLocation)) != 0) + { + is_hidden = false; + } + + if (var.storage != StorageClassFunction && type.pointer && + (var.storage == StorageClassInput || var.storage == StorageClassOutput) && + interface_variable_exists_in_entry_point(var.self) && !is_hidden) + { + if (options.es && get_execution_model() == ExecutionModelVertex && var.storage == StorageClassInput && + type.array.size() == 1) + { + SPIRV_CROSS_THROW("OpenGL ES doesn't support array input variables in vertex shader."); + } + emit_interface_block(var); + emitted = true; + } + else if (is_builtin_variable(var)) + { + auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + // For gl_InstanceIndex emulation on GLES, the API user needs to + // supply this uniform. + + // The draw parameter extension is soft-enabled on GL with some fallbacks. + if (!options.vulkan_semantics) + { + if (!emitted_base_instance && + ((options.vertex.support_nonzero_base_instance && builtin == BuiltInInstanceIndex) || + (builtin == BuiltInBaseInstance))) + { + statement("#ifdef GL_ARB_shader_draw_parameters"); + statement("#define SPIRV_Cross_BaseInstance gl_BaseInstanceARB"); + statement("#else"); + // A crude, but simple workaround which should be good enough for non-indirect draws. + statement("uniform int SPIRV_Cross_BaseInstance;"); + statement("#endif"); + emitted = true; + emitted_base_instance = true; + } + else if (builtin == BuiltInBaseVertex) + { + statement("#ifdef GL_ARB_shader_draw_parameters"); + statement("#define SPIRV_Cross_BaseVertex gl_BaseVertexARB"); + statement("#else"); + // A crude, but simple workaround which should be good enough for non-indirect draws. + statement("uniform int SPIRV_Cross_BaseVertex;"); + statement("#endif"); + } + else if (builtin == BuiltInDrawIndex) + { + statement("#ifndef GL_ARB_shader_draw_parameters"); + // Cannot really be worked around. + statement("#error GL_ARB_shader_draw_parameters is not supported."); + statement("#endif"); + } + } + } + }); + + // Global variables. + for (auto global : global_variables) + { + auto &var = get(global); + if (is_hidden_variable(var, true)) + continue; + + if (var.storage != StorageClassOutput) + { + if (!variable_is_lut(var)) + { + add_resource_name(var.self); + + string initializer; + if (options.force_zero_initialized_variables && var.storage == StorageClassPrivate && + !var.initializer && !var.static_expression && type_can_zero_initialize(get_variable_data_type(var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(var))); + } + + statement(variable_decl(var), initializer, ";"); + emitted = true; + } + } + else if (var.initializer && maybe_get(var.initializer) != nullptr) + { + emit_output_variable_initializer(var); + } + } + + bool needs_hlsl_warning = false; + + for (const auto &heap_type : descriptor_heap_types) + { + auto &type = get(heap_type.type); + + if (heap_type.hlsl_style_stride) + needs_hlsl_warning = true; + + if (type.basetype == SPIRType::Image || type.basetype == SPIRType::AccelerationStructure) + { + string type_layout; + + // We lose NonWritable / NonReadable information, glslang issue, no good way to plumb it through either ... + if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.format != ImageFormatUnknown) + type_layout = join("layout(", to_descriptor_heap_layout(type), ", ", format_to_glsl(type.image.format), ") uniform "); + else + type_layout = join("layout(", to_descriptor_heap_layout(type), ") uniform "); + + statement(type_layout, variable_decl(type, join("spv", to_name(type.self), "ResourceHeap")), "[];"); + } + else if (type.basetype == SPIRType::Sampler) + { + statement("layout(", to_descriptor_heap_layout(type), ") uniform ", + variable_decl(type, join("spv", to_name(type.self), "SamplerHeap")), "[];"); + } + else + { + emit_buffer_block_native(nullptr, &heap_type); + } + } + + if (needs_hlsl_warning) + { + statement("// WARNING: HLSL style descriptor heap stride is assumed for one or more descriptors. Allowing for compatibility with HLSL shaders."); + statement("// This may be not strictly be compatible with GLSL if sizeof(buffer) != sizeof(image)."); + statement("// Application side can convert bindless indices accordingly to compensate or use explicit mapping API to configure strides outside SPIRV-Cross."); + } + + if (emitted) + statement(""); +} + +void CompilerGLSL::emit_output_variable_initializer(const SPIRVariable &var) +{ + // If a StorageClassOutput variable has an initializer, we need to initialize it in main(). + auto &entry_func = this->get(ir.default_entry_point); + auto &type = get(var.basetype); + bool is_patch = has_decoration(var.self, DecorationPatch); + bool is_block = has_decoration(type.self, DecorationBlock); + bool is_control_point = get_execution_model() == ExecutionModelTessellationControl && !is_patch; + + if (is_block) + { + uint32_t member_count = uint32_t(type.member_types.size()); + bool type_is_array = type.array.size() == 1; + uint32_t array_size = 1; + if (type_is_array) + array_size = to_array_size_literal(type); + uint32_t iteration_count = is_control_point ? 1 : array_size; + + // If the initializer is a block, we must initialize each block member one at a time. + for (uint32_t i = 0; i < member_count; i++) + { + // These outputs might not have been properly declared, so don't initialize them in that case. + if (has_member_decoration(type.self, i, DecorationBuiltIn)) + { + if (get_member_decoration(type.self, i, DecorationBuiltIn) == BuiltInCullDistance && + !cull_distance_count) + continue; + + if (get_member_decoration(type.self, i, DecorationBuiltIn) == BuiltInClipDistance && + !clip_distance_count) + continue; + } + + // We need to build a per-member array first, essentially transposing from AoS to SoA. + // This code path hits when we have an array of blocks. + string lut_name; + if (type_is_array) + { + lut_name = join("_", var.self, "_", i, "_init"); + uint32_t member_type_id = get(var.basetype).member_types[i]; + auto &member_type = get(member_type_id); + auto array_type = member_type; + array_type.parent_type = member_type_id; + array_type.op = OpTypeArray; + array_type.array.push_back(array_size); + array_type.array_size_literal.push_back(true); + + SmallVector exprs; + exprs.reserve(array_size); + auto &c = get(var.initializer); + for (uint32_t j = 0; j < array_size; j++) + exprs.push_back(to_expression(get(c.subconstants[j]).subconstants[i])); + statement("const ", type_to_glsl(array_type), " ", lut_name, type_to_array_glsl(array_type, 0), " = ", + type_to_glsl_constructor(array_type), "(", merge(exprs, ", "), ");"); + } + + for (uint32_t j = 0; j < iteration_count; j++) + { + entry_func.fixup_hooks_in.push_back([=, &var]() { + AccessChainMeta meta; + auto &c = this->get(var.initializer); + + uint32_t invocation_id = 0; + uint32_t member_index_id = 0; + if (is_control_point) + { + uint32_t ids = ir.increase_bound_by(3); + auto &uint_type = set(ids, OpTypeInt); + uint_type.basetype = SPIRType::UInt; + uint_type.width = 32; + set(ids + 1, builtin_to_glsl(BuiltInInvocationId, StorageClassInput), ids, true); + set(ids + 2, ids, i, false); + invocation_id = ids + 1; + member_index_id = ids + 2; + } + + if (is_patch) + { + statement("if (gl_InvocationID == 0)"); + begin_scope(); + } + + if (type_is_array && !is_control_point) + { + uint32_t indices[2] = { j, i }; + auto chain = access_chain_internal(var.self, indices, 2, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &meta, nullptr); + statement(chain, " = ", lut_name, "[", j, "];"); + } + else if (is_control_point) + { + uint32_t indices[2] = { invocation_id, member_index_id }; + auto chain = access_chain_internal(var.self, indices, 2, 0, &meta, nullptr); + statement(chain, " = ", lut_name, "[", builtin_to_glsl(BuiltInInvocationId, StorageClassInput), "];"); + } + else + { + auto chain = + access_chain_internal(var.self, &i, 1, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &meta, nullptr); + statement(chain, " = ", to_expression(c.subconstants[i]), ";"); + } + + if (is_patch) + end_scope(); + }); + } + } + } + else if (is_control_point) + { + auto lut_name = join("_", var.self, "_init"); + statement("const ", type_to_glsl(type), " ", lut_name, type_to_array_glsl(type, 0), + " = ", to_expression(var.initializer), ";"); + entry_func.fixup_hooks_in.push_back([&, lut_name]() { + statement(to_expression(var.self), "[gl_InvocationID] = ", lut_name, "[gl_InvocationID];"); + }); + } + else if (has_decoration(var.self, DecorationBuiltIn) && + BuiltIn(get_decoration(var.self, DecorationBuiltIn)) == BuiltInSampleMask) + { + // We cannot copy the array since gl_SampleMask is unsized in GLSL. Unroll time! <_< + entry_func.fixup_hooks_in.push_back([&] { + auto &c = this->get(var.initializer); + uint32_t num_constants = uint32_t(c.subconstants.size()); + for (uint32_t i = 0; i < num_constants; i++) + { + // Don't use to_expression on constant since it might be uint, just fish out the raw int. + statement(to_expression(var.self), "[", i, "] = ", + convert_to_string(this->get(c.subconstants[i]).scalar_i32()), ";"); + } + }); + } + else + { + auto lut_name = join("_", var.self, "_init"); + statement("const ", type_to_glsl(type), " ", lut_name, + type_to_array_glsl(type, var.self), " = ", to_expression(var.initializer), ";"); + entry_func.fixup_hooks_in.push_back([&, lut_name, is_patch]() { + if (is_patch) + { + statement("if (gl_InvocationID == 0)"); + begin_scope(); + } + statement(to_expression(var.self), " = ", lut_name, ";"); + if (is_patch) + end_scope(); + }); + } +} + +void CompilerGLSL::emit_subgroup_arithmetic_workaround(const std::string &func, Op op, GroupOperation group_op) +{ + std::string result; + switch (group_op) + { + case GroupOperationReduce: + result = "reduction"; + break; + + case GroupOperationExclusiveScan: + result = "excl_scan"; + break; + + case GroupOperationInclusiveScan: + result = "incl_scan"; + break; + + default: + SPIRV_CROSS_THROW("Unsupported workaround for arithmetic group operation"); + } + + struct TypeInfo + { + std::string type; + std::string identity; + }; + + std::vector type_infos; + switch (op) + { + case OpGroupNonUniformIAdd: + { + type_infos.emplace_back(TypeInfo{ "uint", "0u" }); + type_infos.emplace_back(TypeInfo{ "uvec2", "uvec2(0u)" }); + type_infos.emplace_back(TypeInfo{ "uvec3", "uvec3(0u)" }); + type_infos.emplace_back(TypeInfo{ "uvec4", "uvec4(0u)" }); + type_infos.emplace_back(TypeInfo{ "int", "0" }); + type_infos.emplace_back(TypeInfo{ "ivec2", "ivec2(0)" }); + type_infos.emplace_back(TypeInfo{ "ivec3", "ivec3(0)" }); + type_infos.emplace_back(TypeInfo{ "ivec4", "ivec4(0)" }); + break; + } + + case OpGroupNonUniformFAdd: + { + type_infos.emplace_back(TypeInfo{ "float", "0.0f" }); + type_infos.emplace_back(TypeInfo{ "vec2", "vec2(0.0f)" }); + type_infos.emplace_back(TypeInfo{ "vec3", "vec3(0.0f)" }); + type_infos.emplace_back(TypeInfo{ "vec4", "vec4(0.0f)" }); + // ARB_gpu_shader_fp64 is required in GL4.0 which in turn is required by NV_thread_shuffle + type_infos.emplace_back(TypeInfo{ "double", "0.0LF" }); + type_infos.emplace_back(TypeInfo{ "dvec2", "dvec2(0.0LF)" }); + type_infos.emplace_back(TypeInfo{ "dvec3", "dvec3(0.0LF)" }); + type_infos.emplace_back(TypeInfo{ "dvec4", "dvec4(0.0LF)" }); + break; + } + + case OpGroupNonUniformIMul: + { + type_infos.emplace_back(TypeInfo{ "uint", "1u" }); + type_infos.emplace_back(TypeInfo{ "uvec2", "uvec2(1u)" }); + type_infos.emplace_back(TypeInfo{ "uvec3", "uvec3(1u)" }); + type_infos.emplace_back(TypeInfo{ "uvec4", "uvec4(1u)" }); + type_infos.emplace_back(TypeInfo{ "int", "1" }); + type_infos.emplace_back(TypeInfo{ "ivec2", "ivec2(1)" }); + type_infos.emplace_back(TypeInfo{ "ivec3", "ivec3(1)" }); + type_infos.emplace_back(TypeInfo{ "ivec4", "ivec4(1)" }); + break; + } + + case OpGroupNonUniformFMul: + { + type_infos.emplace_back(TypeInfo{ "float", "1.0f" }); + type_infos.emplace_back(TypeInfo{ "vec2", "vec2(1.0f)" }); + type_infos.emplace_back(TypeInfo{ "vec3", "vec3(1.0f)" }); + type_infos.emplace_back(TypeInfo{ "vec4", "vec4(1.0f)" }); + type_infos.emplace_back(TypeInfo{ "double", "0.0LF" }); + type_infos.emplace_back(TypeInfo{ "dvec2", "dvec2(1.0LF)" }); + type_infos.emplace_back(TypeInfo{ "dvec3", "dvec3(1.0LF)" }); + type_infos.emplace_back(TypeInfo{ "dvec4", "dvec4(1.0LF)" }); + break; + } + + default: + SPIRV_CROSS_THROW("Unsupported workaround for arithmetic group operation"); + } + + const bool op_is_addition = op == OpGroupNonUniformIAdd || op == OpGroupNonUniformFAdd; + const bool op_is_multiplication = op == OpGroupNonUniformIMul || op == OpGroupNonUniformFMul; + std::string op_symbol; + if (op_is_addition) + { + op_symbol = "+="; + } + else if (op_is_multiplication) + { + op_symbol = "*="; + } + + for (const TypeInfo &t : type_infos) + { + statement(t.type, " ", func, "(", t.type, " v)"); + begin_scope(); + statement(t.type, " ", result, " = ", t.identity, ";"); + statement("uvec4 active_threads = subgroupBallot(true);"); + statement("if (subgroupBallotBitCount(active_threads) == gl_SubgroupSize)"); + begin_scope(); + statement("uint total = gl_SubgroupSize / 2u;"); + statement(result, " = v;"); + statement("for (uint i = 1u; i <= total; i <<= 1u)"); + begin_scope(); + statement("bool valid;"); + if (group_op == GroupOperationReduce) + { + statement(t.type, " s = shuffleXorNV(", result, ", i, gl_SubgroupSize, valid);"); + } + else if (group_op == GroupOperationExclusiveScan || group_op == GroupOperationInclusiveScan) + { + statement(t.type, " s = shuffleUpNV(", result, ", i, gl_SubgroupSize, valid);"); + } + if (op_is_addition || op_is_multiplication) + { + statement(result, " ", op_symbol, " valid ? s : ", t.identity, ";"); + } + end_scope(); + if (group_op == GroupOperationExclusiveScan) + { + statement(result, " = shuffleUpNV(", result, ", 1u, gl_SubgroupSize);"); + statement("if (subgroupElect())"); + begin_scope(); + statement(result, " = ", t.identity, ";"); + end_scope(); + } + end_scope(); + statement("else"); + begin_scope(); + if (group_op == GroupOperationExclusiveScan) + { + statement("uint total = subgroupBallotBitCount(gl_SubgroupLtMask);"); + } + else if (group_op == GroupOperationInclusiveScan) + { + statement("uint total = subgroupBallotBitCount(gl_SubgroupLeMask);"); + } + statement("for (uint i = 0u; i < gl_SubgroupSize; ++i)"); + begin_scope(); + statement("bool valid = subgroupBallotBitExtract(active_threads, i);"); + statement(t.type, " s = shuffleNV(v, i, gl_SubgroupSize);"); + if (group_op == GroupOperationExclusiveScan || group_op == GroupOperationInclusiveScan) + { + statement("valid = valid && (i < total);"); + } + if (op_is_addition || op_is_multiplication) + { + statement(result, " ", op_symbol, " valid ? s : ", t.identity, ";"); + } + end_scope(); + end_scope(); + statement("return ", result, ";"); + end_scope(); + } +} + +void CompilerGLSL::emit_extension_workarounds(ExecutionModel model) +{ + static const char *workaround_types[] = { "int", "ivec2", "ivec3", "ivec4", "uint", "uvec2", "uvec3", "uvec4", + "float", "vec2", "vec3", "vec4", "double", "dvec2", "dvec3", "dvec4" }; + + if (!options.vulkan_semantics) + { + using Supp = ShaderSubgroupSupportHelper; + auto result = shader_subgroup_supporter.resolve(); + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupMask)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupMask, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupEqMask uvec4(gl_ThreadEqMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupGeMask uvec4(gl_ThreadGeMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupGtMask uvec4(gl_ThreadGtMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupLeMask uvec4(gl_ThreadLeMaskNV, 0u, 0u, 0u)"); + statement("#define gl_SubgroupLtMask uvec4(gl_ThreadLtMaskNV, 0u, 0u, 0u)"); + break; + case Supp::ARB_shader_ballot: + statement("#define gl_SubgroupEqMask uvec4(unpackUint2x32(gl_SubGroupEqMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupGeMask uvec4(unpackUint2x32(gl_SubGroupGeMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupGtMask uvec4(unpackUint2x32(gl_SubGroupGtMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupLeMask uvec4(unpackUint2x32(gl_SubGroupLeMaskARB), 0u, 0u)"); + statement("#define gl_SubgroupLtMask uvec4(unpackUint2x32(gl_SubGroupLtMaskARB), 0u, 0u)"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupSize)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupSize, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupSize gl_WarpSizeNV"); + break; + case Supp::ARB_shader_ballot: + statement("#define gl_SubgroupSize gl_SubGroupSizeARB"); + break; + case Supp::AMD_gcn_shader: + statement("#define gl_SubgroupSize uint(gl_SIMDGroupSizeAMD)"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupInvocationID)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupInvocationID, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupInvocationID gl_ThreadInWarpNV"); + break; + case Supp::ARB_shader_ballot: + statement("#define gl_SubgroupInvocationID gl_SubGroupInvocationARB"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupID)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupID, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_SubgroupID gl_WarpIDNV"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::NumSubgroups)) + { + auto exts = Supp::get_candidates_for_feature(Supp::NumSubgroups, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("#define gl_NumSubgroups gl_WarpsPerSMNV"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBroadcast_First)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupBroadcast_First, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_shuffle: + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcastFirst(", t, + " value) { return shuffleNV(value, findLSB(ballotThreadNV(true)), gl_WarpSizeNV); }"); + } + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcast(", t, + " value, uint id) { return shuffleNV(value, id, gl_WarpSizeNV); }"); + } + break; + case Supp::ARB_shader_ballot: + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcastFirst(", t, + " value) { return readFirstInvocationARB(value); }"); + } + for (const char *t : workaround_types) + { + statement(t, " subgroupBroadcast(", t, + " value, uint id) { return readInvocationARB(value, id); }"); + } + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallotFindLSB_MSB)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupBallotFindLSB_MSB, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("uint subgroupBallotFindLSB(uvec4 value) { return findLSB(value.x); }"); + statement("uint subgroupBallotFindMSB(uvec4 value) { return findMSB(value.x); }"); + break; + default: + break; + } + } + statement("#else"); + statement("uint subgroupBallotFindLSB(uvec4 value)"); + begin_scope(); + statement("int firstLive = findLSB(value.x);"); + statement("return uint(firstLive != -1 ? firstLive : (findLSB(value.y) + 32));"); + end_scope(); + statement("uint subgroupBallotFindMSB(uvec4 value)"); + begin_scope(); + statement("int firstLive = findMSB(value.y);"); + statement("return uint(firstLive != -1 ? (firstLive + 32) : findMSB(value.x));"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupAll_Any_AllEqualBool)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupAll_Any_AllEqualBool, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_gpu_shader_5: + statement("bool subgroupAll(bool value) { return allThreadsNV(value); }"); + statement("bool subgroupAny(bool value) { return anyThreadNV(value); }"); + statement("bool subgroupAllEqual(bool value) { return allThreadsEqualNV(value); }"); + break; + case Supp::ARB_shader_group_vote: + statement("bool subgroupAll(bool v) { return allInvocationsARB(v); }"); + statement("bool subgroupAny(bool v) { return anyInvocationARB(v); }"); + statement("bool subgroupAllEqual(bool v) { return allInvocationsEqualARB(v); }"); + break; + case Supp::AMD_gcn_shader: + statement("bool subgroupAll(bool value) { return ballotAMD(value) == ballotAMD(true); }"); + statement("bool subgroupAny(bool value) { return ballotAMD(value) != 0ull; }"); + statement("bool subgroupAllEqual(bool value) { uint64_t b = ballotAMD(value); return b == 0ull || " + "b == ballotAMD(true); }"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupAllEqualT)) + { + statement("#ifndef GL_KHR_shader_subgroup_vote"); + statement( + "#define _SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(type) bool subgroupAllEqual(type value) { return " + "subgroupAllEqual(subgroupBroadcastFirst(value) == value); }"); + for (const char *t : workaround_types) + statement("_SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND(", t, ")"); + statement("#undef _SPIRV_CROSS_SUBGROUP_ALL_EQUAL_WORKAROUND"); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallot)) + { + auto exts = Supp::get_candidates_for_feature(Supp::SubgroupBallot, result); + + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_group: + statement("uvec4 subgroupBallot(bool v) { return uvec4(ballotThreadNV(v), 0u, 0u, 0u); }"); + break; + case Supp::ARB_shader_ballot: + statement("uvec4 subgroupBallot(bool v) { return uvec4(unpackUint2x32(ballotARB(v)), 0u, 0u); }"); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupElect)) + { + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("bool subgroupElect()"); + begin_scope(); + statement("uvec4 activeMask = subgroupBallot(true);"); + statement("uint firstLive = subgroupBallotFindLSB(activeMask);"); + statement("return gl_SubgroupInvocationID == firstLive;"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBarrier)) + { + // Extensions we're using in place of GL_KHR_shader_subgroup_basic state + // that subgroup execute in lockstep so this barrier is implicit. + // However the GL 4.6 spec also states that `barrier` implies a shared memory barrier, + // and a specific test of optimizing scans by leveraging lock-step invocation execution, + // has shown that a `memoryBarrierShared` is needed in place of a `subgroupBarrier`. + // https://github.com/buildaworldnet/IrrlichtBAW/commit/d8536857991b89a30a6b65d29441e51b64c2c7ad#diff-9f898d27be1ea6fc79b03d9b361e299334c1a347b6e4dc344ee66110c6aa596aR19 + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("void subgroupBarrier() { memoryBarrierShared(); }"); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupMemBarrier)) + { + if (model == ExecutionModelGLCompute) + { + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("void subgroupMemoryBarrier() { groupMemoryBarrier(); }"); + statement("void subgroupMemoryBarrierBuffer() { groupMemoryBarrier(); }"); + statement("void subgroupMemoryBarrierShared() { memoryBarrierShared(); }"); + statement("void subgroupMemoryBarrierImage() { groupMemoryBarrier(); }"); + statement("#endif"); + } + else + { + statement("#ifndef GL_KHR_shader_subgroup_basic"); + statement("void subgroupMemoryBarrier() { memoryBarrier(); }"); + statement("void subgroupMemoryBarrierBuffer() { memoryBarrierBuffer(); }"); + statement("void subgroupMemoryBarrierImage() { memoryBarrierImage(); }"); + statement("#endif"); + } + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupInverseBallot_InclBitCount_ExclBitCout)) + { + statement("#ifndef GL_KHR_shader_subgroup_ballot"); + statement("bool subgroupInverseBallot(uvec4 value)"); + begin_scope(); + statement("return any(notEqual(value.xy & gl_SubgroupEqMask.xy, uvec2(0u)));"); + end_scope(); + + statement("uint subgroupBallotInclusiveBitCount(uvec4 value)"); + begin_scope(); + statement("uvec2 v = value.xy & gl_SubgroupLeMask.xy;"); + statement("ivec2 c = bitCount(v);"); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("return uint(c.x);"); + statement_no_indent("#else"); + statement("return uint(c.x + c.y);"); + statement_no_indent("#endif"); + end_scope(); + + statement("uint subgroupBallotExclusiveBitCount(uvec4 value)"); + begin_scope(); + statement("uvec2 v = value.xy & gl_SubgroupLtMask.xy;"); + statement("ivec2 c = bitCount(v);"); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("return uint(c.x);"); + statement_no_indent("#else"); + statement("return uint(c.x + c.y);"); + statement_no_indent("#endif"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallotBitCount)) + { + statement("#ifndef GL_KHR_shader_subgroup_ballot"); + statement("uint subgroupBallotBitCount(uvec4 value)"); + begin_scope(); + statement("ivec2 c = bitCount(value.xy);"); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("return uint(c.x);"); + statement_no_indent("#else"); + statement("return uint(c.x + c.y);"); + statement_no_indent("#endif"); + end_scope(); + statement("#endif"); + statement(""); + } + + if (shader_subgroup_supporter.is_feature_requested(Supp::SubgroupBallotBitExtract)) + { + statement("#ifndef GL_KHR_shader_subgroup_ballot"); + statement("bool subgroupBallotBitExtract(uvec4 value, uint index)"); + begin_scope(); + statement_no_indent("#ifdef GL_NV_shader_thread_group"); + statement("uint shifted = value.x >> index;"); + statement_no_indent("#else"); + statement("uint shifted = value[index >> 5u] >> (index & 0x1fu);"); + statement_no_indent("#endif"); + statement("return (shifted & 1u) != 0u;"); + end_scope(); + statement("#endif"); + statement(""); + } + + auto arithmetic_feature_helper = + [&](Supp::Feature feat, std::string func_name, Op op, GroupOperation group_op) + { + if (shader_subgroup_supporter.is_feature_requested(feat)) + { + auto exts = Supp::get_candidates_for_feature(feat, result); + for (auto &e : exts) + { + const char *name = Supp::get_extension_name(e); + statement(&e == &exts.front() ? "#if" : "#elif", " defined(", name, ")"); + + switch (e) + { + case Supp::NV_shader_thread_shuffle: + emit_subgroup_arithmetic_workaround(func_name, op, group_op); + break; + default: + break; + } + } + statement("#endif"); + statement(""); + } + }; + + arithmetic_feature_helper(Supp::SubgroupArithmeticIAddReduce, "subgroupAdd", OpGroupNonUniformIAdd, + GroupOperationReduce); + arithmetic_feature_helper(Supp::SubgroupArithmeticIAddExclusiveScan, "subgroupExclusiveAdd", + OpGroupNonUniformIAdd, GroupOperationExclusiveScan); + arithmetic_feature_helper(Supp::SubgroupArithmeticIAddInclusiveScan, "subgroupInclusiveAdd", + OpGroupNonUniformIAdd, GroupOperationInclusiveScan); + arithmetic_feature_helper(Supp::SubgroupArithmeticFAddReduce, "subgroupAdd", OpGroupNonUniformFAdd, + GroupOperationReduce); + arithmetic_feature_helper(Supp::SubgroupArithmeticFAddExclusiveScan, "subgroupExclusiveAdd", + OpGroupNonUniformFAdd, GroupOperationExclusiveScan); + arithmetic_feature_helper(Supp::SubgroupArithmeticFAddInclusiveScan, "subgroupInclusiveAdd", + OpGroupNonUniformFAdd, GroupOperationInclusiveScan); + + arithmetic_feature_helper(Supp::SubgroupArithmeticIMulReduce, "subgroupMul", OpGroupNonUniformIMul, + GroupOperationReduce); + arithmetic_feature_helper(Supp::SubgroupArithmeticIMulExclusiveScan, "subgroupExclusiveMul", + OpGroupNonUniformIMul, GroupOperationExclusiveScan); + arithmetic_feature_helper(Supp::SubgroupArithmeticIMulInclusiveScan, "subgroupInclusiveMul", + OpGroupNonUniformIMul, GroupOperationInclusiveScan); + arithmetic_feature_helper(Supp::SubgroupArithmeticFMulReduce, "subgroupMul", OpGroupNonUniformFMul, + GroupOperationReduce); + arithmetic_feature_helper(Supp::SubgroupArithmeticFMulExclusiveScan, "subgroupExclusiveMul", + OpGroupNonUniformFMul, GroupOperationExclusiveScan); + arithmetic_feature_helper(Supp::SubgroupArithmeticFMulInclusiveScan, "subgroupInclusiveMul", + OpGroupNonUniformFMul, GroupOperationInclusiveScan); + } + + if (!workaround_ubo_load_overload_types.empty()) + { + for (auto &type_id : workaround_ubo_load_overload_types) + { + auto &type = get(type_id); + + if (options.es && is_matrix(type)) + { + // Need both variants. + // GLSL cannot overload on precision, so need to dispatch appropriately. + statement("highp ", type_to_glsl(type), " spvWorkaroundRowMajor(highp ", type_to_glsl(type), " wrap) { return wrap; }"); + statement("mediump ", type_to_glsl(type), " spvWorkaroundRowMajorMP(mediump ", type_to_glsl(type), " wrap) { return wrap; }"); + } + else + { + statement(type_to_glsl(type), " spvWorkaroundRowMajor(", type_to_glsl(type), " wrap) { return wrap; }"); + } + } + statement(""); + } +} + +void CompilerGLSL::emit_polyfills(uint32_t polyfills, bool relaxed) +{ + const char *qual = ""; + const char *suffix = (options.es && relaxed) ? "MP" : ""; + if (options.es) + qual = relaxed ? "mediump " : "highp "; + + if (polyfills & PolyfillTranspose2x2) + { + statement(qual, "mat2 spvTranspose", suffix, "(", qual, "mat2 m)"); + begin_scope(); + statement("return mat2(m[0][0], m[1][0], m[0][1], m[1][1]);"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillTranspose3x3) + { + statement(qual, "mat3 spvTranspose", suffix, "(", qual, "mat3 m)"); + begin_scope(); + statement("return mat3(m[0][0], m[1][0], m[2][0], m[0][1], m[1][1], m[2][1], m[0][2], m[1][2], m[2][2]);"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillTranspose4x4) + { + statement(qual, "mat4 spvTranspose", suffix, "(", qual, "mat4 m)"); + begin_scope(); + statement("return mat4(m[0][0], m[1][0], m[2][0], m[3][0], m[0][1], m[1][1], m[2][1], m[3][1], m[0][2], " + "m[1][2], m[2][2], m[3][2], m[0][3], m[1][3], m[2][3], m[3][3]);"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillDeterminant2x2) + { + statement(qual, "float spvDeterminant", suffix, "(", qual, "mat2 m)"); + begin_scope(); + statement("return m[0][0] * m[1][1] - m[0][1] * m[1][0];"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillDeterminant3x3) + { + statement(qual, "float spvDeterminant", suffix, "(", qual, "mat3 m)"); + begin_scope(); + statement("return dot(m[0], vec3(m[1][1] * m[2][2] - m[1][2] * m[2][1], " + "m[1][2] * m[2][0] - m[1][0] * m[2][2], " + "m[1][0] * m[2][1] - m[1][1] * m[2][0]));"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillDeterminant4x4) + { + statement(qual, "float spvDeterminant", suffix, "(", qual, "mat4 m)"); + begin_scope(); + statement("return dot(m[0], vec4(" + "m[2][1] * m[3][2] * m[1][3] - m[3][1] * m[2][2] * m[1][3] + m[3][1] * m[1][2] * m[2][3] - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] + m[1][1] * m[2][2] * m[3][3], " + "m[3][0] * m[2][2] * m[1][3] - m[2][0] * m[3][2] * m[1][3] - m[3][0] * m[1][2] * m[2][3] + m[1][0] * m[3][2] * m[2][3] + m[2][0] * m[1][2] * m[3][3] - m[1][0] * m[2][2] * m[3][3], " + "m[2][0] * m[3][1] * m[1][3] - m[3][0] * m[2][1] * m[1][3] + m[3][0] * m[1][1] * m[2][3] - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] + m[1][0] * m[2][1] * m[3][3], " + "m[3][0] * m[2][1] * m[1][2] - m[2][0] * m[3][1] * m[1][2] - m[3][0] * m[1][1] * m[2][2] + m[1][0] * m[3][1] * m[2][2] + m[2][0] * m[1][1] * m[3][2] - m[1][0] * m[2][1] * m[3][2]));"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillMatrixInverse2x2) + { + statement(qual, "mat2 spvInverse", suffix, "(", qual, "mat2 m)"); + begin_scope(); + statement("return mat2(m[1][1], -m[0][1], -m[1][0], m[0][0]) " + "* (1.0 / (m[0][0] * m[1][1] - m[1][0] * m[0][1]));"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillMatrixInverse3x3) + { + statement(qual, "mat3 spvInverse", suffix, "(", qual, "mat3 m)"); + begin_scope(); + statement(qual, "vec3 t = vec3(m[1][1] * m[2][2] - m[1][2] * m[2][1], m[1][2] * m[2][0] - m[1][0] * m[2][2], m[1][0] * m[2][1] - m[1][1] * m[2][0]);"); + statement("return mat3(t[0], " + "m[0][2] * m[2][1] - m[0][1] * m[2][2], " + "m[0][1] * m[1][2] - m[0][2] * m[1][1], " + "t[1], " + "m[0][0] * m[2][2] - m[0][2] * m[2][0], " + "m[0][2] * m[1][0] - m[0][0] * m[1][2], " + "t[2], " + "m[0][1] * m[2][0] - m[0][0] * m[2][1], " + "m[0][0] * m[1][1] - m[0][1] * m[1][0]) " + "* (1.0 / dot(m[0], t));"); + end_scope(); + statement(""); + } + + if (polyfills & PolyfillMatrixInverse4x4) + { + statement(qual, "mat4 spvInverse", suffix, "(", qual, "mat4 m)"); + begin_scope(); + statement(qual, "vec4 t = vec4(" + "m[2][1] * m[3][2] * m[1][3] - m[3][1] * m[2][2] * m[1][3] + m[3][1] * m[1][2] * m[2][3] - m[1][1] * m[3][2] * m[2][3] - m[2][1] * m[1][2] * m[3][3] + m[1][1] * m[2][2] * m[3][3], " + "m[3][0] * m[2][2] * m[1][3] - m[2][0] * m[3][2] * m[1][3] - m[3][0] * m[1][2] * m[2][3] + m[1][0] * m[3][2] * m[2][3] + m[2][0] * m[1][2] * m[3][3] - m[1][0] * m[2][2] * m[3][3], " + "m[2][0] * m[3][1] * m[1][3] - m[3][0] * m[2][1] * m[1][3] + m[3][0] * m[1][1] * m[2][3] - m[1][0] * m[3][1] * m[2][3] - m[2][0] * m[1][1] * m[3][3] + m[1][0] * m[2][1] * m[3][3], " + "m[3][0] * m[2][1] * m[1][2] - m[2][0] * m[3][1] * m[1][2] - m[3][0] * m[1][1] * m[2][2] + m[1][0] * m[3][1] * m[2][2] + m[2][0] * m[1][1] * m[3][2] - m[1][0] * m[2][1] * m[3][2]);"); + statement("return mat4(" + "t[0], " + "m[3][1] * m[2][2] * m[0][3] - m[2][1] * m[3][2] * m[0][3] - m[3][1] * m[0][2] * m[2][3] + m[0][1] * m[3][2] * m[2][3] + m[2][1] * m[0][2] * m[3][3] - m[0][1] * m[2][2] * m[3][3], " + "m[1][1] * m[3][2] * m[0][3] - m[3][1] * m[1][2] * m[0][3] + m[3][1] * m[0][2] * m[1][3] - m[0][1] * m[3][2] * m[1][3] - m[1][1] * m[0][2] * m[3][3] + m[0][1] * m[1][2] * m[3][3], " + "m[2][1] * m[1][2] * m[0][3] - m[1][1] * m[2][2] * m[0][3] - m[2][1] * m[0][2] * m[1][3] + m[0][1] * m[2][2] * m[1][3] + m[1][1] * m[0][2] * m[2][3] - m[0][1] * m[1][2] * m[2][3], " + "t[1], " + "m[2][0] * m[3][2] * m[0][3] - m[3][0] * m[2][2] * m[0][3] + m[3][0] * m[0][2] * m[2][3] - m[0][0] * m[3][2] * m[2][3] - m[2][0] * m[0][2] * m[3][3] + m[0][0] * m[2][2] * m[3][3], " + "m[3][0] * m[1][2] * m[0][3] - m[1][0] * m[3][2] * m[0][3] - m[3][0] * m[0][2] * m[1][3] + m[0][0] * m[3][2] * m[1][3] + m[1][0] * m[0][2] * m[3][3] - m[0][0] * m[1][2] * m[3][3], " + "m[1][0] * m[2][2] * m[0][3] - m[2][0] * m[1][2] * m[0][3] + m[2][0] * m[0][2] * m[1][3] - m[0][0] * m[2][2] * m[1][3] - m[1][0] * m[0][2] * m[2][3] + m[0][0] * m[1][2] * m[2][3], " + "t[2], " + "m[3][0] * m[2][1] * m[0][3] - m[2][0] * m[3][1] * m[0][3] - m[3][0] * m[0][1] * m[2][3] + m[0][0] * m[3][1] * m[2][3] + m[2][0] * m[0][1] * m[3][3] - m[0][0] * m[2][1] * m[3][3], " + "m[1][0] * m[3][1] * m[0][3] - m[3][0] * m[1][1] * m[0][3] + m[3][0] * m[0][1] * m[1][3] - m[0][0] * m[3][1] * m[1][3] - m[1][0] * m[0][1] * m[3][3] + m[0][0] * m[1][1] * m[3][3], " + "m[2][0] * m[1][1] * m[0][3] - m[1][0] * m[2][1] * m[0][3] - m[2][0] * m[0][1] * m[1][3] + m[0][0] * m[2][1] * m[1][3] + m[1][0] * m[0][1] * m[2][3] - m[0][0] * m[1][1] * m[2][3], " + "t[3], " + "m[2][0] * m[3][1] * m[0][2] - m[3][0] * m[2][1] * m[0][2] + m[3][0] * m[0][1] * m[2][2] - m[0][0] * m[3][1] * m[2][2] - m[2][0] * m[0][1] * m[3][2] + m[0][0] * m[2][1] * m[3][2], " + "m[3][0] * m[1][1] * m[0][2] - m[1][0] * m[3][1] * m[0][2] - m[3][0] * m[0][1] * m[1][2] + m[0][0] * m[3][1] * m[1][2] + m[1][0] * m[0][1] * m[3][2] - m[0][0] * m[1][1] * m[3][2], " + "m[1][0] * m[2][1] * m[0][2] - m[2][0] * m[1][1] * m[0][2] + m[2][0] * m[0][1] * m[1][2] - m[0][0] * m[2][1] * m[1][2] - m[1][0] * m[0][1] * m[2][2] + m[0][0] * m[1][1] * m[2][2]) " + "* (1.0 / dot(m[0], t));"); + end_scope(); + statement(""); + } + + if (!relaxed) + { + static const Polyfill polys[3][3] = { + { PolyfillNMin16, PolyfillNMin32, PolyfillNMin64 }, + { PolyfillNMax16, PolyfillNMax32, PolyfillNMax64 }, + { PolyfillNClamp16, PolyfillNClamp32, PolyfillNClamp64 }, + }; + + static const GLSLstd450 glsl_ops[] = { GLSLstd450NMin, GLSLstd450NMax, GLSLstd450NClamp }; + static const char *spv_ops[] = { "spvNMin", "spvNMax", "spvNClamp" }; + bool has_poly = false; + + for (uint32_t i = 0; i < 3; i++) + { + for (uint32_t j = 0; j < 3; j++) + { + if ((polyfills & polys[i][j]) == 0) + continue; + + const char *types[3][4] = { + { "float16_t", "f16vec2", "f16vec3", "f16vec4" }, + { "float", "vec2", "vec3", "vec4" }, + { "double", "dvec2", "dvec3", "dvec4" }, + }; + + for (uint32_t k = 0; k < 4; k++) + { + auto *type = types[j][k]; + + if (i < 2) + { + statement("spirv_instruction(set = \"GLSL.std.450\", id = ", glsl_ops[i], ") ", + type, " ", spv_ops[i], "(", type, ", ", type, ");"); + } + else + { + statement("spirv_instruction(set = \"GLSL.std.450\", id = ", glsl_ops[i], ") ", + type, " ", spv_ops[i], "(", type, ", ", type, ", ", type, ");"); + } + + has_poly = true; + } + } + } + + if (has_poly) + statement(""); + } + else + { + // Mediump intrinsics don't work correctly, so wrap the intrinsic in an outer shell that ensures mediump + // propagation. + + static const Polyfill polys[3][3] = { + { PolyfillNMin16, PolyfillNMin32, PolyfillNMin64 }, + { PolyfillNMax16, PolyfillNMax32, PolyfillNMax64 }, + { PolyfillNClamp16, PolyfillNClamp32, PolyfillNClamp64 }, + }; + + static const char *spv_ops[] = { "spvNMin", "spvNMax", "spvNClamp" }; + + for (uint32_t i = 0; i < 3; i++) + { + for (uint32_t j = 0; j < 3; j++) + { + if ((polyfills & polys[i][j]) == 0) + continue; + + const char *types[3][4] = { + { "float16_t", "f16vec2", "f16vec3", "f16vec4" }, + { "float", "vec2", "vec3", "vec4" }, + { "double", "dvec2", "dvec3", "dvec4" }, + }; + + for (uint32_t k = 0; k < 4; k++) + { + auto *type = types[j][k]; + + if (i < 2) + { + statement("mediump ", type, " ", spv_ops[i], "Relaxed(", + "mediump ", type, " a, mediump ", type, " b)"); + begin_scope(); + statement("mediump ", type, " res = ", spv_ops[i], "(a, b);"); + statement("return res;"); + end_scope(); + statement(""); + } + else + { + statement("mediump ", type, " ", spv_ops[i], "Relaxed(", + "mediump ", type, " a, mediump ", type, " b, mediump ", type, " c)"); + begin_scope(); + statement("mediump ", type, " res = ", spv_ops[i], "(a, b, c);"); + statement("return res;"); + end_scope(); + statement(""); + } + } + } + } + } +} + +// Returns a string representation of the ID, usable as a function arg. +// Default is to simply return the expression representation fo the arg ID. +// Subclasses may override to modify the return value. +string CompilerGLSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) +{ + // BDA expects pointers through function interface. + if (!arg.alias_global_variable && is_physical_or_buffer_pointer(expression_type(id))) + return to_pointer_expression(id); + + // Make sure that we use the name of the original variable, and not the parameter alias. + uint32_t name_id = id; + auto *var = maybe_get(id); + if (var && var->basevariable) + name_id = var->basevariable; + return to_unpacked_expression(name_id); +} + +void CompilerGLSL::force_temporary_and_recompile(uint32_t id) +{ + auto res = forced_temporaries.insert(id); + + // Forcing new temporaries guarantees forward progress. + if (res.second) + force_recompile_guarantee_forward_progress(); + else + force_recompile(); +} + +uint32_t CompilerGLSL::consume_temporary_in_precision_context(uint32_t type_id, uint32_t id, Options::Precision precision) +{ + // Constants do not have innate precision. + auto handle_type = ir.ids[id].get_type(); + if (handle_type == TypeConstant || handle_type == TypeConstantOp || handle_type == TypeUndef) + return id; + + // Ignore anything that isn't 32-bit values. + auto &type = get(type_id); + if (type.pointer) + return id; + if (type.basetype != SPIRType::Float && type.basetype != SPIRType::UInt && type.basetype != SPIRType::Int) + return id; + + if (precision == Options::DontCare) + { + // If precision is consumed as don't care (operations only consisting of constants), + // we need to bind the expression to a temporary, + // otherwise we have no way of controlling the precision later. + auto itr = forced_temporaries.insert(id); + if (itr.second) + force_recompile_guarantee_forward_progress(); + return id; + } + + auto current_precision = has_decoration(id, DecorationRelaxedPrecision) ? Options::Mediump : Options::Highp; + if (current_precision == precision) + return id; + + auto itr = temporary_to_mirror_precision_alias.find(id); + if (itr == temporary_to_mirror_precision_alias.end()) + { + uint32_t alias_id = ir.increase_bound_by(1); + auto &m = ir.meta[alias_id]; + if (auto *input_m = ir.find_meta(id)) + m = *input_m; + + const char *prefix; + if (precision == Options::Mediump) + { + set_decoration(alias_id, DecorationRelaxedPrecision); + prefix = "mp_copy_"; + } + else + { + unset_decoration(alias_id, DecorationRelaxedPrecision); + prefix = "hp_copy_"; + } + + auto alias_name = join(prefix, to_name(id)); + ParsedIR::sanitize_underscores(alias_name); + set_name(alias_id, alias_name); + + emit_op(type_id, alias_id, to_expression(id), true); + temporary_to_mirror_precision_alias[id] = alias_id; + forced_temporaries.insert(id); + forced_temporaries.insert(alias_id); + force_recompile_guarantee_forward_progress(); + id = alias_id; + } + else + { + id = itr->second; + } + + return id; +} + +void CompilerGLSL::handle_invalid_expression(uint32_t id) +{ + // We tried to read an invalidated expression. + // This means we need another pass at compilation, but next time, + // force temporary variables so that they cannot be invalidated. + force_temporary_and_recompile(id); + + // If the invalid expression happened as a result of a CompositeInsert + // overwrite, we must block this from happening next iteration. + if (composite_insert_overwritten.count(id)) + block_composite_insert_overwrite.insert(id); +} + +// Converts the format of the current expression from packed to unpacked, +// by wrapping the expression in a constructor of the appropriate type. +// GLSL does not support packed formats, so simply return the expression. +// Subclasses that do will override. +string CompilerGLSL::unpack_expression_type(string expr_str, const SPIRType &, uint32_t, bool, bool) +{ + return expr_str; +} + +// Sometimes we proactively enclosed an expression where it turns out we might have not needed it after all. +void CompilerGLSL::strip_enclosed_expression(string &expr) +{ + if (expr.size() < 2 || expr.front() != '(' || expr.back() != ')') + return; + + // Have to make sure that our first and last parens actually enclose everything inside it. + uint32_t paren_count = 0; + for (auto &c : expr) + { + if (c == '(') + paren_count++; + else if (c == ')') + { + paren_count--; + + // If we hit 0 and this is not the final char, our first and final parens actually don't + // enclose the expression, and we cannot strip, e.g.: (a + b) * (c + d). + if (paren_count == 0 && &c != &expr.back()) + return; + } + } + expr.erase(expr.size() - 1, 1); + expr.erase(begin(expr)); +} + +bool CompilerGLSL::needs_enclose_expression(const std::string &expr) +{ + bool need_parens = false; + + // If the expression starts with a unary we need to enclose to deal with cases where we have back-to-back + // unary expressions. + if (!expr.empty()) + { + auto c = expr.front(); + if (c == '-' || c == '+' || c == '!' || c == '~' || c == '&' || c == '*') + need_parens = true; + } + + if (!need_parens) + { + uint32_t paren_count = 0; + for (auto c : expr) + { + if (c == '(' || c == '[') + paren_count++; + else if (c == ')' || c == ']') + { + assert(paren_count); + paren_count--; + } + else if (c == ' ' && paren_count == 0) + { + need_parens = true; + break; + } + } + assert(paren_count == 0); + } + + return need_parens; +} + +string CompilerGLSL::enclose_expression(const string &expr) +{ + // If this expression contains any spaces which are not enclosed by parentheses, + // we need to enclose it so we can treat the whole string as an expression. + // This happens when two expressions have been part of a binary op earlier. + if (needs_enclose_expression(expr)) + return join('(', expr, ')'); + else + return expr; +} + +string CompilerGLSL::dereference_expression(const SPIRType &expr_type, const std::string &expr) +{ + // If this expression starts with an address-of operator ('&'), then + // just return the part after the operator. + // TODO: Strip parens if unnecessary? + if (expr.front() == '&') + return expr.substr(1); + else if (backend.native_pointers) + return join('*', expr); + else if (is_physical_pointer(expr_type) && !is_physical_pointer_to_buffer_block(expr_type)) + return join(enclose_expression(expr), ".value"); + else + return expr; +} + +string CompilerGLSL::address_of_expression(const std::string &expr) +{ + if (expr.size() > 3 && expr[0] == '(' && expr[1] == '*' && expr.back() == ')') + { + // If we have an expression which looks like (*foo), taking the address of it is the same as stripping + // the first two and last characters. We might have to enclose the expression. + // This doesn't work for cases like (*foo + 10), + // but this is an r-value expression which we cannot take the address of anyways. + return enclose_expression(expr.substr(2, expr.size() - 3)); + } + else if (expr.front() == '*') + { + // If this expression starts with a dereference operator ('*'), then + // just return the part after the operator. + return expr.substr(1); + } + else + return join('&', enclose_expression(expr)); +} + +// Just like to_expression except that we enclose the expression inside parentheses if needed. +string CompilerGLSL::to_enclosed_expression(uint32_t id, bool register_expression_read) +{ + return enclose_expression(to_expression(id, register_expression_read)); +} + +// Used explicitly when we want to read a row-major expression, but without any transpose shenanigans. +// need_transpose must be forced to false. +string CompilerGLSL::to_unpacked_row_major_matrix_expression(uint32_t id) +{ + return unpack_expression_type(to_expression(id), expression_type(id), + get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID), + has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked), true); +} + +string CompilerGLSL::to_unpacked_expression(uint32_t id, bool register_expression_read) +{ + // If we need to transpose, it will also take care of unpacking rules. + auto *e = maybe_get(id); + bool need_transpose = e && e->need_transpose; + bool is_remapped = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID); + bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + + if (!need_transpose && (is_remapped || is_packed)) + { + return unpack_expression_type(to_expression(id, register_expression_read), + get_pointee_type(expression_type_id(id)), + get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID), + has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked), false); + } + else + return to_expression(id, register_expression_read); +} + +string CompilerGLSL::to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read) +{ + return enclose_expression(to_unpacked_expression(id, register_expression_read)); +} + +string CompilerGLSL::to_dereferenced_expression(uint32_t id, bool register_expression_read) +{ + auto &type = expression_type(id); + + if (is_pointer(type) && should_dereference(id)) + return dereference_expression(type, to_enclosed_expression(id, register_expression_read)); + else + return to_expression(id, register_expression_read); +} + +string CompilerGLSL::to_pointer_expression(uint32_t id, bool register_expression_read) +{ + auto &type = expression_type(id); + if (is_pointer(type) && expression_is_lvalue(id) && !should_dereference(id)) + return address_of_expression(to_enclosed_expression(id, register_expression_read)); + else + return to_unpacked_expression(id, register_expression_read); +} + +string CompilerGLSL::to_enclosed_pointer_expression(uint32_t id, bool register_expression_read) +{ + auto &type = expression_type(id); + if (is_pointer(type) && expression_is_lvalue(id) && !should_dereference(id)) + return address_of_expression(to_enclosed_expression(id, register_expression_read)); + else + return to_enclosed_unpacked_expression(id, register_expression_read); +} + +string CompilerGLSL::to_extract_component_expression(uint32_t id, uint32_t index) +{ + auto expr = to_enclosed_expression(id); + if (has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked)) + return join(expr, "[", index, "]"); + else + return join(expr, ".", index_to_swizzle(index)); +} + +string CompilerGLSL::to_extract_constant_composite_expression(uint32_t result_type, const SPIRConstant &c, + const uint32_t *chain, uint32_t length) +{ + // It is kinda silly if application actually enter this path since they know the constant up front. + // It is useful here to extract the plain constant directly. + SPIRConstant tmp; + tmp.constant_type = result_type; + auto &composite_type = get(c.constant_type); + assert(composite_type.basetype != SPIRType::Struct && composite_type.array.empty()); + assert(!c.specialization); + + if (is_matrix(composite_type)) + { + if (length == 2) + { + tmp.m.c[0].vecsize = 1; + tmp.m.columns = 1; + tmp.m.c[0].r[0] = c.m.c[chain[0]].r[chain[1]]; + } + else + { + assert(length == 1); + tmp.m.c[0].vecsize = composite_type.vecsize; + tmp.m.columns = 1; + tmp.m.c[0] = c.m.c[chain[0]]; + } + } + else + { + assert(length == 1); + tmp.m.c[0].vecsize = 1; + tmp.m.columns = 1; + tmp.m.c[0].r[0] = c.m.c[0].r[chain[0]]; + } + + return constant_expression(tmp); +} + +string CompilerGLSL::to_rerolled_array_expression(const SPIRType &parent_type, + const string &base_expr, const SPIRType &type) +{ + bool remapped_boolean = parent_type.basetype == SPIRType::Struct && + type.basetype == SPIRType::Boolean && + backend.boolean_in_struct_remapped_type != SPIRType::Boolean; + + SPIRType tmp_type { OpNop }; + if (remapped_boolean) + { + tmp_type = get(type.parent_type); + tmp_type.basetype = backend.boolean_in_struct_remapped_type; + } + else if (type.basetype == SPIRType::Boolean && backend.boolean_in_struct_remapped_type != SPIRType::Boolean) + { + // It's possible that we have an r-value expression that was OpLoaded from a struct. + // We have to reroll this and explicitly cast the input to bool, because the r-value is short. + tmp_type = get(type.parent_type); + remapped_boolean = true; + } + + uint32_t size = to_array_size_literal(type); + auto &parent = get(type.parent_type); + string expr = "{ "; + + for (uint32_t i = 0; i < size; i++) + { + auto subexpr = join(base_expr, "[", convert_to_string(i), "]"); + if (!is_array(parent)) + { + if (remapped_boolean) + subexpr = join(type_to_glsl(tmp_type), "(", subexpr, ")"); + expr += subexpr; + } + else + expr += to_rerolled_array_expression(parent_type, subexpr, parent); + + if (i + 1 < size) + expr += ", "; + } + + expr += " }"; + return expr; +} + +string CompilerGLSL::to_composite_constructor_expression(const SPIRType &parent_type, uint32_t id, bool block_like_type) +{ + auto &type = expression_type(id); + + bool reroll_array = false; + bool remapped_boolean = parent_type.basetype == SPIRType::Struct && + type.basetype == SPIRType::Boolean && + backend.boolean_in_struct_remapped_type != SPIRType::Boolean; + + if (is_array(type)) + { + reroll_array = !backend.array_is_value_type || + (block_like_type && !backend.array_is_value_type_in_buffer_blocks); + + if (remapped_boolean) + { + // Forced to reroll if we have to change bool[] to short[]. + reroll_array = true; + } + } + + if (reroll_array) + { + // For this case, we need to "re-roll" an array initializer from a temporary. + // We cannot simply pass the array directly, since it decays to a pointer and it cannot + // participate in a struct initializer. E.g. + // float arr[2] = { 1.0, 2.0 }; + // Foo foo = { arr }; must be transformed to + // Foo foo = { { arr[0], arr[1] } }; + // The array sizes cannot be deduced from specialization constants since we cannot use any loops. + + // We're only triggering one read of the array expression, but this is fine since arrays have to be declared + // as temporaries anyways. + return to_rerolled_array_expression(parent_type, to_enclosed_expression(id), type); + } + else + { + auto expr = to_unpacked_expression(id); + if (remapped_boolean) + { + auto tmp_type = type; + tmp_type.basetype = backend.boolean_in_struct_remapped_type; + expr = join(type_to_glsl(tmp_type), "(", expr, ")"); + } + + return expr; + } +} + +string CompilerGLSL::to_non_uniform_aware_expression(uint32_t id) +{ + string expr = to_expression(id); + + if (is_descriptor_non_uniform(id)) + convert_non_uniform_expression(expr, id); + + return expr; +} + +string CompilerGLSL::to_atomic_ptr_expression(uint32_t id) +{ + string expr = to_non_uniform_aware_expression(id); + // If we have naked pointer to POD, we need to dereference to get the proper ".value" resolve. + if (should_dereference(id)) + expr = dereference_expression(expression_type(id), expr); + return expr; +} + +string CompilerGLSL::to_expression(uint32_t id, bool register_expression_read) +{ + auto itr = invalid_expressions.find(id); + if (itr != end(invalid_expressions)) + handle_invalid_expression(id); + + if (ir.ids[id].get_type() == TypeExpression) + { + // We might have a more complex chain of dependencies. + // A possible scenario is that we + // + // %1 = OpLoad + // %2 = OpDoSomething %1 %1. here %2 will have a dependency on %1. + // %3 = OpDoSomethingAgain %2 %2. Here %3 will lose the link to %1 since we don't propagate the dependencies like that. + // OpStore %1 %foo // Here we can invalidate %1, and hence all expressions which depend on %1. Only %2 will know since it's part of invalid_expressions. + // %4 = OpDoSomethingAnotherTime %3 %3 // If we forward all expressions we will see %1 expression after store, not before. + // + // However, we can propagate up a list of depended expressions when we used %2, so we can check if %2 is invalid when reading %3 after the store, + // and see that we should not forward reads of the original variable. + auto &expr = get(id); + for (uint32_t dep : expr.expression_dependencies) + if (invalid_expressions.find(dep) != end(invalid_expressions)) + handle_invalid_expression(dep); + } + + if (register_expression_read) + track_expression_read(id); + + switch (ir.ids[id].get_type()) + { + case TypeExpression: + { + auto &e = get(id); + if (e.base_expression) + return to_enclosed_expression(e.base_expression) + e.expression; + else if (e.need_transpose) + { + // This should not be reached for access chains, since we always deal explicitly with transpose state + // when consuming an access chain expression. + uint32_t physical_type_id = get_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID); + bool is_packed = has_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + bool relaxed = has_decoration(id, DecorationRelaxedPrecision); + return convert_row_major_matrix(e.expression, get(e.expression_type), physical_type_id, + is_packed, relaxed); + } + else if (flattened_structs.count(id)) + { + return load_flattened_struct(e.expression, get(e.expression_type)); + } + else + { + if (is_forcing_recompilation()) + { + // During first compilation phase, certain expression patterns can trigger exponential growth of memory. + // Avoid this by returning dummy expressions during this phase. + // Do not use empty expressions here, because those are sentinels for other cases. + return "_"; + } + else + return e.expression; + } + } + + case TypeConstant: + { + auto &c = get(id); + auto &type = get(c.constant_type); + + // WorkGroupSize may be a constant. + if (has_decoration(c.self, DecorationBuiltIn)) + return builtin_to_glsl(BuiltIn(get_decoration(c.self, DecorationBuiltIn)), StorageClassGeneric); + else if (c.specialization) + { + if (backend.workgroup_size_is_hidden) + { + int wg_index = get_constant_mapping_to_workgroup_component(c); + if (wg_index >= 0) + { + auto wg_size = join(builtin_to_glsl(BuiltInWorkgroupSize, StorageClassInput), vector_swizzle(1, wg_index)); + if (type.basetype != SPIRType::UInt) + wg_size = bitcast_expression(type, SPIRType::UInt, wg_size); + return wg_size; + } + } + + if (expression_is_forwarded(id)) + return constant_expression(c); + + return to_name(id); + } + else if (c.is_used_as_lut) + return to_name(id); + else if (type.basetype == SPIRType::Struct && !backend.can_declare_struct_inline) + return to_name(id); + else if (!type.array.empty() && !backend.can_declare_arrays_inline) + return to_name(id); + else + return constant_expression(c); + } + + case TypeConstantOp: + return to_name(id); + + case TypeVariable: + { + auto &var = get(id); + // If we try to use a loop variable before the loop header, we have to redirect it to the static expression, + // the variable has not been declared yet. + if (var.statically_assigned || (var.loop_variable && !var.loop_variable_enable)) + { + // We might try to load from a loop variable before it has been initialized. + // Prefer static expression and fallback to initializer. + if (var.static_expression) + return to_expression(var.static_expression); + else if (var.initializer) + return to_expression(var.initializer); + else + { + // We cannot declare the variable yet, so have to fake it. + uint32_t undef_id = ir.increase_bound_by(1); + return emit_uninitialized_temporary_expression(get_variable_data_type_id(var), undef_id).expression; + } + } + else if (var.deferred_declaration) + { + var.deferred_declaration = false; + return variable_decl(var); + } + else if (flattened_structs.count(id)) + { + return load_flattened_struct(to_name(id), get(var.basetype)); + } + else + { + auto &dec = ir.meta[var.self].decoration; + if (dec.builtin) + return builtin_to_glsl(dec.builtin_type, var.storage); + else + return to_name(id); + } + } + + case TypeCombinedImageSampler: + // This type should never be taken the expression of directly. + // The intention is that texture sampling functions will extract the image and samplers + // separately and take their expressions as needed. + // GLSL does not use this type because OpSampledImage immediately creates a combined image sampler + // expression ala sampler2D(texture, sampler). + SPIRV_CROSS_THROW("Combined image samplers have no default expression representation."); + + case TypeAccessChain: + // We cannot express this type. They only have meaning in other OpAccessChains, OpStore or OpLoad. + SPIRV_CROSS_THROW("Access chains have no default expression representation."); + + default: + return to_name(id); + } +} + +SmallVector CompilerGLSL::get_composite_constant_ids(ConstantID const_id) +{ + if (auto *constant = maybe_get(const_id)) + { + const auto &type = get(constant->constant_type); + if (is_array(type) || type.basetype == SPIRType::Struct) + return constant->subconstants; + if (is_matrix(type)) + return SmallVector(constant->m.id); + if (is_vector(type)) + return SmallVector(constant->m.c[0].id); + SPIRV_CROSS_THROW("Unexpected scalar constant!"); + } + if (!const_composite_insert_ids.count(const_id)) + SPIRV_CROSS_THROW("Unimplemented for this OpSpecConstantOp!"); + return const_composite_insert_ids[const_id]; +} + +void CompilerGLSL::fill_composite_constant(SPIRConstant &constant, TypeID type_id, + const SmallVector &initializers) +{ + auto &type = get(type_id); + constant.specialization = true; + if (is_array(type) || type.basetype == SPIRType::Struct) + { + constant.subconstants = initializers; + } + else if (is_matrix(type)) + { + constant.m.columns = type.columns; + for (uint32_t i = 0; i < type.columns; ++i) + { + constant.m.id[i] = initializers[i]; + constant.m.c[i].vecsize = type.vecsize; + } + } + else if (is_vector(type)) + { + constant.m.c[0].vecsize = type.vecsize; + for (uint32_t i = 0; i < type.vecsize; ++i) + constant.m.c[0].id[i] = initializers[i]; + } + else + SPIRV_CROSS_THROW("Unexpected scalar in SpecConstantOp CompositeInsert!"); +} + +void CompilerGLSL::set_composite_constant(ConstantID const_id, TypeID type_id, + const SmallVector &initializers) +{ + if (maybe_get(const_id)) + { + const_composite_insert_ids[const_id] = initializers; + return; + } + + auto &constant = set(const_id, type_id); + fill_composite_constant(constant, type_id, initializers); + forwarded_temporaries.insert(const_id); +} + +TypeID CompilerGLSL::get_composite_member_type(TypeID type_id, uint32_t member_idx) +{ + auto &type = get(type_id); + if (is_array(type)) + return type.parent_type; + if (type.basetype == SPIRType::Struct) + return type.member_types[member_idx]; + if (is_matrix(type)) + return type.parent_type; + if (is_vector(type)) + return type.parent_type; + SPIRV_CROSS_THROW("Shouldn't reach lower than vector handling OpSpecConstantOp CompositeInsert!"); +} + +string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop) +{ + auto &type = get(cop.basetype); + bool binary = false; + bool unary = false; + string op; + + if (is_legacy() && is_unsigned_opcode(cop.opcode)) + SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy targets."); + + // TODO: Find a clean way to reuse emit_instruction. + switch (cop.opcode) + { + case OpSConvert: + case OpUConvert: + case OpFConvert: + op = type_to_glsl_constructor(type); + break; + +#define GLSL_BOP(opname, x) \ + case Op##opname: \ + binary = true; \ + op = x; \ + break + +#define GLSL_UOP(opname, x) \ + case Op##opname: \ + unary = true; \ + op = x; \ + break + + 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 OpSRem: + { + uint32_t op0 = cop.arguments[0]; + uint32_t op1 = cop.arguments[1]; + return join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "(", + to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")"); + } + + case OpSelect: + { + if (cop.arguments.size() < 3) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + // This one is pretty annoying. It's triggered from + // uint(bool), int(bool) from spec constants. + // 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])) + { + // 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; + } + + case OpVectorShuffle: + { + string expr = type_to_glsl_constructor(type); + expr += "("; + + uint32_t left_components = expression_type(cop.arguments[0]).vecsize; + string left_arg = to_enclosed_expression(cop.arguments[0]); + string right_arg = to_enclosed_expression(cop.arguments[1]); + + for (uint32_t i = 2; i < uint32_t(cop.arguments.size()); i++) + { + uint32_t index = cop.arguments[i]; + if (index == 0xFFFFFFFF) + { + SPIRConstant c; + c.constant_type = type.parent_type; + assert(type.parent_type != ID(0)); + expr += constant_expression(c); + } + else if (index >= left_components) + { + expr += right_arg + "." + "xyzw"[index - left_components]; + } + else + { + expr += left_arg + "." + "xyzw"[index]; + } + + if (i + 1 < uint32_t(cop.arguments.size())) + expr += ", "; + } + + expr += ")"; + return expr; + } + + case OpCompositeExtract: + { + // Trivial vector extracts (of WorkGroupSize typically), + // punch through to the input spec constant if the composite is used as array size. + const auto *c = maybe_get(cop.arguments[0]); + + string expr; + if (c && cop.arguments.size() == 2 && c->is_used_as_array_length && + !backend.supports_spec_constant_array_size && + is_vector(get(c->constant_type))) + { + expr = to_expression(c->specialization_constant_id(0, cop.arguments[1])); + } + else + { + expr = access_chain_internal(cop.arguments[0], &cop.arguments[1], uint32_t(cop.arguments.size() - 1), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr, nullptr); + } + return expr; + } + + case OpCompositeInsert: + { + SmallVector new_init = get_composite_constant_ids(cop.arguments[1]); + uint32_t idx; + uint32_t target_id = cop.self; + uint32_t target_type_id = cop.basetype; + // We have to drill down to the part we want to modify, and create new + // constants for each containing part. + for (idx = 2; idx < cop.arguments.size() - 1; ++idx) + { + uint32_t new_const = ir.increase_bound_by(1); + uint32_t old_const = new_init[cop.arguments[idx]]; + new_init[cop.arguments[idx]] = new_const; + set_composite_constant(target_id, target_type_id, new_init); + new_init = get_composite_constant_ids(old_const); + target_id = new_const; + target_type_id = get_composite_member_type(target_type_id, cop.arguments[idx]); + } + // Now replace the initializer with the one from this instruction. + new_init[cop.arguments[idx]] = cop.arguments[0]; + set_composite_constant(target_id, target_type_id, new_init); + SPIRConstant tmp_const(cop.basetype); + fill_composite_constant(tmp_const, cop.basetype, const_composite_insert_ids[cop.self]); + return constant_expression(tmp_const); + } + + default: + // Some opcodes are unimplemented here, these are currently not possible to test from glslang. + SPIRV_CROSS_THROW("Unimplemented spec constant op."); + } + + uint32_t bit_width = 0; + if (unary || binary || cop.opcode == OpSConvert || cop.opcode == OpUConvert) + bit_width = expression_type(cop.arguments[0]).width; + + SPIRType::BaseType input_type; + bool skip_cast_if_equal_type = opcode_is_sign_invariant(cop.opcode); + + switch (cop.opcode) + { + case OpIEqual: + case OpINotEqual: + input_type = to_signed_basetype(bit_width); + break; + + case OpSLessThan: + case OpSLessThanEqual: + case OpSGreaterThan: + case OpSGreaterThanEqual: + case OpSMod: + case OpSDiv: + case OpShiftRightArithmetic: + case OpSConvert: + case OpSNegate: + input_type = to_signed_basetype(bit_width); + break; + + case OpULessThan: + case OpULessThanEqual: + case OpUGreaterThan: + case OpUGreaterThanEqual: + case OpUMod: + case OpUDiv: + case OpShiftRightLogical: + case OpUConvert: + input_type = to_unsigned_basetype(bit_width); + break; + + default: + input_type = type.basetype; + break; + } + +#undef GLSL_BOP +#undef GLSL_UOP + if (binary) + { + if (cop.arguments.size() < 2) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + string cast_op0; + string cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, cop.arguments[0], + cop.arguments[1], skip_cast_if_equal_type); + + if (type.basetype != input_type && type.basetype != SPIRType::Boolean) + { + expected_type.basetype = input_type; + auto expr = bitcast_glsl_op(type, expected_type); + expr += '('; + expr += join(cast_op0, " ", op, " ", cast_op1); + expr += ')'; + return expr; + } + else + return join("(", cast_op0, " ", op, " ", cast_op1, ")"); + } + else if (unary) + { + if (cop.arguments.size() < 1) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + // Auto-bitcast to result type as needed. + // Works around various casting scenarios in glslang as there is no OpBitcast for specialization constants. + return join("(", op, bitcast_glsl(type, cop.arguments[0]), ")"); + } + else if (cop.opcode == OpSConvert || cop.opcode == OpUConvert) + { + if (cop.arguments.size() < 1) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + + auto &arg_type = expression_type(cop.arguments[0]); + if (arg_type.width < type.width && input_type != arg_type.basetype) + { + auto expected = arg_type; + expected.basetype = input_type; + return join(op, "(", bitcast_glsl(expected, cop.arguments[0]), ")"); + } + else + return join(op, "(", to_expression(cop.arguments[0]), ")"); + } + else + { + if (cop.arguments.size() < 1) + SPIRV_CROSS_THROW("Not enough arguments to OpSpecConstantOp."); + return join(op, "(", to_expression(cop.arguments[0]), ")"); + } +} + +string CompilerGLSL::constant_expression(const SPIRConstant &c, + bool inside_block_like_struct_scope, + bool inside_struct_scope) +{ + auto &type = get(c.constant_type); + + if (is_pointer(type)) + { + return backend.null_pointer_literal; + } + else if (c.is_null_array_specialized_length && backend.requires_matching_array_initializer) + { + require_extension_internal("GL_EXT_null_initializer"); + return backend.constant_null_initializer; + } + else if (c.replicated && type.op != OpTypeArray) + { + if (type.op == OpTypeMatrix) + { + uint32_t num_elements = type.columns; + // GLSL does not allow the replication constructor for matrices + // mat4(vec4(0.0)) needs to be manually expanded to mat4(vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0)); + std::string res; + res += type_to_glsl(type); + res += "("; + for (uint32_t i = 0; i < num_elements; i++) + { + res += to_expression(c.subconstants[0]); + if (i < num_elements - 1) + res += ", "; + } + res += ")"; + return res; + } + else + { + return join(type_to_glsl(type), "(", to_expression(c.subconstants[0]), ")"); + } + } + else if (!c.subconstants.empty()) + { + // Handles Arrays and structures. + string res; + + // Only consider the decay if we are inside a struct scope where we are emitting a member with Offset decoration. + // Outside a block-like struct declaration, we can always bind to a constant array with templated type. + // Should look at ArrayStride here as well, but it's possible to declare a constant struct + // with Offset = 0, using no ArrayStride on the enclosed array type. + // A particular CTS test hits this scenario. + bool array_type_decays = inside_block_like_struct_scope && + is_array(type) && + !backend.array_is_value_type_in_buffer_blocks; + + // Allow Metal to use the array template to make arrays a value type + bool needs_trailing_tracket = false; + if (backend.use_initializer_list && backend.use_typed_initializer_list && type.basetype == SPIRType::Struct && + !is_array(type)) + { + res = type_to_glsl_constructor(type) + "{ "; + } + else if (backend.use_initializer_list && backend.use_typed_initializer_list && backend.array_is_value_type && + is_array(type) && !array_type_decays) + { + const auto *p_type = &type; + SPIRType tmp_type { OpNop }; + + if (inside_struct_scope && + backend.boolean_in_struct_remapped_type != SPIRType::Boolean && + type.basetype == SPIRType::Boolean) + { + tmp_type = type; + tmp_type.basetype = backend.boolean_in_struct_remapped_type; + p_type = &tmp_type; + } + + res = type_to_glsl_constructor(*p_type) + "({ "; + needs_trailing_tracket = true; + } + else if (backend.use_initializer_list) + { + res = "{ "; + } + else + { + res = type_to_glsl_constructor(type) + "("; + } + + uint32_t subconstant_index = 0; + size_t num_elements = c.subconstants.size(); + if (c.replicated) + { + if (type.array.size() != 1) + SPIRV_CROSS_THROW("Multidimensional arrays not yet supported as replicated constans"); + num_elements = type.array[0]; + } + for (size_t i = 0; i < num_elements; i++) + { + auto &elem = c.subconstants[c.replicated ? 0 : i]; + if (auto *op = maybe_get(elem)) + { + res += constant_op_expression(*op); + } + else if (maybe_get(elem) != nullptr) + { + res += to_name(elem); + } + else + { + auto &subc = get(elem); + if (subc.specialization && !expression_is_forwarded(elem)) + res += to_name(elem); + else + { + if (!is_array(type) && type.basetype == SPIRType::Struct) + { + // When we get down to emitting struct members, override the block-like information. + // For constants, we can freely mix and match block-like state. + inside_block_like_struct_scope = + has_member_decoration(type.self, subconstant_index, DecorationOffset); + } + + if (type.basetype == SPIRType::Struct) + inside_struct_scope = true; + + res += constant_expression(subc, inside_block_like_struct_scope, inside_struct_scope); + } + } + + if (i != num_elements - 1) + res += ", "; + + subconstant_index++; + } + + res += backend.use_initializer_list ? " }" : ")"; + if (needs_trailing_tracket) + res += ")"; + + return res; + } + else if (type.basetype == SPIRType::Struct && type.member_types.size() == 0) + { + // Metal tessellation likes empty structs which are then constant expressions. + if (backend.supports_empty_struct) + return "{ }"; + else if (backend.use_typed_initializer_list) + return join(type_to_glsl(type), "{ 0 }"); + else if (backend.use_initializer_list) + return "{ 0 }"; + else + return join(type_to_glsl(type), "(0)"); + } + else if (c.columns() == 1 && type.op != OpTypeCooperativeMatrixKHR) + { + auto res = constant_expression_vector(c, 0); + + if (inside_struct_scope && + backend.boolean_in_struct_remapped_type != SPIRType::Boolean && + type.basetype == SPIRType::Boolean) + { + SPIRType tmp_type = type; + tmp_type.basetype = backend.boolean_in_struct_remapped_type; + res = join(type_to_glsl(tmp_type), "(", res, ")"); + } + + return res; + } + else + { + string res = type_to_glsl(type) + "("; + for (uint32_t col = 0; col < c.columns(); col++) + { + if (c.specialization_constant_id(col) != 0) + res += to_name(c.specialization_constant_id(col)); + else + res += constant_expression_vector(c, col); + + if (col + 1 < c.columns()) + res += ", "; + } + res += ")"; + + if (inside_struct_scope && + backend.boolean_in_struct_remapped_type != SPIRType::Boolean && + type.basetype == SPIRType::Boolean) + { + SPIRType tmp_type = type; + tmp_type.basetype = backend.boolean_in_struct_remapped_type; + res = join(type_to_glsl(tmp_type), "(", res, ")"); + } + + return res; + } +} + +#ifdef _MSC_VER +// snprintf does not exist or is buggy on older MSVC versions, some of them +// being used by MinGW. Use sprintf instead and disable corresponding warning. +#pragma warning(push) +#pragma warning(disable : 4996) +#endif + +string CompilerGLSL::convert_floate4m3_to_string(const SPIRConstant &c, uint32_t col, uint32_t row) +{ + string res; + float float_value = c.scalar_floate4m3(col, row); + + // There is no infinity in e4m3. + if (std::isnan(float_value)) + { + SPIRType type { OpTypeFloat }; + type.basetype = SPIRType::Half; + type.vecsize = 1; + type.columns = 1; + res = join(type_to_glsl(type), "(0.0 / 0.0)"); + } + else + { + SPIRType type { OpTypeFloat }; + type.basetype = SPIRType::FloatE4M3; + type.vecsize = 1; + type.columns = 1; + res = join(type_to_glsl(type), "(", format_float(float_value), ")"); + } + + return res; +} + +string CompilerGLSL::convert_half_to_string(const SPIRConstant &c, uint32_t col, uint32_t row) +{ + string res; + bool is_bfloat8 = get(c.constant_type).basetype == SPIRType::FloatE5M2; + float float_value = is_bfloat8 ? c.scalar_bf8(col, row) : c.scalar_f16(col, row); + + // There is no literal "hf" in GL_NV_gpu_shader5, so to avoid lots + // of complicated workarounds, just value-cast to the half type always. + if (std::isnan(float_value) || std::isinf(float_value)) + { + SPIRType type { OpTypeFloat }; + type.basetype = is_bfloat8 ? SPIRType::FloatE5M2 : SPIRType::Half; + type.vecsize = 1; + type.columns = 1; + + if (float_value == numeric_limits::infinity()) + res = join(type_to_glsl(type), "(1.0 / 0.0)"); + else if (float_value == -numeric_limits::infinity()) + res = join(type_to_glsl(type), "(-1.0 / 0.0)"); + else if (std::isnan(float_value)) + res = join(type_to_glsl(type), "(0.0 / 0.0)"); + else + SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant."); + } + else + { + SPIRType type { OpTypeFloat }; + type.basetype = is_bfloat8 ? SPIRType::FloatE5M2 : SPIRType::Half; + type.vecsize = 1; + type.columns = 1; + res = join(type_to_glsl(type), "(", format_float(float_value), ")"); + } + + return res; +} + +string CompilerGLSL::convert_float_to_string(const SPIRConstant &c, uint32_t col, uint32_t row) +{ + string res; + + bool is_bfloat16 = get(c.constant_type).basetype == SPIRType::BFloat16; + float float_value = is_bfloat16 ? c.scalar_bf16(col, row) : c.scalar_f32(col, row); + + if (std::isnan(float_value) || std::isinf(float_value)) + { + // Use special representation. + if (!is_legacy()) + { + SPIRType out_type { OpTypeFloat }; + SPIRType in_type { OpTypeInt }; + out_type.basetype = SPIRType::Float; + in_type.basetype = SPIRType::UInt; + out_type.vecsize = 1; + in_type.vecsize = 1; + out_type.width = 32; + in_type.width = 32; + + char print_buffer[32]; +#ifdef _WIN32 + sprintf(print_buffer, "0x%xu", c.scalar(col, row)); +#else + snprintf(print_buffer, sizeof(print_buffer), "0x%xu", c.scalar(col, row)); +#endif + + const char *comment = "inf"; + if (float_value == -numeric_limits::infinity()) + comment = "-inf"; + else if (std::isnan(float_value)) + comment = "nan"; + res = join(bitcast_glsl_op(out_type, in_type), "(", print_buffer, " /* ", comment, " */)"); + } + else + { + if (float_value == numeric_limits::infinity()) + { + if (backend.float_literal_suffix) + res = "(1.0f / 0.0f)"; + else + res = "(1.0 / 0.0)"; + } + else if (float_value == -numeric_limits::infinity()) + { + if (backend.float_literal_suffix) + res = "(-1.0f / 0.0f)"; + else + res = "(-1.0 / 0.0)"; + } + else if (std::isnan(float_value)) + { + if (backend.float_literal_suffix) + res = "(0.0f / 0.0f)"; + else + res = "(0.0 / 0.0)"; + } + else + SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant."); + } + } + else + { + res = format_float(float_value); + if (backend.float_literal_suffix) + res += "f"; + } + + if (is_bfloat16) + res = join("bfloat16_t(", res, ")"); + + return res; +} + +std::string CompilerGLSL::convert_double_to_string(const SPIRConstant &c, uint32_t col, uint32_t row) +{ + string res; + double double_value = c.scalar_f64(col, row); + + if (std::isnan(double_value) || std::isinf(double_value)) + { + // Use special representation. + if (!is_legacy()) + { + SPIRType out_type { OpTypeFloat }; + SPIRType in_type { OpTypeInt }; + out_type.basetype = SPIRType::Double; + in_type.basetype = SPIRType::UInt64; + out_type.vecsize = 1; + in_type.vecsize = 1; + out_type.width = 64; + in_type.width = 64; + + uint64_t u64_value = c.scalar_u64(col, row); + + if (options.es && options.version < 310) // GL_NV_gpu_shader5 fallback requires 310. + SPIRV_CROSS_THROW("64-bit integers not supported in ES profile before version 310."); + require_extension_internal("GL_ARB_gpu_shader_int64"); + + char print_buffer[64]; +#ifdef _WIN32 + sprintf(print_buffer, "0x%llx%s", static_cast(u64_value), + backend.long_long_literal_suffix ? "ull" : "ul"); +#else + snprintf(print_buffer, sizeof(print_buffer), "0x%llx%s", static_cast(u64_value), + backend.long_long_literal_suffix ? "ull" : "ul"); +#endif + + const char *comment = "inf"; + if (double_value == -numeric_limits::infinity()) + comment = "-inf"; + else if (std::isnan(double_value)) + comment = "nan"; + res = join(bitcast_glsl_op(out_type, in_type), "(", print_buffer, " /* ", comment, " */)"); + } + else + { + if (options.es) + SPIRV_CROSS_THROW("FP64 not supported in ES profile."); + if (options.version < 400) + require_extension_internal("GL_ARB_gpu_shader_fp64"); + + if (double_value == numeric_limits::infinity()) + { + if (backend.double_literal_suffix) + res = "(1.0lf / 0.0lf)"; + else + res = "(1.0 / 0.0)"; + } + else if (double_value == -numeric_limits::infinity()) + { + if (backend.double_literal_suffix) + res = "(-1.0lf / 0.0lf)"; + else + res = "(-1.0 / 0.0)"; + } + else if (std::isnan(double_value)) + { + if (backend.double_literal_suffix) + res = "(0.0lf / 0.0lf)"; + else + res = "(0.0 / 0.0)"; + } + else + SPIRV_CROSS_THROW("Cannot represent non-finite floating point constant."); + } + } + else + { + res = format_double(double_value); + if (backend.double_literal_suffix) + res += "lf"; + } + + return res; +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +string CompilerGLSL::constant_expression_vector(const SPIRConstant &c, uint32_t vector) +{ + auto type = get(c.constant_type); + type.columns = 1; + + auto scalar_type = type; + scalar_type.vecsize = 1; + + string res; + bool splat = backend.use_constructor_splatting && c.vector_size() > 1; + bool swizzle_splat = backend.can_swizzle_scalar && c.vector_size() > 1; + + if (!type_is_floating_point(type)) + { + // Cannot swizzle literal integers as a special case. + swizzle_splat = false; + } + + if (splat || swizzle_splat) + { + // Cannot use constant splatting if we have specialization constants somewhere in the vector. + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.specialization_constant_id(vector, i) != 0) + { + splat = false; + swizzle_splat = false; + break; + } + } + } + + if (splat || swizzle_splat) + { + if (type.width == 64) + { + uint64_t ident = c.scalar_u64(vector, 0); + for (uint32_t i = 1; i < c.vector_size(); i++) + { + if (ident != c.scalar_u64(vector, i)) + { + splat = false; + swizzle_splat = false; + break; + } + } + } + else + { + uint32_t ident = c.scalar(vector, 0); + for (uint32_t i = 1; i < c.vector_size(); i++) + { + if (ident != c.scalar(vector, i)) + { + splat = false; + swizzle_splat = false; + } + } + } + } + + if (c.vector_size() > 1 && !swizzle_splat) + res += type_to_glsl(type) + "("; + + switch (type.basetype) + { + case SPIRType::FloatE4M3: + if (splat || swizzle_splat) + { + res += convert_floate4m3_to_string(c, vector, 0); + if (swizzle_splat) + res = remap_swizzle(get(c.constant_type), 1, res); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + res += convert_floate4m3_to_string(c, vector, i); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::FloatE5M2: + case SPIRType::Half: + if (splat || swizzle_splat) + { + res += convert_half_to_string(c, vector, 0); + if (swizzle_splat) + res = remap_swizzle(get(c.constant_type), 1, res); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + res += convert_half_to_string(c, vector, i); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::BFloat16: + case SPIRType::Float: + if (splat || swizzle_splat) + { + res += convert_float_to_string(c, vector, 0); + if (swizzle_splat) + res = remap_swizzle(get(c.constant_type), 1, res); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + res += convert_float_to_string(c, vector, i); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Double: + if (splat || swizzle_splat) + { + res += convert_double_to_string(c, vector, 0); + if (swizzle_splat) + res = remap_swizzle(get(c.constant_type), 1, res); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + res += convert_double_to_string(c, vector, i); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Int64: + { + auto tmp = type; + tmp.vecsize = 1; + tmp.columns = 1; + auto int64_type = type_to_glsl(tmp); + + if (splat) + { + res += convert_to_string(c.scalar_i64(vector, 0), int64_type, backend.long_long_literal_suffix); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + res += convert_to_string(c.scalar_i64(vector, i), int64_type, backend.long_long_literal_suffix); + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + } + + case SPIRType::UInt64: + if (splat) + { + res += convert_to_string(c.scalar_u64(vector, 0)); + if (backend.long_long_literal_suffix) + res += "ull"; + else + res += "ul"; + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + { + res += convert_to_string(c.scalar_u64(vector, i)); + if (backend.long_long_literal_suffix) + res += "ull"; + else + res += "ul"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::UInt: + if (splat) + { + res += convert_to_string(c.scalar(vector, 0)); + if (is_legacy() && !has_extension("GL_EXT_gpu_shader4")) + { + // Fake unsigned constant literals with signed ones if possible. + // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed. + if (c.scalar_i32(vector, 0) < 0) + SPIRV_CROSS_THROW("Tried to convert uint literal into int, but this made the literal negative."); + } + else if (backend.uint32_t_literal_suffix) + res += "u"; + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + { + res += convert_to_string(c.scalar(vector, i)); + if (is_legacy() && !has_extension("GL_EXT_gpu_shader4")) + { + // Fake unsigned constant literals with signed ones if possible. + // Things like array sizes, etc, tend to be unsigned even though they could just as easily be signed. + if (c.scalar_i32(vector, i) < 0) + SPIRV_CROSS_THROW("Tried to convert uint literal into int, but this made " + "the literal negative."); + } + else if (backend.uint32_t_literal_suffix) + res += "u"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Int: + if (splat) + res += convert_to_string(c.scalar_i32(vector, 0)); + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + res += convert_to_string(c.scalar_i32(vector, i)); + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::UShort: + if (splat) + { + res += convert_to_string(c.scalar(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + { + if (*backend.uint16_t_literal_suffix) + { + res += convert_to_string(c.scalar_u16(vector, i)); + res += backend.uint16_t_literal_suffix; + } + else + { + // If backend doesn't have a literal suffix, we need to value cast. + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_u16(vector, i)); + res += ")"; + } + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Short: + if (splat) + { + res += convert_to_string(c.scalar_i16(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + { + if (*backend.int16_t_literal_suffix) + { + res += convert_to_string(c.scalar_i16(vector, i)); + res += backend.int16_t_literal_suffix; + } + else + { + // If backend doesn't have a literal suffix, we need to value cast. + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_i16(vector, i)); + res += ")"; + } + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::UByte: + if (splat) + { + res += convert_to_string(c.scalar_u8(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + { + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_u8(vector, i)); + res += ")"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::SByte: + if (splat) + { + res += convert_to_string(c.scalar_i8(vector, 0)); + } + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + { + res += type_to_glsl(scalar_type); + res += "("; + res += convert_to_string(c.scalar_i8(vector, i)); + res += ")"; + } + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + case SPIRType::Boolean: + if (splat) + res += c.scalar(vector, 0) ? "true" : "false"; + else + { + for (uint32_t i = 0; i < c.vector_size(); i++) + { + if (c.vector_size() > 1 && c.specialization_constant_id(vector, i) != 0) + res += to_expression(c.specialization_constant_id(vector, i)); + else + res += c.scalar(vector, i) ? "true" : "false"; + + if (i + 1 < c.vector_size()) + res += ", "; + } + } + break; + + default: + SPIRV_CROSS_THROW("Invalid constant expression basetype."); + } + + if (c.vector_size() > 1 && !swizzle_splat) + res += ")"; + + return res; +} + +SPIRExpression &CompilerGLSL::emit_uninitialized_temporary_expression(uint32_t type, uint32_t id) +{ + forced_temporaries.insert(id); + emit_uninitialized_temporary(type, id); + return set(id, to_name(id), type, true); +} + +void CompilerGLSL::emit_uninitialized_temporary(uint32_t result_type, uint32_t result_id) +{ + // If we're declaring temporaries inside continue blocks, + // we must declare the temporary in the loop header so that the continue block can avoid declaring new variables. + if (!block_temporary_hoisting && current_continue_block && !hoisted_temporaries.count(result_id)) + { + auto &header = get(current_continue_block->loop_dominator); + if (find_if(begin(header.declare_temporary), end(header.declare_temporary), + [result_type, result_id](const pair &tmp) { + return tmp.first == result_type && tmp.second == result_id; + }) == end(header.declare_temporary)) + { + header.declare_temporary.emplace_back(result_type, result_id); + hoisted_temporaries.insert(result_id); + force_recompile(); + } + } + else if (hoisted_temporaries.count(result_id) == 0) + { + auto &type = get(result_type); + auto &flags = get_decoration_bitset(result_id); + + // The result_id has not been made into an expression yet, so use flags interface. + add_local_variable_name(result_id); + + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(result_type)); + + statement(flags_to_qualifiers_glsl(type, result_id, flags), variable_decl(type, to_name(result_id)), initializer, ";"); + } +} + +bool CompilerGLSL::can_declare_inline_temporary(uint32_t id) const +{ + if (!block_temporary_hoisting && current_continue_block && !hoisted_temporaries.count(id)) + return false; + if (hoisted_temporaries.count(id)) + return false; + + return true; +} + +string CompilerGLSL::declare_temporary(uint32_t result_type, uint32_t result_id) +{ + auto &type = get(result_type); + + // If we're declaring temporaries inside continue blocks, + // we must declare the temporary in the loop header so that the continue block can avoid declaring new variables. + if (!block_temporary_hoisting && current_continue_block && !hoisted_temporaries.count(result_id)) + { + auto &header = get(current_continue_block->loop_dominator); + if (find_if(begin(header.declare_temporary), end(header.declare_temporary), + [result_type, result_id](const pair &tmp) { + return tmp.first == result_type && tmp.second == result_id; + }) == end(header.declare_temporary)) + { + header.declare_temporary.emplace_back(result_type, result_id); + hoisted_temporaries.insert(result_id); + force_recompile_guarantee_forward_progress(); + } + + return join(to_name(result_id), " = "); + } + else if (hoisted_temporaries.count(result_id)) + { + // The temporary has already been declared earlier, so just "declare" the temporary by writing to it. + return join(to_name(result_id), " = "); + } + else + { + // The result_id has not been made into an expression yet, so use flags interface. + add_local_variable_name(result_id); + auto &flags = get_decoration_bitset(result_id); + return join(flags_to_qualifiers_glsl(type, result_id, flags), variable_decl(type, to_name(result_id)), " = "); + } +} + +bool CompilerGLSL::expression_is_forwarded(uint32_t id) const +{ + return forwarded_temporaries.count(id) != 0; +} + +bool CompilerGLSL::expression_suppresses_usage_tracking(uint32_t id) const +{ + return suppressed_usage_tracking.count(id) != 0; +} + +bool CompilerGLSL::expression_read_implies_multiple_reads(uint32_t id) const +{ + auto *expr = maybe_get(id); + if (!expr) + return false; + + // If we're emitting code at a deeper loop level than when we emitted the expression, + // we're probably reading the same expression over and over. + return current_loop_level > expr->emitted_loop_level; +} + +SPIRExpression &CompilerGLSL::emit_op(uint32_t result_type, uint32_t result_id, const string &rhs, bool forwarding, + bool suppress_usage_tracking) +{ + if (forwarding && (forced_temporaries.find(result_id) == end(forced_temporaries))) + { + // Just forward it without temporary. + // If the forward is trivial, we do not force flushing to temporary for this expression. + forwarded_temporaries.insert(result_id); + if (suppress_usage_tracking) + suppressed_usage_tracking.insert(result_id); + + return set(result_id, rhs, result_type, true); + } + else + { + // If expression isn't immutable, bind it to a temporary and make the new temporary immutable (they always are). + statement(declare_temporary(result_type, result_id), rhs, ";"); + return set(result_id, to_name(result_id), result_type, true); + } +} + +void CompilerGLSL::emit_transposed_op(uint32_t result_type, uint32_t result_id, const string &rhs, bool forwarding) +{ + if (forwarding && (forced_temporaries.find(result_id) == end(forced_temporaries))) + { + // Just forward it without temporary. + // If the forward is trivial, we do not force flushing to temporary for this expression. + forwarded_temporaries.insert(result_id); + auto &e = set(result_id, rhs, result_type, true); + e.need_transpose = true; + } + else if (can_declare_inline_temporary(result_id)) + { + // If expression isn't immutable, bind it to a temporary and make the new temporary immutable (they always are). + // Since the expression is transposed, we have to ensure the temporary is the transposed type. + + auto &transposed_type_id = extra_sub_expressions[result_id]; + if (!transposed_type_id) + { + auto dummy_type = get(result_type); + std::swap(dummy_type.columns, dummy_type.vecsize); + transposed_type_id = ir.increase_bound_by(1); + set(transposed_type_id, dummy_type); + } + + statement(declare_temporary(transposed_type_id, result_id), rhs, ";"); + auto &e = set(result_id, to_name(result_id), result_type, true); + e.need_transpose = true; + } + else + { + // If we cannot declare the temporary because it's already been hoisted, we don't have the + // chance to override the temporary type ourselves. Just transpose() the expression. + emit_op(result_type, result_id, join("transpose(", rhs, ")"), forwarding); + } +} + +void CompilerGLSL::emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op) +{ + bool forward = should_forward(op0); + emit_op(result_type, result_id, join(op, to_enclosed_unpacked_expression(op0)), forward); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerGLSL::emit_unary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op) +{ + auto &type = get(result_type); + bool forward = should_forward(op0); + emit_op(result_type, result_id, join(type_to_glsl(type), "(", op, to_enclosed_unpacked_expression(op0), ")"), forward); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerGLSL::emit_mesh_tasks(SPIRBlock &block) +{ + statement("EmitMeshTasksEXT(", + to_unpacked_expression(block.mesh.groups[0]), ", ", + to_unpacked_expression(block.mesh.groups[1]), ", ", + to_unpacked_expression(block.mesh.groups[2]), ");"); +} + +void CompilerGLSL::emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op) +{ + // Various FP arithmetic opcodes such as add, sub, mul will hit this. + bool force_temporary_precise = backend.support_precise_qualifier && + has_legacy_nocontract(result_type, result_id) && + type_is_floating_point(get(result_type)); + bool forward = should_forward(op0) && should_forward(op1) && !force_temporary_precise; + + emit_op(result_type, result_id, + join(to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1)), forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op) +{ + auto &type = get(result_type); + auto expr = type_to_glsl_constructor(type); + expr += '('; + for (uint32_t i = 0; i < type.vecsize; i++) + { + // Make sure to call to_expression multiple times to ensure + // that these expressions are properly flushed to temporaries if needed. + expr += op; + expr += to_extract_component_expression(operand, i); + + if (i + 1 < type.vecsize) + expr += ", "; + } + expr += ')'; + emit_op(result_type, result_id, expr, should_forward(operand)); + + inherit_expression_dependencies(result_id, operand); +} + +void CompilerGLSL::emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, bool negate, SPIRType::BaseType expected_type) +{ + auto &type0 = expression_type(op0); + auto &type1 = expression_type(op1); + + SPIRType target_type0 = type0; + SPIRType target_type1 = type1; + target_type0.basetype = expected_type; + target_type1.basetype = expected_type; + target_type0.vecsize = 1; + target_type1.vecsize = 1; + + auto &type = get(result_type); + auto expr = type_to_glsl_constructor(type); + expr += '('; + for (uint32_t i = 0; i < type.vecsize; i++) + { + // Make sure to call to_expression multiple times to ensure + // that these expressions are properly flushed to temporaries if needed. + if (negate) + expr += "!("; + + if (expected_type != SPIRType::Unknown && type0.basetype != expected_type) + expr += bitcast_expression(target_type0, type0.basetype, to_extract_component_expression(op0, i)); + else + expr += to_extract_component_expression(op0, i); + + expr += ' '; + expr += op; + expr += ' '; + + if (expected_type != SPIRType::Unknown && type1.basetype != expected_type) + expr += bitcast_expression(target_type1, type1.basetype, to_extract_component_expression(op1, i)); + else + expr += to_extract_component_expression(op1, i); + + if (negate) + expr += ")"; + + if (i + 1 < type.vecsize) + expr += ", "; + } + expr += ')'; + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +SPIRType CompilerGLSL::binary_op_bitcast_helper(string &cast_op0, string &cast_op1, SPIRType::BaseType &input_type, + uint32_t op0, uint32_t op1, bool skip_cast_if_equal_type) +{ + auto &type0 = expression_type(op0); + auto &type1 = expression_type(op1); + + // We have to bitcast if our inputs are of different type, or if our types are not equal to expected inputs. + // For some functions like OpIEqual and INotEqual, we don't care if inputs are of different types than expected + // since equality test is exactly the same. + bool cast = (type0.basetype != type1.basetype) || (!skip_cast_if_equal_type && type0.basetype != input_type); + + // Create a fake type so we can bitcast to it. + // We only deal with regular arithmetic types here like int, uints and so on. + SPIRType expected_type{type0.op}; + expected_type.basetype = input_type; + expected_type.vecsize = type0.vecsize; + expected_type.columns = type0.columns; + expected_type.width = type0.width; + + if (cast) + { + cast_op0 = bitcast_glsl(expected_type, op0); + cast_op1 = bitcast_glsl(expected_type, op1); + } + else + { + // If we don't cast, our actual input type is that of the first (or second) argument. + cast_op0 = to_enclosed_unpacked_expression(op0); + cast_op1 = to_enclosed_unpacked_expression(op1); + input_type = type0.basetype; + } + + return expected_type; +} + +bool CompilerGLSL::emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) +{ + // Some bitcasts may require complex casting sequences, and are implemented here. + // Otherwise a simply unary function will do with bitcast_glsl_op. + + auto &output_type = get(result_type); + auto &input_type = expression_type(op0); + string expr; + + if (output_type.basetype == SPIRType::Half && input_type.basetype == SPIRType::Float && input_type.vecsize == 1) + expr = join("unpackFloat2x16(floatBitsToUint(", to_unpacked_expression(op0), "))"); + else if (output_type.basetype == SPIRType::Float && input_type.basetype == SPIRType::Half && + input_type.vecsize == 2) + expr = join("uintBitsToFloat(packFloat2x16(", to_unpacked_expression(op0), "))"); + else + return false; + + emit_op(result_type, id, expr, should_forward(op0)); + return true; +} + +void CompilerGLSL::emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type, + bool skip_cast_if_equal_type, + bool implicit_integer_promotion) +{ + string cast_op0, cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); + auto &out_type = get(result_type); + + // We might have casted away from the result type, so bitcast again. + // For example, arithmetic right shift with uint inputs. + // Special case boolean outputs since relational opcodes output booleans instead of int/uint. + auto bitop = join(cast_op0, " ", op, " ", cast_op1); + string expr; + + if (implicit_integer_promotion) + { + // Simple value cast. + expr = join(type_to_glsl(out_type), '(', bitop, ')'); + } + else if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) + { + expected_type.basetype = input_type; + expr = join(bitcast_glsl_op(out_type, expected_type), '(', bitop, ')'); + } + else + { + expr = std::move(bitop); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op) +{ + bool forward = should_forward(op0); + emit_op(result_type, result_id, join(op, "(", to_unpacked_expression(op0), ")"), forward); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerGLSL::emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op) +{ + // Opaque types (e.g. OpTypeSampledImage) must always be forwarded in GLSL + const auto &type = get_type(result_type); + bool must_forward = type_is_opaque_value(type); + bool forward = must_forward || (should_forward(op0) && should_forward(op1)); + emit_op(result_type, result_id, join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ")"), + forward); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op) +{ + auto &type = get(result_type); + if (type_is_floating_point(type)) + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Floating point atomics requires Vulkan semantics."); + if (options.es) + SPIRV_CROSS_THROW("Floating point atomics requires desktop GLSL."); + require_extension_internal("GL_EXT_shader_atomic_float"); + } + + if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64) + require_extension_internal("GL_EXT_shader_atomic_int64"); + + forced_temporaries.insert(result_id); + emit_op(result_type, result_id, + join(op, "(", to_atomic_ptr_expression(op0), ", ", + to_unpacked_expression(op1), ")"), false); + flush_all_atomic_capable_variables(); +} + +void CompilerGLSL::emit_atomic_func_op(uint32_t result_type, uint32_t result_id, + uint32_t op0, uint32_t op1, uint32_t op2, + const char *op) +{ + forced_temporaries.insert(result_id); + emit_op(result_type, result_id, + join(op, "(", to_non_uniform_aware_expression(op0), ", ", + to_unpacked_expression(op1), ", ", to_unpacked_expression(op2), ")"), false); + flush_all_atomic_capable_variables(); +} + +void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op, + SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type) +{ + auto &out_type = get(result_type); + auto &expr_type = expression_type(op0); + auto expected_type = out_type; + + // Bit-widths might be different in unary cases because we use it for SConvert/UConvert and friends. + expected_type.basetype = input_type; + expected_type.width = expr_type.width; + + string cast_op; + if (expr_type.basetype != input_type) + { + if (expr_type.basetype == SPIRType::Boolean) + cast_op = join(type_to_glsl(expected_type), "(", to_unpacked_expression(op0), ")"); + else + cast_op = bitcast_glsl(expected_type, op0); + } + else + cast_op = to_unpacked_expression(op0); + + string expr; + if (out_type.basetype != expected_result_type) + { + expected_type.basetype = expected_result_type; + expected_type.width = out_type.width; + if (out_type.basetype == SPIRType::Boolean) + expr = type_to_glsl(out_type); + else + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0)); + inherit_expression_dependencies(result_id, op0); +} + +// Very special case. Handling bitfieldExtract requires us to deal with different bitcasts of different signs +// and different vector sizes all at once. Need a special purpose method here. +void CompilerGLSL::emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, + SPIRType::BaseType expected_result_type, + SPIRType::BaseType input_type0, SPIRType::BaseType input_type1, + SPIRType::BaseType input_type2) +{ + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type0; + + string cast_op0 = + expression_type(op0).basetype != input_type0 ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + + auto op1_expr = to_unpacked_expression(op1); + auto op2_expr = to_unpacked_expression(op2); + + // Use value casts here instead. Input must be exactly int or uint, but SPIR-V might be 16-bit. + expected_type.basetype = input_type1; + expected_type.vecsize = 1; + string cast_op1 = expression_type(op1).basetype != input_type1 ? + join(type_to_glsl_constructor(expected_type), "(", op1_expr, ")") : + op1_expr; + + expected_type.basetype = input_type2; + expected_type.vecsize = 1; + string cast_op2 = expression_type(op2).basetype != input_type2 ? + join(type_to_glsl_constructor(expected_type), "(", op2_expr, ")") : + op2_expr; + + string expr; + if (out_type.basetype != expected_result_type) + { + expected_type.vecsize = out_type.vecsize; + expected_type.basetype = expected_result_type; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerGLSL::emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, SPIRType::BaseType input_type) +{ + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type; + string cast_op0 = + expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + string cast_op1 = + expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_unpacked_expression(op1); + string cast_op2 = + expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_unpacked_expression(op2); + + string expr; + if (out_type.basetype != input_type) + { + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ", ", cast_op2, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1) && should_forward(op2)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerGLSL::emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, + uint32_t op1, const char *op, SPIRType::BaseType input_type) +{ + // Special purpose method for implementing clustered subgroup opcodes. + // Main difference is that op1 does not participate in any casting, it needs to be a literal. + auto &out_type = get(result_type); + auto expected_type = out_type; + expected_type.basetype = input_type; + string cast_op0 = + expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0); + + string expr; + if (out_type.basetype != input_type) + { + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", to_expression(op1), ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", to_expression(op1), ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0)); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type, bool skip_cast_if_equal_type) +{ + string cast_op0, cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, skip_cast_if_equal_type); + auto &out_type = get(result_type); + + // Special case boolean outputs since relational opcodes output booleans instead of int/uint. + string expr; + if (out_type.basetype != input_type && out_type.basetype != SPIRType::Boolean) + { + expected_type.basetype = input_type; + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + expr += ')'; + } + else + { + expr += join(op, "(", cast_op0, ", ", cast_op1, ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +void CompilerGLSL::emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2); + emit_op(result_type, result_id, + join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ", ", + to_unpacked_expression(op2), ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); +} + +void CompilerGLSL::emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, uint32_t op3, const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3); + emit_op(result_type, result_id, + join(op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), ", ", + to_unpacked_expression(op2), ", ", to_unpacked_expression(op3), ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); + inherit_expression_dependencies(result_id, op3); +} + +void CompilerGLSL::emit_bitfield_insert_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, uint32_t op3, const char *op, + SPIRType::BaseType offset_count_type) +{ + // Only need to cast offset/count arguments. Types of base/insert must be same as result type, + // and bitfieldInsert is sign invariant. + bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3); + + auto op0_expr = to_unpacked_expression(op0); + auto op1_expr = to_unpacked_expression(op1); + auto op2_expr = to_unpacked_expression(op2); + auto op3_expr = to_unpacked_expression(op3); + + assert(offset_count_type == SPIRType::UInt || offset_count_type == SPIRType::Int); + SPIRType target_type { OpTypeInt }; + target_type.width = 32; + target_type.vecsize = 1; + target_type.basetype = offset_count_type; + + if (expression_type(op2).basetype != offset_count_type) + { + // Value-cast here. Input might be 16-bit. GLSL requires int. + op2_expr = join(type_to_glsl_constructor(target_type), "(", op2_expr, ")"); + } + + if (expression_type(op3).basetype != offset_count_type) + { + // Value-cast here. Input might be 16-bit. GLSL requires int. + op3_expr = join(type_to_glsl_constructor(target_type), "(", op3_expr, ")"); + } + + emit_op(result_type, result_id, join(op, "(", op0_expr, ", ", op1_expr, ", ", op2_expr, ", ", op3_expr, ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + inherit_expression_dependencies(result_id, op2); + inherit_expression_dependencies(result_id, op3); +} + +string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t tex) +{ + const char *type; + switch (imgtype.image.dim) + { + case Dim1D: + // Force 2D path for ES. + if (options.es) + type = (imgtype.image.arrayed && !options.es) ? "2DArray" : "2D"; + else + type = (imgtype.image.arrayed && !options.es) ? "1DArray" : "1D"; + break; + case Dim2D: + type = (imgtype.image.arrayed && !options.es) ? "2DArray" : "2D"; + break; + case Dim3D: + type = "3D"; + break; + case DimCube: + type = "Cube"; + break; + case DimRect: + type = "2DRect"; + break; + case DimBuffer: + type = "Buffer"; + break; + case DimSubpassData: + type = "2D"; + break; + default: + type = ""; + break; + } + + // In legacy GLSL, an extension is required for textureLod in the fragment + // shader or textureGrad anywhere. + bool legacy_lod_ext = false; + auto &execution = get_entry_point(); + if (op == "textureGrad" || op == "textureProjGrad" || + ((op == "textureLod" || op == "textureProjLod") && execution.model != ExecutionModelVertex)) + { + if (is_legacy_es()) + { + legacy_lod_ext = true; + require_extension_internal("GL_EXT_shader_texture_lod"); + } + else if (is_legacy_desktop()) + 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 + bool is_comparison = is_depth_image(imgtype, tex); + if (is_comparison && 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")); + + if (imgtype.image.dim == DimCube) + return "shadowCubeNV"; + } + + if (op == "textureSize") + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("textureSize not supported in legacy ES"); + if (is_comparison) + SPIRV_CROSS_THROW("textureSize not supported on shadow sampler in legacy GLSL"); + require_extension_internal("GL_EXT_gpu_shader4"); + } + + if (op == "texelFetch" && is_legacy_es()) + SPIRV_CROSS_THROW("texelFetch not supported in legacy ES"); + + bool is_es_and_depth = is_legacy_es() && is_comparison; + std::string type_prefix = is_comparison ? "shadow" : "texture"; + + if (op == "texture") + return is_es_and_depth ? join(type_prefix, type, "EXT") : join(type_prefix, type); + else if (op == "textureLod") + return join(type_prefix, type, legacy_lod_ext ? "LodEXT" : "Lod"); + else if (op == "textureProj") + return join(type_prefix, type, is_es_and_depth ? "ProjEXT" : "Proj"); + else if (op == "textureGrad") + return join(type_prefix, type, is_legacy_es() ? "GradEXT" : is_legacy_desktop() ? "GradARB" : "Grad"); + else if (op == "textureProjLod") + return join(type_prefix, type, legacy_lod_ext ? "ProjLodEXT" : "ProjLod"); + else if (op == "textureLodOffset") + return join(type_prefix, type, "LodOffset"); + else if (op == "textureProjGrad") + return join(type_prefix, type, + is_legacy_es() ? "ProjGradEXT" : is_legacy_desktop() ? "ProjGradARB" : "ProjGrad"); + else if (op == "textureProjLodOffset") + return join(type_prefix, type, "ProjLodOffset"); + else if (op == "textureSize") + return join("textureSize", type); + else if (op == "texelFetch") + return join("texelFetch", type); + else + { + SPIRV_CROSS_THROW(join("Unsupported legacy texture op: ", op)); + } +} + +bool CompilerGLSL::to_trivial_mix_op(const SPIRType &type, string &op, uint32_t left, uint32_t right, uint32_t lerp) +{ + auto *cleft = maybe_get(left); + auto *cright = maybe_get(right); + auto &lerptype = expression_type(lerp); + + // If our targets aren't constants, we cannot use construction. + if (!cleft || !cright) + return false; + + // If our targets are spec constants, we cannot use construction. + if (cleft->specialization || cright->specialization) + return false; + + auto &value_type = get(cleft->constant_type); + + if (lerptype.basetype != SPIRType::Boolean) + return false; + if (value_type.basetype == SPIRType::Struct || is_array(value_type)) + return false; + if (!backend.use_constructor_splatting && value_type.vecsize != lerptype.vecsize) + return false; + + // Only valid way in SPIR-V 1.4 to use matrices in select is a scalar select. + // matrix(scalar) constructor fills in diagnonals, so gets messy very quickly. + // Just avoid this case. + if (value_type.columns > 1) + return false; + + // If our bool selects between 0 and 1, we can cast from bool instead, making our trivial constructor. + bool ret = true; + for (uint32_t row = 0; ret && row < value_type.vecsize; row++) + { + switch (type.basetype) + { + case SPIRType::Short: + case SPIRType::UShort: + ret = cleft->scalar_u16(0, row) == 0 && cright->scalar_u16(0, row) == 1; + break; + + case SPIRType::Int: + case SPIRType::UInt: + ret = cleft->scalar(0, row) == 0 && cright->scalar(0, row) == 1; + break; + + case SPIRType::Half: + ret = cleft->scalar_f16(0, row) == 0.0f && cright->scalar_f16(0, row) == 1.0f; + break; + + case SPIRType::Float: + ret = cleft->scalar_f32(0, row) == 0.0f && cright->scalar_f32(0, row) == 1.0f; + break; + + case SPIRType::Double: + ret = cleft->scalar_f64(0, row) == 0.0 && cright->scalar_f64(0, row) == 1.0; + break; + + case SPIRType::Int64: + case SPIRType::UInt64: + ret = cleft->scalar_u64(0, row) == 0 && cright->scalar_u64(0, row) == 1; + break; + + default: + ret = false; + break; + } + } + + if (ret) + op = type_to_glsl_constructor(type); + 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_pointer_expression(true_value), " : ", + to_enclosed_pointer_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); + auto &restype = get(result_type); + + // If this results in a variable pointer, assume it may be written through. + if (restype.pointer) + { + register_write(left); + register_write(right); + } + + string mix_op; + bool has_boolean_mix = *backend.boolean_mix_function && + ((options.es && options.version >= 310) || (!options.es && options.version >= 450)); + bool trivial_mix = to_trivial_mix_op(restype, mix_op, left, right, lerp); + + // Cannot use boolean mix when the lerp argument is just one boolean, + // fall back to regular trinary statements. + if (lerptype.vecsize == 1) + has_boolean_mix = false; + + // If we can reduce the mix to a simple cast, do so. + // This helps for cases like int(bool), uint(bool) which is implemented with + // OpSelect bool 1 0. + if (trivial_mix) + { + emit_unary_func_op(result_type, id, lerp, mix_op.c_str()); + } + else if (!has_boolean_mix && lerptype.basetype == SPIRType::Boolean) + { + // Boolean mix not supported on desktop without extension. + // Was added in OpenGL 4.5 with ES 3.1 compat. + // + // Could use GL_EXT_shader_integer_mix on desktop at least, + // but Apple doesn't support it. :( + // Just implement it as ternary expressions. + 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); + inherit_expression_dependencies(id, lerp); + } + else if (lerptype.basetype == SPIRType::Boolean) + emit_trinary_func_op(result_type, id, left, right, lerp, backend.boolean_mix_function); + else + emit_trinary_func_op(result_type, id, left, right, lerp, "mix"); +} + +string CompilerGLSL::to_combined_image_sampler(VariableID image_id, VariableID samp_id) +{ + // Keep track of the array indices we have used to load the image. + // We'll need to use the same array index into the combined image sampler array. + auto image_expr = to_non_uniform_aware_expression(image_id); + string array_expr; + auto array_index = image_expr.find_first_of('['); + if (array_index != string::npos) + array_expr = image_expr.substr(array_index, string::npos); + + auto &args = current_function->arguments; + + // For GLSL and ESSL targets, we must enumerate all possible combinations for sampler2D(texture2D, sampler) and redirect + // all possible combinations into new sampler2D uniforms. + auto *image = maybe_get_backing_variable(image_id); + auto *samp = maybe_get_backing_variable(samp_id); + if (image) + image_id = image->self; + if (samp) + samp_id = samp->self; + + auto image_itr = find_if(begin(args), end(args), + [image_id](const SPIRFunction::Parameter ¶m) { return image_id == param.id; }); + + auto sampler_itr = find_if(begin(args), end(args), + [samp_id](const SPIRFunction::Parameter ¶m) { return samp_id == param.id; }); + + if (image_itr != end(args) || sampler_itr != end(args)) + { + // If any parameter originates from a parameter, we will find it in our argument list. + bool global_image = image_itr == end(args); + bool global_sampler = sampler_itr == end(args); + VariableID iid = global_image ? image_id : VariableID(uint32_t(image_itr - begin(args))); + VariableID sid = global_sampler ? samp_id : VariableID(uint32_t(sampler_itr - begin(args))); + + auto &combined = current_function->combined_parameters; + auto itr = find_if(begin(combined), end(combined), [=](const SPIRFunction::CombinedImageSamplerParameter &p) { + return p.global_image == global_image && p.global_sampler == global_sampler && p.image_id == iid && + p.sampler_id == sid; + }); + + if (itr != end(combined)) + return to_expression(itr->id) + array_expr; + else + { + SPIRV_CROSS_THROW("Cannot find mapping for combined sampler parameter, was " + "build_combined_image_samplers() used " + "before compile() was called?"); + } + } + else + { + // For global sampler2D, look directly at the global remapping table. + auto &mapping = combined_image_samplers; + auto itr = find_if(begin(mapping), end(mapping), [image_id, samp_id](const CombinedImageSampler &combined) { + return combined.image_id == image_id && combined.sampler_id == samp_id; + }); + + if (itr != end(combined_image_samplers)) + return to_expression(itr->combined_id) + array_expr; + else + { + SPIRV_CROSS_THROW("Cannot find mapping for combined sampler, was build_combined_image_samplers() used " + "before compile() was called?"); + } + } +} + +bool CompilerGLSL::is_supported_subgroup_op_in_opengl(Op op, const uint32_t *ops) +{ + switch (op) + { + case OpGroupNonUniformElect: + case OpGroupNonUniformBallot: + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformBroadcastFirst: + case OpGroupNonUniformAll: + case OpGroupNonUniformAny: + case OpGroupNonUniformAllEqual: + case OpControlBarrier: + case OpMemoryBarrier: + case OpGroupNonUniformBallotBitCount: + case OpGroupNonUniformBallotBitExtract: + case OpGroupNonUniformInverseBallot: + return true; + case OpGroupNonUniformIAdd: + case OpGroupNonUniformFAdd: + case OpGroupNonUniformIMul: + case OpGroupNonUniformFMul: + { + const GroupOperation operation = static_cast(ops[3]); + if (operation == GroupOperationReduce || operation == GroupOperationInclusiveScan || + operation == GroupOperationExclusiveScan) + { + return true; + } + else + { + return false; + } + } + default: + return false; + } +} + +void CompilerGLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) +{ + 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), result_id).c_str()); + } + else + { + // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types. + emit_op(result_type, result_id, to_combined_image_sampler(image_id, samp_id), true, true); + } + + // Make sure to suppress usage tracking and any expression invalidation. + // It is illegal to create temporaries of opaque types. + forwarded_temporaries.erase(result_id); +} + +static inline bool image_opcode_is_sample_no_dref(Op op) +{ + switch (op) + { + case OpImageSampleExplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageFetch: + case OpImageRead: + case OpImageSparseSampleExplicitLod: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleProjExplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseFetch: + case OpImageSparseRead: + return true; + + default: + return false; + } +} + +void CompilerGLSL::emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id, uint32_t &feedback_id, + uint32_t &texel_id) +{ + // Need to allocate two temporaries. + if (options.es) + SPIRV_CROSS_THROW("Sparse texture feedback is not supported on ESSL."); + require_extension_internal("GL_ARB_sparse_texture2"); + + auto &temps = extra_sub_expressions[id]; + if (temps == 0) + temps = ir.increase_bound_by(2); + + feedback_id = temps + 0; + texel_id = temps + 1; + + auto &return_type = get(result_type_id); + if (return_type.basetype != SPIRType::Struct || return_type.member_types.size() != 2) + SPIRV_CROSS_THROW("Invalid return type for sparse feedback."); + emit_uninitialized_temporary(return_type.member_types[0], feedback_id); + emit_uninitialized_temporary(return_type.member_types[1], texel_id); +} + +uint32_t CompilerGLSL::get_sparse_feedback_texel_id(uint32_t id) const +{ + auto itr = extra_sub_expressions.find(id); + if (itr == extra_sub_expressions.end()) + return 0; + else + return itr->second + 1; +} + +void CompilerGLSL::emit_texture_op(const Instruction &i, bool sparse) +{ + auto *ops = stream(i); + auto op = static_cast(i.op); + + SmallVector inherited_expressions; + + uint32_t result_type_id = ops[0]; + uint32_t id = ops[1]; + auto &return_type = get(result_type_id); + + uint32_t sparse_code_id = 0; + uint32_t sparse_texel_id = 0; + if (sparse) + emit_sparse_feedback_temporaries(result_type_id, id, sparse_code_id, sparse_texel_id); + + bool forward = false; + string expr = to_texture_op(i, sparse, &forward, inherited_expressions); + + if (sparse) + { + statement(to_expression(sparse_code_id), " = ", expr, ";"); + expr = join(type_to_glsl(return_type), "(", to_expression(sparse_code_id), ", ", to_expression(sparse_texel_id), + ")"); + forward = true; + inherited_expressions.clear(); + } + + emit_op(result_type_id, id, expr, forward); + for (auto &inherit : inherited_expressions) + inherit_expression_dependencies(id, inherit); + + // Do not register sparse ops as control dependent as they are always lowered to a temporary. + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjDrefImplicitLod: + register_control_dependent_expression(id); + break; + + default: + break; + } +} + +std::string CompilerGLSL::to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions) +{ + auto *ops = stream(i); + auto op = static_cast(i.op); + uint32_t length = i.length; + + uint32_t result_type_id = ops[0]; + VariableID img = ops[2]; + uint32_t coord = ops[3]; + uint32_t dref = 0; + uint32_t comp = 0; + bool gather = false; + bool proj = false; + bool fetch = false; + bool nonuniform_expression = false; + const uint32_t *opt = nullptr; + + auto &result_type = get(result_type_id); + + inherited_expressions.push_back(coord); + if (is_descriptor_non_uniform(img) && !maybe_get_backing_variable(img)) + nonuniform_expression = true; + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleDrefExplicitLod: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + break; + + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + proj = true; + break; + + case OpImageDrefGather: + case OpImageSparseDrefGather: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + gather = true; + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("textureGather requires ESSL 310."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("textureGather with depth compare requires GLSL 400."); + break; + + case OpImageGather: + case OpImageSparseGather: + comp = ops[4]; + opt = &ops[5]; + length -= 5; + gather = true; + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("textureGather requires ESSL 310."); + else if (!options.es && options.version < 400) + { + if (!expression_is_constant_null(comp)) + SPIRV_CROSS_THROW("textureGather with component requires GLSL 400."); + require_extension_internal("GL_ARB_texture_gather"); + } + break; + + case OpImageFetch: + case OpImageSparseFetch: + if (options.vulkan_semantics && !dummy_sampler_id && (op == OpImageFetch || op == OpImageSparseFetch)) + require_extension_internal("GL_EXT_samplerless_texture_functions"); + // fallthrough + case OpImageRead: // Reads == fetches in Metal (other langs will not get here) + opt = &ops[4]; + length -= 4; + fetch = true; + break; + + case OpImageSampleProjImplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleProjExplicitLod: + opt = &ops[4]; + length -= 4; + proj = true; + break; + + default: + opt = &ops[4]; + length -= 4; + break; + } + + // Bypass pointers because we need the real image struct + auto &type = expression_type(img); + auto &imgtype = get(type.self); + + uint32_t coord_components = 0; + switch (imgtype.image.dim) + { + case Dim1D: + coord_components = 1; + break; + case Dim2D: + coord_components = 2; + break; + case Dim3D: + coord_components = 3; + break; + case DimCube: + coord_components = 3; + break; + case DimBuffer: + coord_components = 1; + break; + default: + coord_components = 2; + break; + } + + if (dref) + inherited_expressions.push_back(dref); + + if (proj) + coord_components++; + if (imgtype.image.arrayed) + coord_components++; + + uint32_t bias = 0; + uint32_t lod = 0; + uint32_t grad_x = 0; + uint32_t grad_y = 0; + uint32_t coffset = 0; + uint32_t offset = 0; + uint32_t coffsets = 0; + uint32_t sample = 0; + uint32_t minlod = 0; + uint32_t flags = 0; + + if (length) + { + flags = *opt++; + length--; + } + + auto test = [&](uint32_t &v, uint32_t flag) { + if (length && (flags & flag)) + { + v = *opt++; + inherited_expressions.push_back(v); + length--; + } + }; + + test(bias, ImageOperandsBiasMask); + test(lod, ImageOperandsLodMask); + test(grad_x, ImageOperandsGradMask); + test(grad_y, ImageOperandsGradMask); + test(coffset, ImageOperandsConstOffsetMask); + test(offset, ImageOperandsOffsetMask); + test(coffsets, ImageOperandsConstOffsetsMask); + test(sample, ImageOperandsSampleMask); + test(minlod, ImageOperandsMinLodMask); + + TextureFunctionBaseArguments base_args = {}; + base_args.img = img; + base_args.imgtype = &imgtype; + base_args.is_fetch = fetch != 0; + base_args.is_gather = gather != 0; + base_args.is_proj = proj != 0; + + string expr; + + // texture() with bias on sampler2DArrayShadow or samplerCubeArrayShadow requires GL_EXT_texture_shadow_lod. + // textureOffset() with bias on sampler2DArrayShadow also requires it. + if (bias != 0 && dref != 0 && !fetch && !gather && + ((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || + (imgtype.image.arrayed && imgtype.image.dim == DimCube)) && + is_depth_image(imgtype, img)) + { + require_extension_internal("GL_EXT_texture_shadow_lod"); + } + + TextureFunctionNameArguments name_args = {}; + + name_args.base = base_args; + name_args.has_array_offsets = coffsets != 0; + name_args.has_offset = coffset != 0 || offset != 0; + name_args.has_grad = grad_x != 0 || grad_y != 0; + name_args.has_dref = dref != 0; + name_args.is_sparse_feedback = sparse; + name_args.has_min_lod = minlod != 0; + name_args.lod = lod; + expr += to_function_name(name_args); + expr += "("; + + uint32_t sparse_texel_id = 0; + if (sparse) + sparse_texel_id = get_sparse_feedback_texel_id(ops[1]); + + TextureFunctionArguments args = {}; + args.base = base_args; + args.coord = coord; + args.coord_components = coord_components; + args.dref = dref; + args.grad_x = grad_x; + args.grad_y = grad_y; + args.lod = lod; + args.has_array_offsets = coffsets != 0; + + if (coffsets) + args.offset = coffsets; + else if (coffset) + args.offset = coffset; + else + args.offset = offset; + + args.bias = bias; + args.component = comp; + args.sample = sample; + args.sparse_texel = sparse_texel_id; + args.min_lod = minlod; + args.nonuniform_expression = nonuniform_expression; + expr += to_function_args(args, forward); + expr += ")"; + + // texture(samplerXShadow) returns float. shadowX() returns vec4, but only in desktop GLSL. Swizzle here. + if (is_legacy() && !options.es && is_depth_image(imgtype, img)) + expr += ".r"; + + // Sampling from a texture which was deduced to be a depth image, might actually return 1 component here. + // Remap back to 4 components as sampling opcodes expect. + if (backend.comparison_image_samples_scalar && image_opcode_is_sample_no_dref(op)) + { + bool image_is_depth = false; + const auto *combined = maybe_get(img); + VariableID image_id = combined ? combined->image : img; + + if (combined && is_depth_image(imgtype, combined->image)) + image_is_depth = true; + else if (is_depth_image(imgtype, img)) + image_is_depth = true; + + // We must also check the backing variable for the image. + // We might have loaded an OpImage, and used that handle for two different purposes. + // Once with comparison, once without. + auto *image_variable = maybe_get_backing_variable(image_id); + if (image_variable && is_depth_image(get(image_variable->basetype), image_variable->self)) + image_is_depth = true; + + if (image_is_depth) + expr = remap_swizzle(result_type, 1, expr); + } + + if (!sparse && !backend.support_small_type_sampling_result && result_type.width < 32) + { + // Just value cast (narrowing) to expected type since we cannot rely on narrowing to work automatically. + // Hopefully compiler picks this up and converts the texturing instruction to the appropriate precision. + expr = join(type_to_glsl_constructor(result_type), "(", expr, ")"); + } + + // Deals with reads from MSL. We might need to downconvert to fewer components. + if (op == OpImageRead) + expr = remap_swizzle(result_type, 4, expr); + + return expr; +} + +bool CompilerGLSL::expression_is_constant_null(uint32_t id) const +{ + auto *c = maybe_get(id); + if (!c) + return false; + return c->constant_is_null(); +} + +bool CompilerGLSL::expression_is_non_value_type_array(uint32_t value_type_id, uint32_t ptr) +{ + auto &type = get(value_type_id); + if (!is_array(type)) + return false; + + if (!backend.array_is_value_type) + return true; + + if (!backend.array_is_value_type_in_buffer_blocks && maybe_get_backing_buffer_pointer(ptr)) + return true; + + auto *var = maybe_get_backing_variable(ptr); + if (!var) + return false; + + auto &backed_type = get(var->basetype); + + // Only consider explicitly laid out types here, not IO blocks. + return !backend.array_is_value_type_in_buffer_blocks && backed_type.basetype == SPIRType::Struct && + has_member_decoration(backed_type.self, 0, DecorationOffset); +} + +// 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(const TextureFunctionNameArguments &args) +{ + if (args.has_min_lod) + { + if (options.es) + SPIRV_CROSS_THROW("Sparse residency is not supported in ESSL."); + require_extension_internal("GL_ARB_sparse_texture_clamp"); + } + + string fname; + auto &imgtype = *args.base.imgtype; + VariableID tex = args.base.img; + + // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL for some reason. + // To emulate this, we will have to use textureGrad with a constant gradient of 0. + // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code. + // 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) && + is_depth_image(imgtype, tex) && args.lod && !args.base.is_fetch) + { + if (!expression_is_constant_null(args.lod)) + { + require_extension_internal("GL_EXT_texture_shadow_lod"); + } + else + { + workaround_lod_array_shadow_as_grad = true; + } + } + + if (args.is_sparse_feedback) + fname += "sparse"; + + if (args.base.is_fetch) + fname += args.is_sparse_feedback ? "TexelFetch" : "texelFetch"; + else + { + fname += args.is_sparse_feedback ? "Texture" : "texture"; + + if (args.base.is_gather) + fname += "Gather"; + if (args.has_array_offsets) + fname += "Offsets"; + if (args.base.is_proj) + fname += "Proj"; + if (args.has_grad || workaround_lod_array_shadow_as_grad) + fname += "Grad"; + if (args.lod != 0 && !workaround_lod_array_shadow_as_grad) + fname += "Lod"; + } + + if (args.has_offset) + fname += "Offset"; + + if (args.has_min_lod) + fname += "Clamp"; + + if (args.is_sparse_feedback || args.has_min_lod) + fname += "ARB"; + + return (is_legacy() && !args.base.is_gather) ? legacy_tex_op(fname, imgtype, tex) : fname; +} + +std::string CompilerGLSL::convert_separate_image_to_expression(uint32_t id) +{ + auto *var = maybe_get_backing_variable(id); + + // If we are fetching from a plain OpTypeImage, we must combine with a dummy sampler in GLSL. + // In Vulkan GLSL, we can make use of the newer GL_EXT_samplerless_texture_functions. + if (var) + { + auto &type = get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer) + { + if (options.vulkan_semantics) + { + if (dummy_sampler_id) + { + // Don't need to consider Shadow state since the dummy sampler is always non-shadow. + auto sampled_type = type; + sampled_type.basetype = SPIRType::SampledImage; + return join(type_to_glsl(sampled_type), "(", to_non_uniform_aware_expression(id), ", ", + to_expression(dummy_sampler_id), ")"); + } + else + { + // Newer glslang supports this extension to deal with texture2D as argument to texture functions. + require_extension_internal("GL_EXT_samplerless_texture_functions"); + } + } + else + { + if (!dummy_sampler_id) + SPIRV_CROSS_THROW("Cannot find dummy sampler ID. Was " + "build_dummy_sampler_for_combined_images() called?"); + + return to_combined_image_sampler(id, dummy_sampler_id); + } + } + } + + return to_non_uniform_aware_expression(id); +} + +// Returns the function args for a texture sampling function for the specified image and sampling characteristics. +string CompilerGLSL::to_function_args(const TextureFunctionArguments &args, bool *p_forward) +{ + VariableID img = args.base.img; + auto &imgtype = *args.base.imgtype; + + string farg_str; + if (args.base.is_fetch) + farg_str = convert_separate_image_to_expression(img); + else + farg_str = to_non_uniform_aware_expression(img); + + if (args.nonuniform_expression && farg_str.find_first_of('[') != string::npos) + { + // Only emit nonuniformEXT() wrapper if the underlying expression is arrayed in some way. + farg_str = join(backend.nonuniform_qualifier, "(", farg_str, ")"); + } + + bool swizz_func = backend.swizzle_is_function; + auto swizzle = [swizz_func](uint32_t comps, uint32_t in_comps) -> const char * { + if (comps == in_comps) + return ""; + + switch (comps) + { + case 1: + return ".x"; + case 2: + return swizz_func ? ".xy()" : ".xy"; + case 3: + return swizz_func ? ".xyz()" : ".xyz"; + default: + return ""; + } + }; + + bool forward = should_forward(args.coord); + + // The IR can give us more components than we need, so chop them off as needed. + auto swizzle_expr = swizzle(args.coord_components, expression_type(args.coord).vecsize); + // Only enclose the UV expression if needed. + auto coord_expr = + (*swizzle_expr == '\0') ? to_expression(args.coord) : (to_enclosed_expression(args.coord) + swizzle_expr); + + // texelFetch only takes int, not uint. + auto &coord_type = expression_type(args.coord); + if (coord_type.basetype == SPIRType::UInt) + { + auto expected_type = coord_type; + expected_type.vecsize = args.coord_components; + expected_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(expected_type, coord_type.basetype, coord_expr); + } + + // textureLod on sampler2DArrayShadow and samplerCubeShadow does not exist in GLSL for some reason. + // To emulate this, we will have to use textureGrad with a constant gradient of 0. + // The workaround will assert that the LOD is in fact constant 0, or we cannot emit correct code. + // This happens for HLSL SampleCmpLevelZero on Texture2DArray and TextureCube. + // If GL_EXT_texture_shadow_lod is in use, textureLod is available directly with arbitrary LOD. + bool workaround_lod_array_shadow_as_grad = + ((imgtype.image.arrayed && imgtype.image.dim == Dim2D) || imgtype.image.dim == DimCube) && + is_depth_image(imgtype, img) && args.lod != 0 && !args.base.is_fetch && + !has_extension("GL_EXT_texture_shadow_lod"); + + if (args.dref) + { + forward = forward && should_forward(args.dref); + + // SPIR-V splits dref and coordinate. + if (args.base.is_gather || + args.coord_components == 4) // GLSL also splits the arguments in two. Same for textureGather. + { + farg_str += ", "; + farg_str += to_expression(args.coord); + farg_str += ", "; + farg_str += to_expression(args.dref); + } + else if (args.base.is_proj) + { + // Have to reshuffle so we get vec4(coord, dref, proj), special case. + // Other shading languages splits up the arguments for coord and compare value like SPIR-V. + // The coordinate type for textureProj shadow is always vec4 even for sampler1DShadow. + farg_str += ", vec4("; + + if (imgtype.image.dim == Dim1D) + { + // Could reuse coord_expr, but we will mess up the temporary usage checking. + farg_str += to_enclosed_expression(args.coord) + ".x"; + farg_str += ", "; + farg_str += "0.0, "; + farg_str += to_expression(args.dref); + farg_str += ", "; + farg_str += to_enclosed_expression(args.coord) + ".y)"; + } + else if (imgtype.image.dim == Dim2D) + { + // Could reuse coord_expr, but we will mess up the temporary usage checking. + farg_str += to_enclosed_expression(args.coord) + (swizz_func ? ".xy()" : ".xy"); + farg_str += ", "; + farg_str += to_expression(args.dref); + farg_str += ", "; + farg_str += to_enclosed_expression(args.coord) + ".z)"; + } + else + SPIRV_CROSS_THROW("Invalid type for textureProj with shadow."); + } + else + { + // Create a composite which merges coord/dref into a single vector. + auto type = expression_type(args.coord); + type.vecsize = args.coord_components + 1; + if (imgtype.image.dim == Dim1D && options.es) + type.vecsize++; + farg_str += ", "; + farg_str += type_to_glsl_constructor(type); + farg_str += "("; + + if (imgtype.image.dim == Dim1D && options.es) + { + if (imgtype.image.arrayed) + { + farg_str += enclose_expression(coord_expr) + ".x"; + farg_str += ", 0.0, "; + farg_str += enclose_expression(coord_expr) + ".y"; + } + else + { + farg_str += coord_expr; + farg_str += ", 0.0"; + } + } + else + farg_str += coord_expr; + + farg_str += ", "; + farg_str += to_expression(args.dref); + farg_str += ")"; + } + } + else + { + if (imgtype.image.dim == Dim1D && options.es) + { + // Have to fake a second coordinate. + if (type_is_floating_point(coord_type)) + { + // Cannot mix proj and array. + if (imgtype.image.arrayed || args.base.is_proj) + { + coord_expr = join("vec3(", enclose_expression(coord_expr), ".x, 0.0, ", + enclose_expression(coord_expr), ".y)"); + } + else + coord_expr = join("vec2(", coord_expr, ", 0.0)"); + } + else + { + if (imgtype.image.arrayed) + { + coord_expr = join("ivec3(", enclose_expression(coord_expr), + ".x, 0, ", + enclose_expression(coord_expr), ".y)"); + } + else + coord_expr = join("ivec2(", coord_expr, ", 0)"); + } + } + + farg_str += ", "; + farg_str += coord_expr; + } + + if (args.grad_x || args.grad_y) + { + forward = forward && should_forward(args.grad_x); + forward = forward && should_forward(args.grad_y); + farg_str += ", "; + farg_str += to_expression(args.grad_x); + farg_str += ", "; + farg_str += to_expression(args.grad_y); + } + + if (args.lod) + { + if (workaround_lod_array_shadow_as_grad) + { + // Implement textureGrad() instead. LOD == 0.0 is implemented as gradient of 0.0. + // Implementing this as plain texture() is not safe on some implementations. + if (imgtype.image.dim == Dim2D) + farg_str += ", vec2(0.0), vec2(0.0)"; + else if (imgtype.image.dim == DimCube) + farg_str += ", vec3(0.0), vec3(0.0)"; + } + else + { + forward = forward && should_forward(args.lod); + farg_str += ", "; + + // Lod expression for TexelFetch in GLSL must be int, and only int. + if (args.base.is_fetch && imgtype.image.dim != DimBuffer && !imgtype.image.ms) + farg_str += bitcast_expression(SPIRType::Int, args.lod); + else + farg_str += to_expression(args.lod); + } + } + else if (args.base.is_fetch && imgtype.image.dim != DimBuffer && !imgtype.image.ms) + { + // Lod argument is optional in OpImageFetch, but we require a LOD value, pick 0 as the default. + farg_str += ", 0"; + } + + if (args.offset) + { + forward = forward && should_forward(args.offset); + farg_str += ", "; + farg_str += bitcast_expression(SPIRType::Int, args.offset); + } + + if (args.sample) + { + farg_str += ", "; + farg_str += bitcast_expression(SPIRType::Int, args.sample); + } + + if (args.min_lod) + { + farg_str += ", "; + farg_str += to_expression(args.min_lod); + } + + if (args.sparse_texel) + { + // Sparse texel output parameter comes after everything else, except it's before the optional, component/bias arguments. + farg_str += ", "; + farg_str += to_expression(args.sparse_texel); + } + + if (args.bias) + { + forward = forward && should_forward(args.bias); + farg_str += ", "; + farg_str += to_expression(args.bias); + } + + if (args.component && !expression_is_constant_null(args.component)) + { + forward = forward && should_forward(args.component); + farg_str += ", "; + farg_str += bitcast_expression(SPIRType::Int, args.component); + } + + *p_forward = forward; + + return farg_str; +} + +Op CompilerGLSL::get_remapped_spirv_op(Op op) const +{ + if (options.relax_nan_checks) + { + switch (op) + { + case OpFUnordLessThan: + op = OpFOrdLessThan; + break; + case OpFUnordLessThanEqual: + op = OpFOrdLessThanEqual; + break; + case OpFUnordGreaterThan: + op = OpFOrdGreaterThan; + break; + case OpFUnordGreaterThanEqual: + op = OpFOrdGreaterThanEqual; + break; + case OpFUnordEqual: + op = OpFOrdEqual; + break; + case OpFOrdNotEqual: + op = OpFUnordNotEqual; + break; + + default: + break; + } + } + + return op; +} + +GLSLstd450 CompilerGLSL::get_remapped_glsl_op(GLSLstd450 std450_op) const +{ + // Relax to non-NaN aware opcodes. + if (options.relax_nan_checks) + { + switch (std450_op) + { + case GLSLstd450NClamp: + std450_op = GLSLstd450FClamp; + break; + case GLSLstd450NMin: + std450_op = GLSLstd450FMin; + break; + case GLSLstd450NMax: + std450_op = GLSLstd450FMax; + break; + default: + break; + } + } + + return std450_op; +} + +void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t length) +{ + auto op = static_cast(eop); + + if (is_legacy() && is_unsigned_glsl_opcode(op)) + SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy GLSL targets."); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, length); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + op = get_remapped_glsl_op(op); + + switch (op) + { + // FP fiddling + case GLSLstd450Round: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "round"); + else + { + auto op0 = to_enclosed_expression(args[0]); + auto &op0_type = expression_type(args[0]); + auto expr = join("floor(", op0, " + ", type_to_glsl_constructor(op0_type), "(0.5))"); + bool forward = should_forward(args[0]); + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + } + break; + + case GLSLstd450RoundEven: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "roundEven"); + else if (!options.es) + { + // This extension provides round() with round-to-even semantics. + require_extension_internal("GL_EXT_gpu_shader4"); + emit_unary_func_op(result_type, id, args[0], "round"); + } + else + SPIRV_CROSS_THROW("roundEven supported only in ESSL 300."); + break; + + case GLSLstd450Trunc: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "trunc"); + else + { + // Implement by value-casting to int and back. + bool forward = should_forward(args[0]); + auto op0 = to_unpacked_expression(args[0]); + auto &op0_type = expression_type(args[0]); + auto via_type = op0_type; + via_type.basetype = SPIRType::Int; + auto expr = join(type_to_glsl(op0_type), "(", type_to_glsl(via_type), "(", op0, "))"); + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + } + break; + + case GLSLstd450SAbs: + emit_unary_func_op_cast(result_type, id, args[0], "abs", int_type, int_type); + break; + case GLSLstd450FAbs: + emit_unary_func_op(result_type, id, args[0], "abs"); + break; + case GLSLstd450SSign: + emit_unary_func_op_cast(result_type, id, args[0], "sign", int_type, int_type); + break; + case GLSLstd450FSign: + emit_unary_func_op(result_type, id, args[0], "sign"); + break; + case GLSLstd450Floor: + emit_unary_func_op(result_type, id, args[0], "floor"); + break; + case GLSLstd450Ceil: + emit_unary_func_op(result_type, id, args[0], "ceil"); + break; + case GLSLstd450Fract: + emit_unary_func_op(result_type, id, args[0], "fract"); + break; + case GLSLstd450Radians: + emit_unary_func_op(result_type, id, args[0], "radians"); + break; + case GLSLstd450Degrees: + emit_unary_func_op(result_type, id, args[0], "degrees"); + break; + case GLSLstd450Fma: + if ((!options.es && options.version < 400) || (options.es && options.version < 320)) + { + auto expr = join(to_enclosed_expression(args[0]), " * ", to_enclosed_expression(args[1]), " + ", + to_enclosed_expression(args[2])); + + emit_op(result_type, id, expr, + should_forward(args[0]) && should_forward(args[1]) && should_forward(args[2])); + for (uint32_t i = 0; i < 3; i++) + inherit_expression_dependencies(id, args[i]); + } + else + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fma"); + break; + + case GLSLstd450Modf: + register_call_out_argument(args[1]); + if (!is_legacy()) + { + forced_temporaries.insert(id); + emit_binary_func_op(result_type, id, args[0], args[1], "modf"); + } + else + { + //NB. legacy GLSL doesn't have trunc() either, so we do a value cast + auto &op1_type = expression_type(args[1]); + auto via_type = op1_type; + via_type.basetype = SPIRType::Int; + statement(to_expression(args[1]), " = ", + type_to_glsl(op1_type), "(", type_to_glsl(via_type), + "(", to_expression(args[0]), "));"); + emit_binary_op(result_type, id, args[0], args[1], "-"); + } + break; + + case GLSLstd450ModfStruct: + { + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, id); + if (!is_legacy()) + { + statement(to_expression(id), ".", to_member_name(type, 0), " = ", "modf(", to_expression(args[0]), ", ", + to_expression(id), ".", to_member_name(type, 1), ");"); + } + else + { + //NB. legacy GLSL doesn't have trunc() either, so we do a value cast + auto &op0_type = expression_type(args[0]); + auto via_type = op0_type; + via_type.basetype = SPIRType::Int; + statement(to_expression(id), ".", to_member_name(type, 1), " = ", type_to_glsl(op0_type), + "(", type_to_glsl(via_type), "(", to_expression(args[0]), "));"); + statement(to_expression(id), ".", to_member_name(type, 0), " = ", to_enclosed_expression(args[0]), " - ", + to_expression(id), ".", to_member_name(type, 1), ";"); + } + break; + } + + // Minmax + case GLSLstd450UMin: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "min", uint_type, false); + break; + + case GLSLstd450SMin: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "min", int_type, false); + break; + + case GLSLstd450FMin: + emit_binary_func_op(result_type, id, args[0], args[1], "min"); + break; + + case GLSLstd450FMax: + emit_binary_func_op(result_type, id, args[0], args[1], "max"); + break; + + case GLSLstd450UMax: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "max", uint_type, false); + break; + + case GLSLstd450SMax: + emit_binary_func_op_cast(result_type, id, args[0], args[1], "max", int_type, false); + break; + + case GLSLstd450FClamp: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp"); + break; + + case GLSLstd450UClamp: + emit_trinary_func_op_cast(result_type, id, args[0], args[1], args[2], "clamp", uint_type); + break; + + case GLSLstd450SClamp: + emit_trinary_func_op_cast(result_type, id, args[0], args[1], args[2], "clamp", int_type); + break; + + // Trig + case GLSLstd450Sin: + emit_unary_func_op(result_type, id, args[0], "sin"); + break; + case GLSLstd450Cos: + emit_unary_func_op(result_type, id, args[0], "cos"); + break; + case GLSLstd450Tan: + emit_unary_func_op(result_type, id, args[0], "tan"); + break; + case GLSLstd450Asin: + emit_unary_func_op(result_type, id, args[0], "asin"); + break; + case GLSLstd450Acos: + emit_unary_func_op(result_type, id, args[0], "acos"); + break; + case GLSLstd450Atan: + emit_unary_func_op(result_type, id, args[0], "atan"); + break; + case GLSLstd450Sinh: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "sinh"); + else + { + bool forward = should_forward(args[0]); + auto expr = join("(exp(", to_expression(args[0]), ") - exp(-", to_enclosed_expression(args[0]), ")) * 0.5"); + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + } + break; + case GLSLstd450Cosh: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "cosh"); + else + { + bool forward = should_forward(args[0]); + auto expr = join("(exp(", to_expression(args[0]), ") + exp(-", to_enclosed_expression(args[0]), ")) * 0.5"); + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + } + break; + case GLSLstd450Tanh: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "tanh"); + else + { + // Create temporaries to store the result of exp(arg) and exp(-arg). + uint32_t &ids = extra_sub_expressions[id]; + if (!ids) + { + ids = ir.increase_bound_by(2); + + // Inherit precision qualifier (legacy has no NoContraction). + if (has_decoration(id, DecorationRelaxedPrecision)) + { + set_decoration(ids, DecorationRelaxedPrecision); + set_decoration(ids + 1, DecorationRelaxedPrecision); + } + } + uint32_t epos_id = ids; + uint32_t eneg_id = ids + 1; + + emit_op(result_type, epos_id, join("exp(", to_expression(args[0]), ")"), false); + emit_op(result_type, eneg_id, join("exp(-", to_enclosed_expression(args[0]), ")"), false); + inherit_expression_dependencies(epos_id, args[0]); + inherit_expression_dependencies(eneg_id, args[0]); + + auto expr = join("(", to_enclosed_expression(epos_id), " - ", to_enclosed_expression(eneg_id), ") / " + "(", to_enclosed_expression(epos_id), " + ", to_enclosed_expression(eneg_id), ")"); + emit_op(result_type, id, expr, true); + inherit_expression_dependencies(id, epos_id); + inherit_expression_dependencies(id, eneg_id); + } + break; + case GLSLstd450Asinh: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "asinh"); + else + emit_emulated_ahyper_op(result_type, id, args[0], GLSLstd450Asinh); + break; + case GLSLstd450Acosh: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "acosh"); + else + emit_emulated_ahyper_op(result_type, id, args[0], GLSLstd450Acosh); + break; + case GLSLstd450Atanh: + if (!is_legacy()) + emit_unary_func_op(result_type, id, args[0], "atanh"); + else + emit_emulated_ahyper_op(result_type, id, args[0], GLSLstd450Atanh); + break; + case GLSLstd450Atan2: + emit_binary_func_op(result_type, id, args[0], args[1], "atan"); + break; + + // Exponentials + case GLSLstd450Pow: + emit_binary_func_op(result_type, id, args[0], args[1], "pow"); + break; + case GLSLstd450Exp: + emit_unary_func_op(result_type, id, args[0], "exp"); + break; + case GLSLstd450Log: + emit_unary_func_op(result_type, id, args[0], "log"); + break; + case GLSLstd450Exp2: + emit_unary_func_op(result_type, id, args[0], "exp2"); + break; + case GLSLstd450Log2: + emit_unary_func_op(result_type, id, args[0], "log2"); + break; + case GLSLstd450Sqrt: + emit_unary_func_op(result_type, id, args[0], "sqrt"); + break; + case GLSLstd450InverseSqrt: + emit_unary_func_op(result_type, id, args[0], "inversesqrt"); + break; + + // Matrix math + case GLSLstd450Determinant: + { + // No need to transpose - it doesn't affect the determinant + auto *e = maybe_get(args[0]); + bool old_transpose = e && e->need_transpose; + if (old_transpose) + e->need_transpose = false; + + if (options.version < 150) // also matches ES 100 + { + auto &type = expression_type(args[0]); + assert(type.vecsize >= 2 && type.vecsize <= 4); + assert(type.vecsize == type.columns); + + // ARB_gpu_shader_fp64 needs GLSL 150, other types are not valid + if (type.basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Unsupported type for matrix determinant"); + + bool relaxed = has_decoration(id, DecorationRelaxedPrecision); + require_polyfill(static_cast(PolyfillDeterminant2x2 << (type.vecsize - 2)), + relaxed); + emit_unary_func_op(result_type, id, args[0], + (options.es && relaxed) ? "spvDeterminantMP" : "spvDeterminant"); + } + else + emit_unary_func_op(result_type, id, args[0], "determinant"); + + if (old_transpose) + e->need_transpose = true; + break; + } + + case GLSLstd450MatrixInverse: + { + // The inverse of the transpose is the same as the transpose of + // the inverse, so we can just flip need_transpose of the result. + auto *a = maybe_get(args[0]); + bool old_transpose = a && a->need_transpose; + if (old_transpose) + a->need_transpose = false; + + const char *func = "inverse"; + if (options.version < 140) // also matches ES 100 + { + auto &type = get(result_type); + assert(type.vecsize >= 2 && type.vecsize <= 4); + assert(type.vecsize == type.columns); + + // ARB_gpu_shader_fp64 needs GLSL 150, other types are invalid + if (type.basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Unsupported type for matrix inverse"); + + bool relaxed = has_decoration(id, DecorationRelaxedPrecision); + require_polyfill(static_cast(PolyfillMatrixInverse2x2 << (type.vecsize - 2)), + relaxed); + func = (options.es && relaxed) ? "spvInverseMP" : "spvInverse"; + } + + bool forward = should_forward(args[0]); + auto &e = emit_op(result_type, id, join(func, "(", to_unpacked_expression(args[0]), ")"), forward); + inherit_expression_dependencies(id, args[0]); + + if (old_transpose) + { + e.need_transpose = true; + a->need_transpose = true; + } + break; + } + + // Lerping + case GLSLstd450FMix: + case GLSLstd450IMix: + { + emit_mix_op(result_type, id, args[0], args[1], args[2]); + break; + } + case GLSLstd450Step: + emit_binary_func_op(result_type, id, args[0], args[1], "step"); + break; + case GLSLstd450SmoothStep: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "smoothstep"); + break; + + // Packing + case GLSLstd450Frexp: + register_call_out_argument(args[1]); + forced_temporaries.insert(id); + emit_binary_func_op(result_type, id, args[0], args[1], "frexp"); + break; + + case GLSLstd450FrexpStruct: + { + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, id); + statement(to_expression(id), ".", to_member_name(type, 0), " = ", "frexp(", to_expression(args[0]), ", ", + to_expression(id), ".", to_member_name(type, 1), ");"); + break; + } + + case GLSLstd450Ldexp: + { + bool forward = should_forward(args[0]) && should_forward(args[1]); + + auto op0 = to_unpacked_expression(args[0]); + auto op1 = to_unpacked_expression(args[1]); + auto &op1_type = expression_type(args[1]); + if (op1_type.basetype != SPIRType::Int) + { + // Need a value cast here. + auto target_type = op1_type; + target_type.basetype = SPIRType::Int; + op1 = join(type_to_glsl_constructor(target_type), "(", op1, ")"); + } + + auto expr = join("ldexp(", op0, ", ", op1, ")"); + + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, args[0]); + inherit_expression_dependencies(id, args[1]); + break; + } + + case GLSLstd450PackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "packSnorm4x8"); + break; + case GLSLstd450PackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "packUnorm4x8"); + break; + case GLSLstd450PackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "packSnorm2x16"); + break; + case GLSLstd450PackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "packUnorm2x16"); + break; + case GLSLstd450PackHalf2x16: + emit_unary_func_op(result_type, id, args[0], "packHalf2x16"); + break; + case GLSLstd450UnpackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpackSnorm4x8"); + break; + case GLSLstd450UnpackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpackUnorm4x8"); + break; + case GLSLstd450UnpackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpackSnorm2x16"); + break; + case GLSLstd450UnpackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpackUnorm2x16"); + break; + case GLSLstd450UnpackHalf2x16: + emit_unary_func_op(result_type, id, args[0], "unpackHalf2x16"); + break; + + case GLSLstd450PackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "packDouble2x32"); + break; + case GLSLstd450UnpackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "unpackDouble2x32"); + break; + + // Vector math + case GLSLstd450Length: + emit_unary_func_op(result_type, id, args[0], "length"); + break; + case GLSLstd450Distance: + emit_binary_func_op(result_type, id, args[0], args[1], "distance"); + break; + case GLSLstd450Cross: + emit_binary_func_op(result_type, id, args[0], args[1], "cross"); + break; + case GLSLstd450Normalize: + emit_unary_func_op(result_type, id, args[0], "normalize"); + break; + case GLSLstd450FaceForward: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "faceforward"); + break; + case GLSLstd450Reflect: + emit_binary_func_op(result_type, id, args[0], args[1], "reflect"); + break; + case GLSLstd450Refract: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "refract"); + break; + + // Bit-fiddling + case GLSLstd450FindILsb: + // findLSB always returns int. + emit_unary_func_op_cast(result_type, id, args[0], "findLSB", expression_type(args[0]).basetype, int_type); + break; + + case GLSLstd450FindSMsb: + emit_unary_func_op_cast(result_type, id, args[0], "findMSB", int_type, int_type); + break; + + case GLSLstd450FindUMsb: + emit_unary_func_op_cast(result_type, id, args[0], "findMSB", uint_type, + int_type); // findMSB always returns int. + break; + + // Multisampled varying + case GLSLstd450InterpolateAtCentroid: + emit_unary_func_op(result_type, id, args[0], "interpolateAtCentroid"); + break; + case GLSLstd450InterpolateAtSample: + emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtSample"); + break; + case GLSLstd450InterpolateAtOffset: + emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtOffset"); + break; + + case GLSLstd450NMin: + case GLSLstd450NMax: + { + if (options.vulkan_semantics) + { + require_extension_internal("GL_EXT_spirv_intrinsics"); + bool relaxed = has_decoration(id, DecorationRelaxedPrecision); + Polyfill poly = {}; + switch (get(result_type).width) + { + case 16: + poly = op == GLSLstd450NMin ? PolyfillNMin16 : PolyfillNMax16; + break; + + case 32: + poly = op == GLSLstd450NMin ? PolyfillNMin32 : PolyfillNMax32; + break; + + case 64: + poly = op == GLSLstd450NMin ? PolyfillNMin64 : PolyfillNMax64; + break; + + default: + SPIRV_CROSS_THROW("Invalid bit width for NMin/NMax."); + } + + require_polyfill(poly, relaxed); + + // Function return decorations are broken, so need to do double polyfill. + if (relaxed) + require_polyfill(poly, false); + + const char *op_str; + if (relaxed) + op_str = op == GLSLstd450NMin ? "spvNMinRelaxed" : "spvNMaxRelaxed"; + else + op_str = op == GLSLstd450NMin ? "spvNMin" : "spvNMax"; + + emit_binary_func_op(result_type, id, args[0], args[1], op_str); + } + else + { + emit_nminmax_op(result_type, id, args[0], args[1], op); + } + break; + } + + case GLSLstd450NClamp: + { + if (options.vulkan_semantics) + { + require_extension_internal("GL_EXT_spirv_intrinsics"); + bool relaxed = has_decoration(id, DecorationRelaxedPrecision); + Polyfill poly = {}; + switch (get(result_type).width) + { + case 16: + poly = PolyfillNClamp16; + break; + + case 32: + poly = PolyfillNClamp32; + break; + + case 64: + poly = PolyfillNClamp64; + break; + + default: + SPIRV_CROSS_THROW("Invalid bit width for NMin/NMax."); + } + + require_polyfill(poly, relaxed); + + // Function return decorations are broken, so need to do double polyfill. + if (relaxed) + require_polyfill(poly, false); + + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], relaxed ? "spvNClampRelaxed" : "spvNClamp"); + } + else + { + // Make sure we have a unique ID here to avoid aliasing the extra sub-expressions between clamp and NMin sub-op. + // IDs cannot exceed 24 bits, so we can make use of the higher bits for some unique flags. + uint32_t &max_id = extra_sub_expressions[id | EXTRA_SUB_EXPRESSION_TYPE_AUX]; + if (!max_id) + max_id = ir.increase_bound_by(1); + + // Inherit precision qualifiers. + ir.meta[max_id] = ir.meta[id]; + + emit_nminmax_op(result_type, max_id, args[0], args[1], GLSLstd450NMax); + emit_nminmax_op(result_type, id, max_id, args[2], GLSLstd450NMin); + } + break; + } + + default: + statement("// unimplemented GLSL op ", eop); + break; + } +} + +void CompilerGLSL::emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op) +{ + // Need to emulate this call. + uint32_t &ids = extra_sub_expressions[id]; + if (!ids) + { + ids = ir.increase_bound_by(5); + auto btype = get(result_type); + btype.basetype = SPIRType::Boolean; + set(ids, btype); + } + + uint32_t btype_id = ids + 0; + uint32_t left_nan_id = ids + 1; + uint32_t right_nan_id = ids + 2; + uint32_t tmp_id = ids + 3; + uint32_t mixed_first_id = ids + 4; + + // Inherit precision qualifiers. + ir.meta[tmp_id] = ir.meta[id]; + ir.meta[mixed_first_id] = ir.meta[id]; + + if (!is_legacy()) + { + emit_unary_func_op(btype_id, left_nan_id, op0, "isnan"); + emit_unary_func_op(btype_id, right_nan_id, op1, "isnan"); + } + else if (expression_type(op0).vecsize > 1) + { + // If the number doesn't equal itself, it must be NaN + emit_binary_func_op(btype_id, left_nan_id, op0, op0, "notEqual"); + emit_binary_func_op(btype_id, right_nan_id, op1, op1, "notEqual"); + } + else + { + emit_binary_op(btype_id, left_nan_id, op0, op0, "!="); + emit_binary_op(btype_id, right_nan_id, op1, op1, "!="); + } + emit_binary_func_op(result_type, tmp_id, op0, op1, op == GLSLstd450NMin ? "min" : "max"); + emit_mix_op(result_type, mixed_first_id, tmp_id, op1, left_nan_id); + emit_mix_op(result_type, id, mixed_first_id, op0, right_nan_id); +} + +void CompilerGLSL::emit_emulated_ahyper_op(uint32_t result_type, uint32_t id, uint32_t op0, GLSLstd450 op) +{ + const char *one = backend.float_literal_suffix ? "1.0f" : "1.0"; + std::string expr; + bool forward = should_forward(op0); + + switch (op) + { + case GLSLstd450Asinh: + expr = join("log(", to_enclosed_expression(op0), " + sqrt(", + to_enclosed_expression(op0), " * ", to_enclosed_expression(op0), " + ", one, "))"); + emit_op(result_type, id, expr, forward); + break; + + case GLSLstd450Acosh: + expr = join("log(", to_enclosed_expression(op0), " + sqrt(", + to_enclosed_expression(op0), " * ", to_enclosed_expression(op0), " - ", one, "))"); + break; + + case GLSLstd450Atanh: + expr = join("log((", one, " + ", to_enclosed_expression(op0), ") / " + "(", one, " - ", to_enclosed_expression(op0), ")) * 0.5", + backend.float_literal_suffix ? "f" : ""); + break; + + default: + SPIRV_CROSS_THROW("Invalid op."); + } + + emit_op(result_type, id, expr, forward); + inherit_expression_dependencies(id, op0); +} + +void CompilerGLSL::emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, + uint32_t) +{ + require_extension_internal("GL_AMD_shader_ballot"); + + enum AMDShaderBallot + { + SwizzleInvocationsAMD = 1, + SwizzleInvocationsMaskedAMD = 2, + WriteInvocationAMD = 3, + MbcntAMD = 4 + }; + + auto op = static_cast(eop); + + switch (op) + { + case SwizzleInvocationsAMD: + emit_binary_func_op(result_type, id, args[0], args[1], "swizzleInvocationsAMD"); + register_control_dependent_expression(id); + break; + + case SwizzleInvocationsMaskedAMD: + emit_binary_func_op(result_type, id, args[0], args[1], "swizzleInvocationsMaskedAMD"); + register_control_dependent_expression(id); + break; + + case WriteInvocationAMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "writeInvocationAMD"); + register_control_dependent_expression(id); + break; + + case MbcntAMD: + emit_unary_func_op(result_type, id, args[0], "mbcntAMD"); + register_control_dependent_expression(id); + break; + + default: + statement("// unimplemented SPV AMD shader ballot op ", eop); + break; + } +} + +void CompilerGLSL::emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t id, uint32_t eop, + const uint32_t *args, uint32_t) +{ + require_extension_internal("GL_AMD_shader_explicit_vertex_parameter"); + + enum AMDShaderExplicitVertexParameter + { + InterpolateAtVertexAMD = 1 + }; + + auto op = static_cast(eop); + + switch (op) + { + case InterpolateAtVertexAMD: + emit_binary_func_op(result_type, id, args[0], args[1], "interpolateAtVertexAMD"); + break; + + default: + statement("// unimplemented SPV AMD shader explicit vertex parameter op ", eop); + break; + } +} + +void CompilerGLSL::emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t id, uint32_t eop, + const uint32_t *args, uint32_t) +{ + require_extension_internal("GL_AMD_shader_trinary_minmax"); + + enum AMDShaderTrinaryMinMax + { + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9 + }; + + auto op = static_cast(eop); + + switch (op) + { + case FMin3AMD: + case UMin3AMD: + case SMin3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "min3"); + break; + + case FMax3AMD: + case UMax3AMD: + case SMax3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "max3"); + break; + + case FMid3AMD: + case UMid3AMD: + case SMid3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "mid3"); + break; + + default: + statement("// unimplemented SPV AMD shader trinary minmax op ", eop); + break; + } +} + +void CompilerGLSL::emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, + uint32_t) +{ + require_extension_internal("GL_AMD_gcn_shader"); + + enum AMDGCNShader + { + CubeFaceIndexAMD = 1, + CubeFaceCoordAMD = 2, + TimeAMD = 3 + }; + + auto op = static_cast(eop); + + switch (op) + { + case CubeFaceIndexAMD: + emit_unary_func_op(result_type, id, args[0], "cubeFaceIndexAMD"); + break; + case CubeFaceCoordAMD: + emit_unary_func_op(result_type, id, args[0], "cubeFaceCoordAMD"); + break; + case TimeAMD: + { + string expr = "timeAMD()"; + emit_op(result_type, id, expr, true); + register_control_dependent_expression(id); + break; + } + + default: + statement("// unimplemented SPV AMD gcn shader op ", eop); + break; + } +} + +void CompilerGLSL::emit_subgroup_op(const Instruction &i) +{ + const uint32_t *ops = stream(i); + auto op = static_cast(i.op); + + if (!options.vulkan_semantics && !is_supported_subgroup_op_in_opengl(op, ops)) + SPIRV_CROSS_THROW("This subgroup operation is only supported in Vulkan semantics."); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + if (options.vulkan_semantics) + { + auto &return_type = get(ops[0]); + switch (return_type.basetype) + { + case SPIRType::SByte: + case SPIRType::UByte: + require_extension_internal("GL_EXT_shader_subgroup_extended_types_int8"); + break; + + case SPIRType::Short: + case SPIRType::UShort: + require_extension_internal("GL_EXT_shader_subgroup_extended_types_int16"); + break; + + case SPIRType::Half: + require_extension_internal("GL_EXT_shader_subgroup_extended_types_float16"); + break; + + case SPIRType::Int64: + case SPIRType::UInt64: + require_extension_internal("GL_EXT_shader_subgroup_extended_types_int64"); + break; + + default: + break; + } + } + + switch (op) + { + case OpGroupNonUniformElect: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupElect); + break; + + case OpGroupNonUniformBallotBitCount: + { + const GroupOperation operation = static_cast(ops[3]); + if (operation == GroupOperationReduce) + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallotBitCount); + else if (operation == GroupOperationInclusiveScan || operation == GroupOperationExclusiveScan) + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupInverseBallot_InclBitCount_ExclBitCout); + } + break; + + case OpGroupNonUniformBallotBitExtract: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallotBitExtract); + break; + + case OpGroupNonUniformInverseBallot: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupInverseBallot_InclBitCount_ExclBitCout); + break; + + case OpGroupNonUniformBallot: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallot); + break; + + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBallotFindLSB_MSB); + break; + + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformBroadcastFirst: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBroadcast_First); + break; + + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + require_extension_internal("GL_KHR_shader_subgroup_shuffle"); + break; + + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + require_extension_internal("GL_KHR_shader_subgroup_shuffle_relative"); + break; + + case OpGroupNonUniformRotateKHR: + require_extension_internal("GL_KHR_shader_subgroup_rotate"); + break; + + case OpGroupNonUniformAll: + case OpGroupNonUniformAny: + case OpGroupNonUniformAllEqual: + { + const SPIRType &type = expression_type(ops[3]); + if (type.basetype == SPIRType::BaseType::Boolean && type.vecsize == 1u) + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupAll_Any_AllEqualBool); + else + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupAllEqualT); + } + break; + + // clang-format off +#define GLSL_GROUP_OP(OP)\ + case OpGroupNonUniform##OP:\ + {\ + auto operation = static_cast(ops[3]);\ + if (operation == GroupOperationClusteredReduce)\ + require_extension_internal("GL_KHR_shader_subgroup_clustered");\ + else if (operation == GroupOperationReduce)\ + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupArithmetic##OP##Reduce);\ + else if (operation == GroupOperationExclusiveScan)\ + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupArithmetic##OP##ExclusiveScan);\ + else if (operation == GroupOperationInclusiveScan)\ + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupArithmetic##OP##InclusiveScan);\ + else\ + SPIRV_CROSS_THROW("Invalid group operation.");\ + break;\ + } + + GLSL_GROUP_OP(IAdd) + GLSL_GROUP_OP(FAdd) + GLSL_GROUP_OP(IMul) + GLSL_GROUP_OP(FMul) + +#undef GLSL_GROUP_OP + // clang-format on + + case OpGroupNonUniformFMin: + case OpGroupNonUniformFMax: + case OpGroupNonUniformSMin: + case OpGroupNonUniformSMax: + case OpGroupNonUniformUMin: + case OpGroupNonUniformUMax: + case OpGroupNonUniformBitwiseAnd: + case OpGroupNonUniformBitwiseOr: + case OpGroupNonUniformBitwiseXor: + case OpGroupNonUniformLogicalAnd: + case OpGroupNonUniformLogicalOr: + case OpGroupNonUniformLogicalXor: + { + auto operation = static_cast(ops[3]); + if (operation == GroupOperationClusteredReduce) + { + require_extension_internal("GL_KHR_shader_subgroup_clustered"); + } + else if (operation == GroupOperationExclusiveScan || operation == GroupOperationInclusiveScan || + operation == GroupOperationReduce) + { + require_extension_internal("GL_KHR_shader_subgroup_arithmetic"); + } + else + SPIRV_CROSS_THROW("Invalid group operation."); + break; + } + + case OpGroupNonUniformQuadSwap: + case OpGroupNonUniformQuadBroadcast: + require_extension_internal("GL_KHR_shader_subgroup_quad"); + break; + + case OpGroupNonUniformQuadAllKHR: + case OpGroupNonUniformQuadAnyKHR: + // Require both extensions to be enabled. + require_extension_internal("GL_KHR_shader_subgroup_vote"); + require_extension_internal("GL_EXT_shader_quad_control"); + break; + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + // These quad ops do not have a scope parameter. + if (op != OpGroupNonUniformQuadAllKHR && op != OpGroupNonUniformQuadAnyKHR) + { + auto scope = static_cast(evaluate_constant_u32(ops[2])); + if (scope != ScopeSubgroup) + SPIRV_CROSS_THROW("Only subgroup scope is supported."); + } + + switch (op) + { + case OpGroupNonUniformElect: + emit_op(result_type, id, "subgroupElect()", true); + break; + + case OpGroupNonUniformBroadcast: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupBroadcast"); + break; + + case OpGroupNonUniformBroadcastFirst: + emit_unary_func_op(result_type, id, ops[3], "subgroupBroadcastFirst"); + break; + + case OpGroupNonUniformBallot: + emit_unary_func_op(result_type, id, ops[3], "subgroupBallot"); + break; + + case OpGroupNonUniformInverseBallot: + emit_unary_func_op(result_type, id, ops[3], "subgroupInverseBallot"); + break; + + case OpGroupNonUniformBallotBitExtract: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupBallotBitExtract"); + break; + + case OpGroupNonUniformBallotFindLSB: + emit_unary_func_op(result_type, id, ops[3], "subgroupBallotFindLSB"); + break; + + case OpGroupNonUniformBallotFindMSB: + emit_unary_func_op(result_type, id, ops[3], "subgroupBallotFindMSB"); + break; + + case OpGroupNonUniformBallotBitCount: + { + auto operation = static_cast(ops[3]); + if (operation == GroupOperationReduce) + emit_unary_func_op(result_type, id, ops[4], "subgroupBallotBitCount"); + else if (operation == GroupOperationInclusiveScan) + emit_unary_func_op(result_type, id, ops[4], "subgroupBallotInclusiveBitCount"); + else if (operation == GroupOperationExclusiveScan) + emit_unary_func_op(result_type, id, ops[4], "subgroupBallotExclusiveBitCount"); + else + SPIRV_CROSS_THROW("Invalid BitCount operation."); + break; + } + + case OpGroupNonUniformShuffle: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffle"); + break; + + case OpGroupNonUniformShuffleXor: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleXor"); + break; + + case OpGroupNonUniformShuffleUp: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleUp"); + break; + + case OpGroupNonUniformShuffleDown: + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupShuffleDown"); + break; + + case OpGroupNonUniformRotateKHR: + if (i.length > 5) + emit_trinary_func_op(result_type, id, ops[3], ops[4], ops[5], "subgroupClusteredRotate"); + else + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupRotate"); + break; + + case OpGroupNonUniformAll: + emit_unary_func_op(result_type, id, ops[3], "subgroupAll"); + break; + + case OpGroupNonUniformAny: + emit_unary_func_op(result_type, id, ops[3], "subgroupAny"); + break; + + case OpGroupNonUniformAllEqual: + emit_unary_func_op(result_type, id, ops[3], "subgroupAllEqual"); + break; + + // clang-format off +#define GLSL_GROUP_OP(op, glsl_op) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[4], "subgroup" #glsl_op); \ + else if (operation == GroupOperationInclusiveScan) \ + emit_unary_func_op(result_type, id, ops[4], "subgroupInclusive" #glsl_op); \ + else if (operation == GroupOperationExclusiveScan) \ + emit_unary_func_op(result_type, id, ops[4], "subgroupExclusive" #glsl_op); \ + else if (operation == GroupOperationClusteredReduce) \ + emit_binary_func_op(result_type, id, ops[4], ops[5], "subgroupClustered" #glsl_op); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + +#define GLSL_GROUP_OP_CAST(op, glsl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroup" #glsl_op, type, type); \ + else if (operation == GroupOperationInclusiveScan) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroupInclusive" #glsl_op, type, type); \ + else if (operation == GroupOperationExclusiveScan) \ + emit_unary_func_op_cast(result_type, id, ops[4], "subgroupExclusive" #glsl_op, type, type); \ + else if (operation == GroupOperationClusteredReduce) \ + emit_binary_func_op_cast_clustered(result_type, id, ops[4], ops[5], "subgroupClustered" #glsl_op, type); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + + 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_CAST(SMin, Min, int_type) + GLSL_GROUP_OP_CAST(SMax, Max, int_type) + GLSL_GROUP_OP_CAST(UMin, Min, uint_type) + GLSL_GROUP_OP_CAST(UMax, Max, uint_type) + GLSL_GROUP_OP(BitwiseAnd, And) + GLSL_GROUP_OP(BitwiseOr, Or) + GLSL_GROUP_OP(BitwiseXor, Xor) + GLSL_GROUP_OP(LogicalAnd, And) + GLSL_GROUP_OP(LogicalOr, Or) + GLSL_GROUP_OP(LogicalXor, Xor) +#undef GLSL_GROUP_OP +#undef GLSL_GROUP_OP_CAST + // clang-format on + + case OpGroupNonUniformQuadSwap: + { + uint32_t direction = evaluate_constant_u32(ops[4]); + if (direction == 0) + emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapHorizontal"); + else if (direction == 1) + emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapVertical"); + else if (direction == 2) + emit_unary_func_op(result_type, id, ops[3], "subgroupQuadSwapDiagonal"); + else + SPIRV_CROSS_THROW("Invalid quad swap direction."); + break; + } + + case OpGroupNonUniformQuadBroadcast: + { + emit_binary_func_op(result_type, id, ops[3], ops[4], "subgroupQuadBroadcast"); + break; + } + + case OpGroupNonUniformQuadAllKHR: + emit_unary_func_op(result_type, id, ops[2], "subgroupQuadAll"); + break; + + case OpGroupNonUniformQuadAnyKHR: + emit_unary_func_op(result_type, id, ops[2], "subgroupQuadAny"); + break; + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + register_control_dependent_expression(id); +} + +string CompilerGLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) +{ + // OpBitcast can deal with pointers. + if (out_type.pointer || in_type.pointer) + { + if (out_type.vecsize == 2 || in_type.vecsize == 2) + require_extension_internal("GL_EXT_buffer_reference_uvec2"); + return type_to_glsl(out_type); + } + + if (out_type.basetype == in_type.basetype) + return ""; + + assert(out_type.basetype != SPIRType::Boolean); + assert(in_type.basetype != SPIRType::Boolean); + + bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type); + bool same_size_cast = out_type.width == in_type.width; + + // Trivial bitcast case, casts between integers. + if (integral_cast && same_size_cast) + return type_to_glsl(out_type); + + // Catch-all 8-bit arithmetic casts (GL_EXT_shader_explicit_arithmetic_types). + if (out_type.width == 8 && in_type.width >= 16 && integral_cast && in_type.vecsize == 1) + return "unpack8"; + else if (in_type.width == 8 && out_type.width == 16 && integral_cast && out_type.vecsize == 1) + return "pack16"; + else if (in_type.width == 8 && out_type.width == 32 && integral_cast && out_type.vecsize == 1) + return "pack32"; + + // Floating <-> Integer special casts. Just have to enumerate all cases. :( + // 16-bit, 32-bit and 64-bit floats. + if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Float) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Float -> Uint bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "floatBitsToUint"; + } + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Float) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Float -> Int bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "floatBitsToInt"; + } + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::UInt) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Uint -> Float bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "uintBitsToFloat"; + } + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::Int) + { + if (is_legacy_es()) + SPIRV_CROSS_THROW("Int -> Float bitcast not supported on legacy ESSL."); + else if (!options.es && options.version < 330) + require_extension_internal("GL_ARB_shader_bit_encoding"); + return "intBitsToFloat"; + } + + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Double) + return "doubleBitsToInt64"; + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Double) + return "doubleBitsToUint64"; + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::Int64) + return "int64BitsToDouble"; + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::UInt64) + return "uint64BitsToDouble"; + else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Half) + return "float16BitsToInt16"; + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::Half) + return "float16BitsToUint16"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::Short) + return "int16BitsToFloat16"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UShort) + return "uint16BitsToFloat16"; + + // And finally, some even more special purpose casts. + if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::UInt && in_type.vecsize == 2) + return "packUint2x32"; + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::UInt64 && out_type.vecsize == 2) + return "unpackUint2x32"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1) + return "unpackFloat2x16"; + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2) + return "packFloat2x16"; + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Short && in_type.vecsize == 2) + return "packInt2x16"; + else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Int && in_type.vecsize == 1) + return "unpackInt2x16"; + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::UShort && in_type.vecsize == 2) + return "packUint2x16"; + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1) + return "unpackUint2x16"; + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Short && in_type.vecsize == 4) + return "packInt4x16"; + else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::Int64 && in_type.vecsize == 1) + return "unpackInt4x16"; + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::UShort && in_type.vecsize == 4) + return "packUint4x16"; + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::UInt64 && in_type.vecsize == 1) + return "unpackUint4x16"; + else if (out_type.basetype == SPIRType::BFloat16 && in_type.basetype == SPIRType::UShort) + return "uintBitsToBFloat16EXT"; + else if (out_type.basetype == SPIRType::BFloat16 && in_type.basetype == SPIRType::Short) + return "intBitsToBFloat16EXT"; + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::BFloat16) + return "bfloat16BitsToUintEXT"; + else if (out_type.basetype == SPIRType::Short && in_type.basetype == SPIRType::BFloat16) + return "bfloat16BitsToIntEXT"; + else if (out_type.basetype == SPIRType::FloatE4M3 && in_type.basetype == SPIRType::UByte) + return "uintBitsToFloate4m3EXT"; + else if (out_type.basetype == SPIRType::FloatE4M3 && in_type.basetype == SPIRType::SByte) + return "intBitsToFloate4m3EXT"; + else if (out_type.basetype == SPIRType::UByte && in_type.basetype == SPIRType::FloatE4M3) + return "floate4m3BitsToUintEXT"; + else if (out_type.basetype == SPIRType::SByte && in_type.basetype == SPIRType::FloatE4M3) + return "floate4m3BitsToIntEXT"; + else if (out_type.basetype == SPIRType::FloatE5M2 && in_type.basetype == SPIRType::UByte) + return "uintBitsToFloate5m2EXT"; + else if (out_type.basetype == SPIRType::FloatE5M2 && in_type.basetype == SPIRType::SByte) + return "intBitsToFloate5m2EXT"; + else if (out_type.basetype == SPIRType::UByte && in_type.basetype == SPIRType::FloatE5M2) + return "floate5m2BitsToUintEXT"; + else if (out_type.basetype == SPIRType::SByte && in_type.basetype == SPIRType::FloatE5M2) + return "floate5m2BitsToIntEXT"; + + return ""; +} + +string CompilerGLSL::bitcast_glsl(const SPIRType &result_type, uint32_t argument) +{ + auto op = bitcast_glsl_op(result_type, expression_type(argument)); + if (op.empty()) + return to_enclosed_unpacked_expression(argument); + else + return join(op, "(", to_unpacked_expression(argument), ")"); +} + +std::string CompilerGLSL::bitcast_expression(SPIRType::BaseType target_type, uint32_t arg) +{ + auto expr = to_expression(arg); + auto &src_type = expression_type(arg); + if (src_type.basetype != target_type) + { + auto target = src_type; + target.basetype = target_type; + expr = join(bitcast_glsl_op(target, src_type), "(", expr, ")"); + } + + return expr; +} + +std::string CompilerGLSL::bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type, + const std::string &expr) +{ + if (target_type.basetype == expr_type) + return expr; + + auto src_type = target_type; + src_type.basetype = expr_type; + return join(bitcast_glsl_op(target_type, src_type), "(", expr, ")"); +} + +string CompilerGLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) +{ + switch (builtin) + { + case BuiltInPosition: + return "gl_Position"; + case BuiltInPointSize: + return "gl_PointSize"; + case BuiltInClipDistance: + { + if (options.es) + require_extension_internal("GL_EXT_clip_cull_distance"); + return "gl_ClipDistance"; + } + case BuiltInCullDistance: + { + if (options.es) + require_extension_internal("GL_EXT_clip_cull_distance"); + return "gl_CullDistance"; + } + case BuiltInVertexId: + if (options.vulkan_semantics) + SPIRV_CROSS_THROW("Cannot implement gl_VertexID in Vulkan GLSL. This shader was created " + "with GL semantics."); + return "gl_VertexID"; + case BuiltInInstanceId: + if (options.vulkan_semantics) + { + auto model = get_entry_point().model; + switch (model) + { + case ExecutionModelIntersectionKHR: + case ExecutionModelAnyHitKHR: + case ExecutionModelClosestHitKHR: + // gl_InstanceID is allowed in these shaders. + break; + + default: + SPIRV_CROSS_THROW("Cannot implement gl_InstanceID in Vulkan GLSL. This shader was " + "created with GL semantics."); + } + } + if (!options.es && options.version < 140) + { + require_extension_internal("GL_ARB_draw_instanced"); + } + return "gl_InstanceID"; + case BuiltInVertexIndex: + if (options.vulkan_semantics) + return "gl_VertexIndex"; + else + return "gl_VertexID"; // gl_VertexID already has the base offset applied. + case BuiltInInstanceIndex: + if (options.vulkan_semantics) + return "gl_InstanceIndex"; + + if (!options.es && options.version < 140) + { + require_extension_internal("GL_ARB_draw_instanced"); + } + + if (options.vertex.support_nonzero_base_instance) + { + if (!options.vulkan_semantics) + { + // This is a soft-enable. We will opt-in to using gl_BaseInstanceARB if supported. + require_extension_internal("GL_ARB_shader_draw_parameters"); + } + return "(gl_InstanceID + SPIRV_Cross_BaseInstance)"; // ... but not gl_InstanceID. + } + else + return "gl_InstanceID"; + case BuiltInPrimitiveId: + if (storage == StorageClassInput && get_entry_point().model == ExecutionModelGeometry) + return "gl_PrimitiveIDIn"; + else + return "gl_PrimitiveID"; + case BuiltInInvocationId: + return "gl_InvocationID"; + case BuiltInLayer: + { + auto model = get_execution_model(); + if (model == ExecutionModelVertex || model == ExecutionModelTessellationEvaluation) + { + if (options.es) + require_extension_internal("GL_NV_viewport_array2"); + else + require_extension_internal("GL_ARB_shader_viewport_layer_array"); + } + return "gl_Layer"; + } + case BuiltInViewportIndex: + return "gl_ViewportIndex"; + case BuiltInTessLevelOuter: + return "gl_TessLevelOuter"; + case BuiltInTessLevelInner: + return "gl_TessLevelInner"; + case BuiltInTessCoord: + return "gl_TessCoord"; + case BuiltInPatchVertices: + return "gl_PatchVerticesIn"; + case BuiltInFragCoord: + return "gl_FragCoord"; + case BuiltInPointCoord: + return "gl_PointCoord"; + case BuiltInFrontFacing: + return "gl_FrontFacing"; + case BuiltInFragDepth: + return "gl_FragDepth"; + case BuiltInNumWorkgroups: + return "gl_NumWorkGroups"; + case BuiltInWorkgroupSize: + return "gl_WorkGroupSize"; + case BuiltInWorkgroupId: + return "gl_WorkGroupID"; + case BuiltInLocalInvocationId: + return "gl_LocalInvocationID"; + case BuiltInGlobalInvocationId: + return "gl_GlobalInvocationID"; + case BuiltInLocalInvocationIndex: + return "gl_LocalInvocationIndex"; + case BuiltInHelperInvocation: + return "gl_HelperInvocation"; + + case BuiltInBaseVertex: + if (options.es) + SPIRV_CROSS_THROW("BaseVertex not supported in ES profile."); + + if (options.vulkan_semantics) + { + if (options.version < 460) + { + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_BaseVertexARB"; + } + return "gl_BaseVertex"; + } + // On regular GL, this is soft-enabled and we emit ifdefs in code. + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "SPIRV_Cross_BaseVertex"; + + case BuiltInBaseInstance: + if (options.es) + SPIRV_CROSS_THROW("BaseInstance not supported in ES profile."); + + if (options.vulkan_semantics) + { + if (options.version < 460) + { + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_BaseInstanceARB"; + } + return "gl_BaseInstance"; + } + // On regular GL, this is soft-enabled and we emit ifdefs in code. + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "SPIRV_Cross_BaseInstance"; + + case BuiltInDrawIndex: + if (options.es) + SPIRV_CROSS_THROW("DrawIndex not supported in ES profile."); + + if (options.vulkan_semantics) + { + if (options.version < 460) + { + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_DrawIDARB"; + } + return "gl_DrawID"; + } + // On regular GL, this is soft-enabled and we emit ifdefs in code. + require_extension_internal("GL_ARB_shader_draw_parameters"); + return "gl_DrawIDARB"; + + case BuiltInSampleId: + if (is_legacy()) + SPIRV_CROSS_THROW("Sample variables not supported in legacy GLSL."); + else if (options.es && options.version < 320) + require_extension_internal("GL_OES_sample_variables"); + else if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_sample_shading"); + return "gl_SampleID"; + + case BuiltInSampleMask: + if (is_legacy()) + SPIRV_CROSS_THROW("Sample variables not supported in legacy GLSL."); + else if (options.es && options.version < 320) + require_extension_internal("GL_OES_sample_variables"); + else if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_sample_shading"); + + if (storage == StorageClassInput) + return "gl_SampleMaskIn"; + else + return "gl_SampleMask"; + + case BuiltInSamplePosition: + if (is_legacy()) + SPIRV_CROSS_THROW("Sample variables not supported in legacy GLSL."); + else if (options.es && options.version < 320) + require_extension_internal("GL_OES_sample_variables"); + else if (!options.es && options.version < 400) + require_extension_internal("GL_ARB_sample_shading"); + return "gl_SamplePosition"; + + case BuiltInViewIndex: + if (options.vulkan_semantics) + return "gl_ViewIndex"; + else + return "gl_ViewID_OVR"; + + case BuiltInNumSubgroups: + request_subgroup_feature(ShaderSubgroupSupportHelper::NumSubgroups); + return "gl_NumSubgroups"; + + case BuiltInSubgroupId: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupID); + return "gl_SubgroupID"; + + case BuiltInSubgroupSize: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupSize); + return "gl_SubgroupSize"; + + case BuiltInSubgroupLocalInvocationId: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupInvocationID); + return "gl_SubgroupInvocationID"; + + case BuiltInSubgroupEqMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupEqMask"; + + case BuiltInSubgroupGeMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupGeMask"; + + case BuiltInSubgroupGtMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupGtMask"; + + case BuiltInSubgroupLeMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupLeMask"; + + case BuiltInSubgroupLtMask: + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMask); + return "gl_SubgroupLtMask"; + + case BuiltInLaunchIdKHR: + return ray_tracing_is_khr ? "gl_LaunchIDEXT" : "gl_LaunchIDNV"; + case BuiltInLaunchSizeKHR: + return ray_tracing_is_khr ? "gl_LaunchSizeEXT" : "gl_LaunchSizeNV"; + case BuiltInWorldRayOriginKHR: + return ray_tracing_is_khr ? "gl_WorldRayOriginEXT" : "gl_WorldRayOriginNV"; + case BuiltInWorldRayDirectionKHR: + return ray_tracing_is_khr ? "gl_WorldRayDirectionEXT" : "gl_WorldRayDirectionNV"; + case BuiltInObjectRayOriginKHR: + return ray_tracing_is_khr ? "gl_ObjectRayOriginEXT" : "gl_ObjectRayOriginNV"; + case BuiltInObjectRayDirectionKHR: + return ray_tracing_is_khr ? "gl_ObjectRayDirectionEXT" : "gl_ObjectRayDirectionNV"; + case BuiltInRayTminKHR: + return ray_tracing_is_khr ? "gl_RayTminEXT" : "gl_RayTminNV"; + case BuiltInRayTmaxKHR: + return ray_tracing_is_khr ? "gl_RayTmaxEXT" : "gl_RayTmaxNV"; + case BuiltInInstanceCustomIndexKHR: + return ray_tracing_is_khr ? "gl_InstanceCustomIndexEXT" : "gl_InstanceCustomIndexNV"; + case BuiltInObjectToWorldKHR: + return ray_tracing_is_khr ? "gl_ObjectToWorldEXT" : "gl_ObjectToWorldNV"; + case BuiltInWorldToObjectKHR: + return ray_tracing_is_khr ? "gl_WorldToObjectEXT" : "gl_WorldToObjectNV"; + case BuiltInHitTNV: + // gl_HitTEXT is an alias of RayTMax in KHR. + return "gl_HitTNV"; + case BuiltInHitKindKHR: + return ray_tracing_is_khr ? "gl_HitKindEXT" : "gl_HitKindNV"; + case BuiltInIncomingRayFlagsKHR: + return ray_tracing_is_khr ? "gl_IncomingRayFlagsEXT" : "gl_IncomingRayFlagsNV"; + + case BuiltInBaryCoordKHR: + { + if (options.es && options.version < 320) + SPIRV_CROSS_THROW("gl_BaryCoordEXT requires ESSL 320."); + else if (!options.es && options.version < 450) + SPIRV_CROSS_THROW("gl_BaryCoordEXT requires GLSL 450."); + + if (barycentric_is_nv) + { + require_extension_internal("GL_NV_fragment_shader_barycentric"); + return "gl_BaryCoordNV"; + } + else + { + require_extension_internal("GL_EXT_fragment_shader_barycentric"); + return "gl_BaryCoordEXT"; + } + } + + case BuiltInBaryCoordNoPerspNV: + { + if (options.es && options.version < 320) + SPIRV_CROSS_THROW("gl_BaryCoordNoPerspEXT requires ESSL 320."); + else if (!options.es && options.version < 450) + SPIRV_CROSS_THROW("gl_BaryCoordNoPerspEXT requires GLSL 450."); + + if (barycentric_is_nv) + { + require_extension_internal("GL_NV_fragment_shader_barycentric"); + return "gl_BaryCoordNoPerspNV"; + } + else + { + require_extension_internal("GL_EXT_fragment_shader_barycentric"); + return "gl_BaryCoordNoPerspEXT"; + } + } + + case BuiltInFragStencilRefEXT: + { + if (!options.es) + { + require_extension_internal("GL_ARB_shader_stencil_export"); + return "gl_FragStencilRefARB"; + } + else + SPIRV_CROSS_THROW("Stencil export not supported in GLES."); + } + + case BuiltInPrimitiveShadingRateKHR: + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Can only use PrimitiveShadingRateKHR in Vulkan GLSL."); + require_extension_internal("GL_EXT_fragment_shading_rate"); + return "gl_PrimitiveShadingRateEXT"; + } + + case BuiltInShadingRateKHR: + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Can only use ShadingRateKHR in Vulkan GLSL."); + require_extension_internal("GL_EXT_fragment_shading_rate"); + return "gl_ShadingRateEXT"; + } + + case BuiltInDeviceIndex: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Need Vulkan semantics for device group support."); + require_extension_internal("GL_EXT_device_group"); + return "gl_DeviceIndex"; + + case BuiltInFullyCoveredEXT: + if (!options.es) + require_extension_internal("GL_NV_conservative_raster_underestimation"); + else + SPIRV_CROSS_THROW("Need desktop GL to use GL_NV_conservative_raster_underestimation."); + return "gl_FragFullyCoveredNV"; + + case BuiltInPrimitiveTriangleIndicesEXT: + return "gl_PrimitiveTriangleIndicesEXT"; + case BuiltInPrimitiveLineIndicesEXT: + return "gl_PrimitiveLineIndicesEXT"; + case BuiltInPrimitivePointIndicesEXT: + return "gl_PrimitivePointIndicesEXT"; + case BuiltInCullPrimitiveEXT: + return "gl_CullPrimitiveEXT"; + + case BuiltInHitTriangleVertexPositionsKHR: + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Need Vulkan semantics for EXT_ray_tracing_position_fetch."); + require_extension_internal("GL_EXT_ray_tracing_position_fetch"); + return "gl_HitTriangleVertexPositionsEXT"; + } + + case BuiltInClusterIDNV: + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Can only use ClusterIDNV in Vulkan GLSL."); + require_extension_internal("GL_NV_cluster_acceleration_structure"); + return "gl_ClusterIDNV"; + } + + case BuiltInResourceHeapEXT: + // This builtin name is a placeholder. + // We will override this name later with prefix per actual type. + // However, this allows untyped access chain to index into the heap directly. + return "ResourceHeap"; + + case BuiltInSamplerHeapEXT: + return "SamplerHeap"; + + default: + return join("gl_BuiltIn_", convert_to_string(builtin)); + } +} + +const char *CompilerGLSL::index_to_swizzle(uint32_t index) +{ + switch (index) + { + case 0: + return "x"; + case 1: + return "y"; + case 2: + return "z"; + case 3: + return "w"; + default: + return "x"; // Don't crash, but engage the "undefined behavior" described for out-of-bounds logical addressing in spec. + } +} + +void CompilerGLSL::access_chain_internal_append_index(std::string &expr, uint32_t /*base*/, const SPIRType * /*type*/, + AccessChainFlags flags, bool &access_chain_is_arrayed, + uint32_t index) +{ + bool index_is_literal = (flags & ACCESS_CHAIN_INDEX_IS_LITERAL_BIT) != 0; + bool ptr_chain = (flags & ACCESS_CHAIN_PTR_CHAIN_BIT) != 0; + bool register_expression_read = (flags & ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT) == 0; + + string idx_expr = index_is_literal ? convert_to_string(index) : to_unpacked_expression(index, register_expression_read); + + // For the case where the base of an OpPtrAccessChain already ends in [n], + // we need to use the index as an offset to the existing index, otherwise, + // we can just use the index directly. + if (ptr_chain && access_chain_is_arrayed) + { + size_t split_pos = expr.find_last_of(']'); + size_t enclose_split = expr.find_last_of(')'); + + // If we have already enclosed the expression, don't try to be clever, it will break. + if (split_pos > enclose_split || enclose_split == string::npos) + { + string expr_front = expr.substr(0, split_pos); + string expr_back = expr.substr(split_pos); + expr = expr_front + " + " + enclose_expression(idx_expr) + expr_back; + return; + } + } + + expr += "["; + expr += idx_expr; + expr += "]"; +} + +bool CompilerGLSL::access_chain_needs_stage_io_builtin_translation(uint32_t) +{ + return true; +} + +string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, + AccessChainFlags flags, AccessChainMeta *meta, + const SPIRType *untyped_data_type) +{ + string expr; + + bool index_is_literal = (flags & ACCESS_CHAIN_INDEX_IS_LITERAL_BIT) != 0; + bool msb_is_id = (flags & ACCESS_CHAIN_LITERAL_MSB_FORCE_ID) != 0; + bool chain_only = (flags & ACCESS_CHAIN_CHAIN_ONLY_BIT) != 0; + bool ptr_chain = (flags & ACCESS_CHAIN_PTR_CHAIN_BIT) != 0; + bool register_expression_read = (flags & ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT) == 0; + bool flatten_member_reference = (flags & ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT) != 0; + + if (!chain_only) + { + // We handle transpose explicitly, so don't resolve that here. + auto *e = maybe_get(base); + bool old_transpose = e && e->need_transpose; + if (e) + e->need_transpose = false; + expr = to_enclosed_expression(base, register_expression_read); + if (e) + e->need_transpose = old_transpose; + } + + // Start traversing type hierarchy at the proper non-pointer types, + // but keep type_id referencing the original pointer for use below. + uint32_t type_id = expression_type_id(base); + + // If nullptr we're doing untyped pointers. + // For now we don't really care about types since we're just doing a single index into the heap. + // If we intend to support complete untyped pointers usage later, we need to pass down the base type + // and override chain type based on that. + const auto *type = untyped_data_type ? untyped_data_type : &get_pointee_type(type_id); + + if (!backend.native_pointers) + { + if (ptr_chain) + SPIRV_CROSS_THROW("Backend does not support native pointers and does not support OpPtrAccessChain."); + + // Wrapped buffer reference pointer types will need to poke into the internal "value" member before + // continuing the access chain. + if (should_dereference(base)) + expr = dereference_expression(get(type_id), expr); + } + else if (should_dereference(base) && type->basetype != SPIRType::Struct && !ptr_chain) + expr = join("(", dereference_expression(*type, expr), ")"); + + bool access_chain_is_arrayed = expr.find_first_of('[') != string::npos; + bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base); + bool is_packed = has_extended_decoration(base, SPIRVCrossDecorationPhysicalTypePacked); + uint32_t physical_type = get_extended_decoration(base, SPIRVCrossDecorationPhysicalTypeID); + bool is_invariant = has_decoration(base, DecorationInvariant); + bool relaxed_precision = has_decoration(base, DecorationRelaxedPrecision); + bool pending_array_enclose = false; + bool dimension_flatten = false; + bool access_meshlet_position_y = false; + bool chain_is_builtin = false; + BuiltIn chained_builtin = {}; + + if (auto *base_expr = maybe_get(base)) + { + access_meshlet_position_y = base_expr->access_meshlet_position_y; + } + + // If we are translating access to a structured buffer, the first subscript '._m0' must be hidden + bool hide_first_subscript = count > 1 && is_user_type_structured(base); + + const auto append_index = [&](uint32_t index, bool is_literal, bool is_ptr_chain = false) { + AccessChainFlags mod_flags = flags; + if (!is_literal) + mod_flags &= ~ACCESS_CHAIN_INDEX_IS_LITERAL_BIT; + if (!is_ptr_chain) + mod_flags &= ~ACCESS_CHAIN_PTR_CHAIN_BIT; + access_chain_internal_append_index(expr, base, type, mod_flags, access_chain_is_arrayed, index); + if (check_physical_type_cast(expr, type, physical_type)) + physical_type = 0; + }; + + for (uint32_t i = 0; i < count; i++) + { + uint32_t index = indices[i]; + + bool is_literal = index_is_literal; + if (is_literal && msb_is_id && (index >> 31u) != 0u) + { + is_literal = false; + index &= 0x7fffffffu; + } + + bool ptr_chain_array_entry = ptr_chain && i == 0 && is_array(*type); + + if (ptr_chain_array_entry) + { + // This is highly unusual code, since normally we'd use plain AccessChain, but it's still allowed. + // We are considered to have a pointer to array and one element shifts by one array at a time. + // If we use normal array indexing, we'll first decay to pointer, and lose the array-ness, + // so we have to take pointer to array explicitly. + if (!should_dereference(base)) + expr = enclose_expression(address_of_expression(expr)); + } + + if (ptr_chain && i == 0) + { + // Pointer chains + // If we are flattening multidimensional arrays, only create opening bracket on first + // array index. + if (options.flatten_multidimensional_arrays) + { + dimension_flatten = type->array.size() >= 1; + pending_array_enclose = dimension_flatten; + if (pending_array_enclose) + expr += "["; + } + + if (options.flatten_multidimensional_arrays && dimension_flatten) + { + // If we are flattening multidimensional arrays, do manual stride computation. + if (is_literal) + expr += convert_to_string(index); + else + expr += to_enclosed_expression(index, register_expression_read); + + for (auto j = uint32_t(type->array.size()); j; j--) + { + expr += " * "; + expr += enclose_expression(to_array_size(*type, j - 1)); + } + + if (type->array.empty()) + pending_array_enclose = false; + else + expr += " + "; + + if (!pending_array_enclose) + expr += "]"; + } + else + { + if (flags & ACCESS_CHAIN_PTR_CHAIN_POINTER_ARITH_BIT) + { + SPIRType tmp_type(OpTypeInt); + tmp_type.basetype = SPIRType::UInt64; + tmp_type.width = 64; + tmp_type.vecsize = 1; + tmp_type.columns = 1; + + TypeID ptr_type_id = expression_type_id(base); + const SPIRType &ptr_type = get(ptr_type_id); + const SPIRType &pointee_type = get_pointee_type(ptr_type); + + // This only runs in native pointer backends. + // Can replace reinterpret_cast with a backend string if ever needed. + // We expect this to count as a de-reference. + // This leaks some MSL details, but feels slightly overkill to + // add yet another virtual interface just for this. + auto intptr_expr = join("reinterpret_cast<", type_to_glsl(tmp_type), ">(", expr, ")"); + intptr_expr += join(" + ", to_enclosed_unpacked_expression(index), " * ", + get_decoration(ptr_type_id, DecorationArrayStride)); + + if (flags & ACCESS_CHAIN_PTR_CHAIN_CAST_TO_SCALAR_BIT) + { + is_packed = true; + expr = join("*reinterpret_cast(", intptr_expr, ")"); + } + else + { + expr = join("*reinterpret_cast<", type_to_glsl(ptr_type), ">(", intptr_expr, ")"); + } + } + else + append_index(index, is_literal, true); + } + + if (type->basetype == SPIRType::ControlPointArray) + { + type_id = type->parent_type; + type = &get(type_id); + } + + access_chain_is_arrayed = true; + + // Explicitly enclose the expression if this is one of the weird pointer-to-array cases. + // We don't want any future indexing to add to this array dereference. + // Enclosing the expression blocks that and avoids any shenanigans with operand priority. + if (ptr_chain_array_entry) + expr = join("(", expr, ")"); + } + // Arrays and OpTypeCooperativeVectorNV (aka fancy arrays) + else if (!type->array.empty() || type->op == OpTypeCooperativeVectorNV) + { + // If we are flattening multidimensional arrays, only create opening bracket on first + // array index. + if (options.flatten_multidimensional_arrays && !pending_array_enclose) + { + dimension_flatten = type->array.size() > 1; + pending_array_enclose = dimension_flatten; + if (pending_array_enclose) + expr += "["; + } + + assert(type->parent_type); + + auto *var = maybe_get(base); + if (backend.force_gl_in_out_block && i == 0 && var && is_builtin_variable(*var) && + !has_decoration(type->self, DecorationBlock)) + { + // This deals with scenarios for tesc/geom where arrays of gl_Position[] are declared. + // Normally, these variables live in blocks when compiled from GLSL, + // but HLSL seems to just emit straight arrays here. + // We must pretend this access goes through gl_in/gl_out arrays + // to be able to access certain builtins as arrays. + // Similar concerns apply for mesh shaders where we have to redirect to gl_MeshVerticesEXT or MeshPrimitivesEXT. + auto builtin = ir.meta[base].decoration.builtin_type; + bool mesh_shader = get_execution_model() == ExecutionModelMeshEXT; + + chain_is_builtin = true; + chained_builtin = builtin; + + switch (builtin) + { + case BuiltInCullDistance: + case BuiltInClipDistance: + if (type->array.size() == 1) // Red herring. Only consider block IO for two-dimensional arrays here. + { + append_index(index, is_literal); + break; + } + // fallthrough + case BuiltInPosition: + case BuiltInPointSize: + if (mesh_shader) + expr = join("gl_MeshVerticesEXT[", to_expression(index, register_expression_read), "].", expr); + else if (var->storage == StorageClassInput) + expr = join("gl_in[", to_expression(index, register_expression_read), "].", expr); + else if (var->storage == StorageClassOutput) + expr = join("gl_out[", to_expression(index, register_expression_read), "].", expr); + else + append_index(index, is_literal); + break; + + case BuiltInPrimitiveId: + case BuiltInLayer: + case BuiltInViewportIndex: + case BuiltInCullPrimitiveEXT: + case BuiltInPrimitiveShadingRateKHR: + if (mesh_shader) + expr = join("gl_MeshPrimitivesEXT[", to_expression(index, register_expression_read), "].", expr); + else + append_index(index, is_literal); + break; + + default: + append_index(index, is_literal); + break; + } + } + else if (backend.force_merged_mesh_block && i == 0 && var && + !is_builtin_variable(*var) && var->storage == StorageClassOutput) + { + if (is_per_primitive_variable(*var)) + expr = join("gl_MeshPrimitivesEXT[", to_expression(index, register_expression_read), "].", expr); + else + expr = join("gl_MeshVerticesEXT[", to_expression(index, register_expression_read), "].", expr); + } + else if (options.flatten_multidimensional_arrays && dimension_flatten) + { + // If we are flattening multidimensional arrays, do manual stride computation. + auto &parent_type = get(type->parent_type); + + if (is_literal) + expr += convert_to_string(index); + else + expr += to_enclosed_expression(index, register_expression_read); + + for (auto j = uint32_t(parent_type.array.size()); j; j--) + { + expr += " * "; + expr += enclose_expression(to_array_size(parent_type, j - 1)); + } + + if (parent_type.array.empty()) + pending_array_enclose = false; + else + expr += " + "; + + if (!pending_array_enclose) + expr += "]"; + } + else if (index_is_literal || !builtin_translates_to_nonarray(BuiltIn(get_decoration(base, DecorationBuiltIn)))) + { + // Some builtins are arrays in SPIR-V but not in other languages, e.g. gl_SampleMask[] is an array in SPIR-V but not in Metal. + // By throwing away the index, we imply the index was 0, which it must be for gl_SampleMask. + // For literal indices we are working on composites, so we ignore this since we have already converted to proper array. + append_index(index, is_literal); + } + + if (var && has_decoration(var->self, DecorationBuiltIn) && + get_decoration(var->self, DecorationBuiltIn) == BuiltInPosition && + get_execution_model() == ExecutionModelMeshEXT) + { + access_meshlet_position_y = true; + } + + if (get(type->parent_type).op == OpTypeStruct && + has_decoration(type->parent_type, DecorationArrayStride)) + { + uint32_t native_stride = get_decoration(type->parent_type, DecorationArrayStride); + uint32_t array_stride = get_decoration(type_id, DecorationArrayStride); + if (native_stride != array_stride) + expr += ".data"; + } + + type_id = type->parent_type; + type = &get(type_id); + + // If the physical type has an unnatural vecsize, + // we must assume it's a faked struct where the .data member + // is used for the real payload. + if (physical_type && (is_vector(*type) || is_scalar(*type))) + { + auto &phys = get(physical_type); + if (phys.vecsize > 4) + expr += ".data"; + } + + access_chain_is_arrayed = true; + } + // For structs, the index refers to a constant, which indexes into the members, possibly through a redirection mapping. + // We also check if this member is a builtin, since we then replace the entire expression with the builtin one. + else if (type->basetype == SPIRType::Struct) + { + if (!is_literal) + index = evaluate_constant_u32(index); + + if (index < uint32_t(type->member_type_index_redirection.size())) + index = type->member_type_index_redirection[index]; + + if (index >= type->member_types.size()) + SPIRV_CROSS_THROW("Member index is out of bounds!"); + + if (hide_first_subscript) + { + // First "._m0" subscript has been hidden, subsequent fields must be emitted even for structured buffers + hide_first_subscript = false; + } + else + { + BuiltIn builtin = BuiltInMax; + if (is_member_builtin(*type, index, &builtin) && access_chain_needs_stage_io_builtin_translation(base)) + { + if (access_chain_is_arrayed) + { + expr += "."; + expr += builtin_to_glsl(builtin, type->storage); + } + else + expr = builtin_to_glsl(builtin, type->storage); + + if (builtin == BuiltInPosition && get_execution_model() == ExecutionModelMeshEXT) + { + access_meshlet_position_y = true; + } + + chain_is_builtin = true; + chained_builtin = builtin; + } + else + { + // If the member has a qualified name, use it as the entire chain + string qual_mbr_name = get_member_qualified_name(type_id, index); + if (!qual_mbr_name.empty()) + expr = qual_mbr_name; + else if (flatten_member_reference) + expr += join("_", to_member_name(*type, index)); + else + { + // Any pointer de-refences for values are handled in the first access chain. + // For pointer chains, the pointer-ness is resolved through an array access. + // The only time this is not true is when accessing array of SSBO/UBO. + // This case is explicitly handled. + expr += to_member_reference(base, *type, index, ptr_chain || i != 0); + } + } + } + + if (has_member_decoration(type->self, index, DecorationInvariant)) + is_invariant = true; + if (has_member_decoration(type->self, index, DecorationRelaxedPrecision)) + relaxed_precision = true; + + is_packed = member_is_packed_physical_type(*type, index); + if (member_is_remapped_physical_type(*type, index)) + physical_type = get_extended_member_decoration(type->self, index, SPIRVCrossDecorationPhysicalTypeID); + else + physical_type = 0; + + row_major_matrix_needs_conversion = member_is_non_native_row_major_matrix(*type, index); + type_id = type->member_types[index]; + type = &get(type->member_types[index]); + } + // Matrix -> Vector + else if (type->columns > 1) + { + // If we have a row-major matrix here, we need to defer any transpose in case this access chain + // is used to store a column. We can resolve it right here and now if we access a scalar directly, + // by flipping indexing order of the matrix. + + expr += "["; + if (is_literal) + expr += convert_to_string(index); + else + expr += to_unpacked_expression(index, register_expression_read); + expr += "]"; + + // If the physical type has an unnatural vecsize, + // we must assume it's a faked struct where the .data member + // is used for the real payload. + if (physical_type) + { + auto &phys = get(physical_type); + if (phys.vecsize > 4 || phys.columns > 4) + expr += ".data"; + } + + type_id = type->parent_type; + type = &get(type_id); + } + // Vector -> Scalar + else if (type->op == OpTypeCooperativeMatrixKHR || type->vecsize > 1) + { + string deferred_index; + if (row_major_matrix_needs_conversion) + { + // Flip indexing order. + auto column_index = expr.find_last_of('['); + if (column_index != string::npos) + { + deferred_index = expr.substr(column_index); + + auto end_deferred_index = deferred_index.find_last_of(']'); + if (end_deferred_index != string::npos && end_deferred_index + 1 != deferred_index.size()) + { + // If we have any data member fixups, it must be transposed so that it refers to this index. + // E.g. [0].data followed by [1] would be shuffled to [1][0].data which is wrong, + // and needs to be [1].data[0] instead. + end_deferred_index++; + deferred_index = deferred_index.substr(end_deferred_index) + + deferred_index.substr(0, end_deferred_index); + } + + expr.resize(column_index); + } + } + + // Internally, access chain implementation can also be used on composites, + // ignore scalar access workarounds in this case. + StorageClass effective_storage = StorageClassGeneric; + bool ignore_potential_sliced_writes = false; + if ((flags & ACCESS_CHAIN_FORCE_COMPOSITE_BIT) == 0) + { + if (expression_type(base).pointer) + effective_storage = get_expression_effective_storage_class(base); + + // Special consideration for control points. + // Control points can only be written by InvocationID, so there is no need + // to consider scalar access chains here. + // Cleans up some cases where it's very painful to determine the accurate storage class + // since blocks can be partially masked ... + auto *var = maybe_get_backing_variable(base); + if (var && var->storage == StorageClassOutput && + get_execution_model() == ExecutionModelTessellationControl && + !has_decoration(var->self, DecorationPatch)) + { + ignore_potential_sliced_writes = true; + } + } + else + ignore_potential_sliced_writes = true; + + if (!row_major_matrix_needs_conversion && !ignore_potential_sliced_writes) + { + // On some backends, we might not be able to safely access individual scalars in a vector. + // To work around this, we might have to cast the access chain reference to something which can, + // like a pointer to scalar, which we can then index into. + prepare_access_chain_for_scalar_access(expr, get(type->parent_type), effective_storage, + is_packed); + } + + if (is_literal) + { + bool out_of_bounds = index >= type->vecsize && type->op != OpTypeCooperativeMatrixKHR; + + if (!is_packed && !row_major_matrix_needs_conversion && type->op != OpTypeCooperativeMatrixKHR) + { + expr += "."; + expr += index_to_swizzle(out_of_bounds ? 0 : index); + } + else + { + // For packed vectors, we can only access them as an array, not by swizzle. + expr += join("[", out_of_bounds ? 0 : index, "]"); + } + } + else if (ir.ids[index].get_type() == TypeConstant && !is_packed && !row_major_matrix_needs_conversion) + { + auto &c = get(index); + bool out_of_bounds = (c.scalar() >= type->vecsize); + + if (c.specialization) + { + // If the index is a spec constant, we cannot turn extract into a swizzle. + expr += join("[", out_of_bounds ? "0" : to_expression(index), "]"); + } + else + { + expr += "."; + expr += index_to_swizzle(out_of_bounds ? 0 : c.scalar()); + } + } + else + { + expr += "["; + expr += to_unpacked_expression(index, register_expression_read); + expr += "]"; + } + + if (row_major_matrix_needs_conversion && !ignore_potential_sliced_writes) + { + if (prepare_access_chain_for_scalar_access(expr, get(type->parent_type), effective_storage, + is_packed)) + { + // We're in a pointer context now, so just remove any member dereference. + auto first_index = deferred_index.find_first_of('['); + if (first_index != string::npos && first_index != 0) + deferred_index = deferred_index.substr(first_index); + } + } + + if (access_meshlet_position_y) + { + if (is_literal) + { + access_meshlet_position_y = index == 1; + } + else + { + const auto *c = maybe_get(index); + if (c) + access_meshlet_position_y = c->scalar() == 1; + else + { + // We don't know, but we have to assume no. + // Flip Y in mesh shaders is an opt-in horrible hack, so we'll have to assume shaders try to behave. + access_meshlet_position_y = false; + } + } + } + + expr += deferred_index; + row_major_matrix_needs_conversion = false; + + is_packed = false; + physical_type = 0; + type_id = type->parent_type; + type = &get(type_id); + } + else if (!backend.allow_truncated_access_chain) + SPIRV_CROSS_THROW("Cannot subdivide a scalar value!"); + } + + if (pending_array_enclose) + { + SPIRV_CROSS_THROW("Flattening of multidimensional arrays were enabled, " + "but the access chain was terminated in the middle of a multidimensional array. " + "This is not supported."); + } + + if (meta) + { + meta->need_transpose = row_major_matrix_needs_conversion; + meta->storage_is_packed = is_packed; + meta->storage_is_invariant = is_invariant; + meta->storage_physical_type = physical_type; + meta->relaxed_precision = relaxed_precision; + meta->access_meshlet_position_y = access_meshlet_position_y; + meta->chain_is_builtin = chain_is_builtin; + meta->builtin = chained_builtin; + } + + return expr; +} + +bool CompilerGLSL::check_physical_type_cast(std::string &, const SPIRType *, uint32_t) +{ + return false; +} + +bool CompilerGLSL::prepare_access_chain_for_scalar_access(std::string &, const SPIRType &, StorageClass, bool &) +{ + return false; +} + +string CompilerGLSL::to_flattened_struct_member(const string &basename, const SPIRType &type, uint32_t index) +{ + auto ret = join(basename, "_", to_member_name(type, index)); + ParsedIR::sanitize_underscores(ret); + return ret; +} + +uint32_t CompilerGLSL::get_physical_type_id_stride(TypeID) const +{ + SPIRV_CROSS_THROW("Invalid to call get_physical_type_id_stride on a backend without native pointer support."); +} + +string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type, + AccessChainMeta *meta, bool ptr_chain, const SPIRType *untyped_data_type) +{ + if (flattened_buffer_blocks.count(base)) + { + if (untyped_data_type) + SPIRV_CROSS_THROW("Flattening not compatible with untyped pointers."); + + uint32_t matrix_stride = 0; + uint32_t array_stride = 0; + bool need_transpose = false; + flattened_access_chain_offset(expression_type(base), indices, count, 0, 16, &need_transpose, &matrix_stride, + &array_stride, ptr_chain); + + if (meta) + { + meta->need_transpose = target_type.columns > 1 && need_transpose; + meta->storage_is_packed = false; + } + + return flattened_access_chain(base, indices, count, target_type, 0, matrix_stride, array_stride, + need_transpose); + } + else if (flattened_structs.count(base) && count > 0) + { + if (untyped_data_type) + SPIRV_CROSS_THROW("Flattening not compatible with untyped pointers."); + + AccessChainFlags flags = ACCESS_CHAIN_CHAIN_ONLY_BIT | ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT; + if (ptr_chain) + flags |= ACCESS_CHAIN_PTR_CHAIN_BIT; + + if (flattened_structs[base]) + { + flags |= ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT; + if (meta) + meta->flattened_struct = target_type.basetype == SPIRType::Struct; + } + + auto chain = access_chain_internal(base, indices, count, flags, nullptr, nullptr).substr(1); + if (meta) + { + meta->need_transpose = false; + meta->storage_is_packed = false; + } + + auto basename = to_flattened_access_chain_expression(base); + auto ret = join(basename, "_", chain); + ParsedIR::sanitize_underscores(ret); + return ret; + } + else + { + AccessChainFlags flags = ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT; + if (ptr_chain) + { + flags |= ACCESS_CHAIN_PTR_CHAIN_BIT; + // PtrAccessChain could get complicated. + TypeID type_id = expression_type_id(base); + if (backend.native_pointers && has_decoration(type_id, DecorationArrayStride)) + { + // If there is a mismatch we have to go via 64-bit pointer arithmetic :'( + // Using packed hacks only gets us so far, and is not designed to deal with pointer to + // random values. It works for structs though. + TypeID pointee_type_id = get_pointee_type_id(type_id); + uint32_t physical_stride = get_physical_type_id_stride(pointee_type_id); + uint32_t requested_stride = get_decoration(type_id, DecorationArrayStride); + if (physical_stride != requested_stride) + { + flags |= ACCESS_CHAIN_PTR_CHAIN_POINTER_ARITH_BIT; + if (is_vector(get(pointee_type_id))) + flags |= ACCESS_CHAIN_PTR_CHAIN_CAST_TO_SCALAR_BIT; + } + } + } + + return access_chain_internal(base, indices, count, flags, meta, untyped_data_type); + } +} + +string CompilerGLSL::load_flattened_struct(const string &basename, const SPIRType &type) +{ + auto expr = type_to_glsl_constructor(type); + expr += '('; + + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + if (i) + expr += ", "; + + auto &member_type = get(type.member_types[i]); + if (member_type.basetype == SPIRType::Struct) + expr += load_flattened_struct(to_flattened_struct_member(basename, type, i), member_type); + else + expr += to_flattened_struct_member(basename, type, i); + } + expr += ')'; + return expr; +} + +std::string CompilerGLSL::to_flattened_access_chain_expression(uint32_t id) +{ + // Do not use to_expression as that will unflatten access chains. + string basename; + if (const auto *var = maybe_get(id)) + basename = to_name(var->self); + else if (const auto *expr = maybe_get(id)) + basename = expr->expression; + else + basename = to_expression(id); + + return basename; +} + +void CompilerGLSL::store_flattened_struct(const string &basename, uint32_t rhs_id, const SPIRType &type, + const SmallVector &indices) +{ + SmallVector sub_indices = indices; + sub_indices.push_back(0); + + auto *member_type = &type; + for (auto &index : indices) + member_type = &get(member_type->member_types[index]); + + for (uint32_t i = 0; i < uint32_t(member_type->member_types.size()); i++) + { + sub_indices.back() = i; + auto lhs = join(basename, "_", to_member_name(*member_type, i)); + ParsedIR::sanitize_underscores(lhs); + + if (get(member_type->member_types[i]).basetype == SPIRType::Struct) + { + store_flattened_struct(lhs, rhs_id, type, sub_indices); + } + else + { + auto rhs = to_expression(rhs_id) + to_multi_member_reference(type, sub_indices); + statement(lhs, " = ", rhs, ";"); + } + } +} + +void CompilerGLSL::store_flattened_struct(uint32_t lhs_id, uint32_t value) +{ + auto &type = expression_type(lhs_id); + auto basename = to_flattened_access_chain_expression(lhs_id); + store_flattened_struct(basename, value, type, {}); +} + +std::string CompilerGLSL::flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + uint32_t /* array_stride */, bool need_transpose) +{ + if (!target_type.array.empty()) + SPIRV_CROSS_THROW("Access chains that result in an array can not be flattened"); + else if (target_type.basetype == SPIRType::Struct) + return flattened_access_chain_struct(base, indices, count, target_type, offset); + else if (target_type.columns > 1) + return flattened_access_chain_matrix(base, indices, count, target_type, offset, matrix_stride, need_transpose); + else + return flattened_access_chain_vector(base, indices, count, target_type, offset, matrix_stride, need_transpose); +} + +std::string CompilerGLSL::flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset) +{ + std::string expr; + + if (backend.can_declare_struct_inline) + { + expr += type_to_glsl_constructor(target_type); + expr += "("; + } + else + expr += "{"; + + for (uint32_t i = 0; i < uint32_t(target_type.member_types.size()); ++i) + { + if (i != 0) + expr += ", "; + + const SPIRType &member_type = get(target_type.member_types[i]); + uint32_t member_offset = type_struct_member_offset(target_type, i); + + // The access chain terminates at the struct, so we need to find matrix strides and row-major information + // ahead of time. + bool need_transpose = false; + bool relaxed = false; + uint32_t matrix_stride = 0; + if (member_type.columns > 1) + { + auto decorations = combined_decoration_for_member(target_type, i); + need_transpose = decorations.get(DecorationRowMajor); + relaxed = decorations.get(DecorationRelaxedPrecision); + matrix_stride = type_struct_member_matrix_stride(target_type, i); + } + + auto tmp = flattened_access_chain(base, indices, count, member_type, offset + member_offset, matrix_stride, + 0 /* array_stride */, need_transpose); + + // Cannot forward transpositions, so resolve them here. + if (need_transpose) + expr += convert_row_major_matrix(tmp, member_type, 0, false, relaxed); + else + expr += tmp; + } + + expr += backend.can_declare_struct_inline ? ")" : "}"; + + return expr; +} + +std::string CompilerGLSL::flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, + uint32_t matrix_stride, bool need_transpose) +{ + assert(matrix_stride); + SPIRType tmp_type = target_type; + if (need_transpose) + swap(tmp_type.vecsize, tmp_type.columns); + + std::string expr; + + expr += type_to_glsl_constructor(tmp_type); + expr += "("; + + for (uint32_t i = 0; i < tmp_type.columns; i++) + { + if (i != 0) + expr += ", "; + + expr += flattened_access_chain_vector(base, indices, count, tmp_type, offset + i * matrix_stride, matrix_stride, + /* need_transpose= */ false); + } + + expr += ")"; + + return expr; +} + +std::string CompilerGLSL::flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, + uint32_t matrix_stride, bool need_transpose) +{ + auto result = flattened_access_chain_offset(expression_type(base), indices, count, offset, 16); + + auto buffer_name = to_name(expression_type(base).self); + + if (need_transpose) + { + std::string expr; + + if (target_type.vecsize > 1) + { + expr += type_to_glsl_constructor(target_type); + expr += "("; + } + + for (uint32_t i = 0; i < target_type.vecsize; ++i) + { + if (i != 0) + expr += ", "; + + uint32_t component_offset = result.second + i * matrix_stride; + + assert(component_offset % (target_type.width / 8) == 0); + uint32_t index = component_offset / (target_type.width / 8); + + expr += buffer_name; + expr += "["; + expr += result.first; // this is a series of N1 * k1 + N2 * k2 + ... that is either empty or ends with a + + expr += convert_to_string(index / 4); + expr += "]"; + + expr += vector_swizzle(1, index % 4); + } + + if (target_type.vecsize > 1) + { + expr += ")"; + } + + return expr; + } + else + { + assert(result.second % (target_type.width / 8) == 0); + uint32_t index = result.second / (target_type.width / 8); + + std::string expr; + + expr += buffer_name; + expr += "["; + expr += result.first; // this is a series of N1 * k1 + N2 * k2 + ... that is either empty or ends with a + + expr += convert_to_string(index / 4); + expr += "]"; + + expr += vector_swizzle(target_type.vecsize, index % 4); + + return expr; + } +} + +std::pair CompilerGLSL::flattened_access_chain_offset( + const SPIRType &basetype, const uint32_t *indices, uint32_t count, uint32_t offset, uint32_t word_stride, + bool *need_transpose, uint32_t *out_matrix_stride, uint32_t *out_array_stride, bool ptr_chain) +{ + // Start traversing type hierarchy at the proper non-pointer types. + const auto *type = &get_pointee_type(basetype); + + std::string expr; + + // Inherit matrix information in case we are access chaining a vector which might have come from a row major layout. + bool row_major_matrix_needs_conversion = need_transpose ? *need_transpose : false; + uint32_t matrix_stride = out_matrix_stride ? *out_matrix_stride : 0; + uint32_t array_stride = out_array_stride ? *out_array_stride : 0; + + for (uint32_t i = 0; i < count; i++) + { + uint32_t index = indices[i]; + + // Pointers + if (ptr_chain && i == 0) + { + // Here, the pointer type will be decorated with an array stride. + array_stride = get_decoration(basetype.self, DecorationArrayStride); + if (!array_stride) + SPIRV_CROSS_THROW("SPIR-V does not define ArrayStride for buffer block."); + + auto *constant = maybe_get(index); + if (constant) + { + // Constant array access. + offset += constant->scalar() * array_stride; + } + else + { + // Dynamic array access. + if (array_stride % word_stride) + { + SPIRV_CROSS_THROW("Array stride for dynamic indexing must be divisible by the size " + "of a 4-component vector. " + "Likely culprit here is a float or vec2 array inside a push " + "constant block which is std430. " + "This cannot be flattened. Try using std140 layout instead."); + } + + expr += to_enclosed_expression(index); + expr += " * "; + expr += convert_to_string(array_stride / word_stride); + expr += " + "; + } + } + // Arrays + else if (!type->array.empty()) + { + auto *constant = maybe_get(index); + if (constant) + { + // Constant array access. + offset += constant->scalar() * array_stride; + } + else + { + // Dynamic array access. + if (array_stride % word_stride) + { + SPIRV_CROSS_THROW("Array stride for dynamic indexing must be divisible by the size " + "of a 4-component vector. " + "Likely culprit here is a float or vec2 array inside a push " + "constant block which is std430. " + "This cannot be flattened. Try using std140 layout instead."); + } + + expr += to_enclosed_expression(index, false); + expr += " * "; + expr += convert_to_string(array_stride / word_stride); + expr += " + "; + } + + uint32_t parent_type = type->parent_type; + type = &get(parent_type); + + if (!type->array.empty()) + array_stride = get_decoration(parent_type, DecorationArrayStride); + } + // For structs, the index refers to a constant, which indexes into the members. + // We also check if this member is a builtin, since we then replace the entire expression with the builtin one. + else if (type->basetype == SPIRType::Struct) + { + index = evaluate_constant_u32(index); + + if (index >= type->member_types.size()) + SPIRV_CROSS_THROW("Member index is out of bounds!"); + + offset += type_struct_member_offset(*type, index); + + auto &struct_type = *type; + type = &get(type->member_types[index]); + + if (type->columns > 1) + { + matrix_stride = type_struct_member_matrix_stride(struct_type, index); + row_major_matrix_needs_conversion = + combined_decoration_for_member(struct_type, index).get(DecorationRowMajor); + } + else + row_major_matrix_needs_conversion = false; + + if (!type->array.empty()) + array_stride = type_struct_member_array_stride(struct_type, index); + } + // Matrix -> Vector + else if (type->columns > 1) + { + auto *constant = maybe_get(index); + if (constant) + { + index = evaluate_constant_u32(index); + offset += index * (row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride); + } + else + { + uint32_t indexing_stride = row_major_matrix_needs_conversion ? (type->width / 8) : matrix_stride; + // Dynamic array access. + if (indexing_stride % word_stride) + { + SPIRV_CROSS_THROW("Matrix stride for dynamic indexing must be divisible by the size of a " + "4-component vector. " + "Likely culprit here is a row-major matrix being accessed dynamically. " + "This cannot be flattened. Try using std140 layout instead."); + } + + expr += to_enclosed_expression(index, false); + expr += " * "; + expr += convert_to_string(indexing_stride / word_stride); + expr += " + "; + } + + type = &get(type->parent_type); + } + // Vector -> Scalar + else if (type->vecsize > 1) + { + auto *constant = maybe_get(index); + if (constant) + { + index = evaluate_constant_u32(index); + offset += index * (row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8)); + } + else + { + uint32_t indexing_stride = row_major_matrix_needs_conversion ? matrix_stride : (type->width / 8); + + // Dynamic array access. + if (indexing_stride % word_stride) + { + SPIRV_CROSS_THROW("Stride for dynamic vector indexing must be divisible by the " + "size of a 4-component vector. " + "This cannot be flattened in legacy targets."); + } + + expr += to_enclosed_expression(index, false); + expr += " * "; + expr += convert_to_string(indexing_stride / word_stride); + expr += " + "; + } + + type = &get(type->parent_type); + } + else + SPIRV_CROSS_THROW("Cannot subdivide a scalar value!"); + } + + if (need_transpose) + *need_transpose = row_major_matrix_needs_conversion; + if (out_matrix_stride) + *out_matrix_stride = matrix_stride; + if (out_array_stride) + *out_array_stride = array_stride; + + return std::make_pair(expr, offset); +} + +bool CompilerGLSL::should_dereference(uint32_t id) +{ + const auto &type = expression_type(id); + // Non-pointer expressions don't need to be dereferenced. + if (!is_pointer(type)) + return false; + + // Handles shouldn't be dereferenced either. + if (!expression_is_lvalue(id)) + return false; + + // If id is a variable but not a phi variable, we should not dereference it. + // BDA passed around as parameters are always pointers. + if (auto *var = maybe_get(id)) + return (var->parameter && is_physical_or_buffer_pointer(type)) || var->phi_variable; + + if (auto *expr = maybe_get(id)) + { + // If id is an access chain, we should not dereference it. + if (expr->access_chain) + return false; + + // If id is a forwarded copy of a variable pointer, we should not dereference it. + SPIRVariable *var = nullptr; + while (expr->loaded_from && expression_is_forwarded(expr->self)) + { + auto &src_type = expression_type(expr->loaded_from); + // To be a copy, the pointer and its source expression must be the + // same type. Can't check type.self, because for some reason that's + // usually the base type with pointers stripped off. This check is + // complex enough that I've hoisted it out of the while condition. + if (src_type.pointer != type.pointer || src_type.pointer_depth != type.pointer_depth || + src_type.parent_type != type.parent_type) + break; + if ((var = maybe_get(expr->loaded_from))) + break; + if (!(expr = maybe_get(expr->loaded_from))) + break; + } + + return !var || var->phi_variable; + } + + // Otherwise, we should dereference this pointer expression. + return true; +} + +bool CompilerGLSL::should_dereference_caller_param(uint32_t id) +{ + const auto &type = expression_type(id); + // BDA is always passed around as pointers. Similarly, we need to pass variable buffer pointers as pointers. + if (is_physical_or_buffer_pointer(type)) + return false; + + return should_dereference(id); +} + +bool CompilerGLSL::should_forward(uint32_t id) const +{ + // If id is a variable we will try to forward it regardless of force_temporary check below + // This is important because otherwise we'll get local sampler copies (highp sampler2D foo = bar) that are invalid in OpenGL GLSL + + auto *var = maybe_get(id); + if (var) + { + // Never forward volatile builtin variables, e.g. SPIR-V 1.6 HelperInvocation. + return !(has_decoration(id, DecorationBuiltIn) && has_decoration(id, DecorationVolatile)); + } + + // For debugging emit temporary variables for all expressions + if (options.force_temporary) + return false; + + // If an expression carries enough dependencies we need to stop forwarding at some point, + // or we explode compilers. There are usually limits to how much we can nest expressions. + auto *expr = maybe_get(id); + const uint32_t max_expression_dependencies = 64; + if (expr && expr->expression_dependencies.size() >= max_expression_dependencies) + return false; + + if (expr && expr->loaded_from + && has_decoration(expr->loaded_from, DecorationBuiltIn) + && has_decoration(expr->loaded_from, DecorationVolatile)) + { + // Never forward volatile builtin variables, e.g. SPIR-V 1.6 HelperInvocation. + return false; + } + + // Immutable expression can always be forwarded. + if (is_immutable(id)) + return true; + + if (expr && expr->buffer_pointer) + return true; + + return false; +} + +bool CompilerGLSL::should_suppress_usage_tracking(uint32_t id) const +{ + // Used only by opcodes which don't do any real "work", they just swizzle data in some fashion. + return !expression_is_forwarded(id) || expression_suppresses_usage_tracking(id); +} + +void CompilerGLSL::track_expression_read(uint32_t id) +{ + switch (ir.ids[id].get_type()) + { + case TypeExpression: + { + auto &e = get(id); + for (auto implied_read : e.implied_read_expressions) + track_expression_read(implied_read); + break; + } + + case TypeAccessChain: + { + auto &e = get(id); + for (auto implied_read : e.implied_read_expressions) + track_expression_read(implied_read); + break; + } + + default: + break; + } + + // If we try to read a forwarded temporary more than once we will stamp out possibly complex code twice. + // In this case, it's better to just bind the complex expression to the temporary and read that temporary twice. + if (expression_is_forwarded(id) && !expression_suppresses_usage_tracking(id)) + { + auto &v = expression_usage_counts[id]; + v++; + + // If we create an expression outside a loop, + // but access it inside a loop, we're implicitly reading it multiple times. + // If the expression in question is expensive, we should hoist it out to avoid relying on loop-invariant code motion + // working inside the backend compiler. + if (expression_read_implies_multiple_reads(id)) + v++; + + if (v >= 2) + { + //if (v == 2) + // fprintf(stderr, "ID %u was forced to temporary due to more than 1 expression use!\n", id); + + // Force a recompile after this pass to avoid forwarding this variable. + force_temporary_and_recompile(id); + } + } +} + +bool CompilerGLSL::args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure) +{ + if (forced_temporaries.find(id) != end(forced_temporaries)) + return false; + + for (uint32_t i = 0; i < num_args; i++) + if (!should_forward(args[i])) + return false; + + // We need to forward globals as well. + if (!pure) + { + for (auto global : global_variables) + if (!should_forward(global)) + return false; + for (auto aliased : aliased_variables) + if (!should_forward(aliased)) + return false; + } + + return true; +} + +void CompilerGLSL::register_impure_function_call() +{ + // Impure functions can modify globals and aliased variables, so invalidate them as well. + for (auto global : global_variables) + flush_dependees(get(global)); + for (auto aliased : aliased_variables) + flush_dependees(get(aliased)); + for (auto ptr : buffer_pointer_variables) + flush_dependees(get(ptr)); +} + +void CompilerGLSL::register_call_out_argument(uint32_t id) +{ + register_write(id); + + auto *var = maybe_get(id); + if (var) + flush_variable_declaration(var->self); +} + +string CompilerGLSL::variable_decl_function_local(SPIRVariable &var) +{ + // These variables are always function local, + // so make sure we emit the variable without storage qualifiers. + // Some backends will inject custom variables locally in a function + // with a storage qualifier which is not function-local. + auto old_storage = var.storage; + var.storage = StorageClassFunction; + auto expr = variable_decl(var); + var.storage = old_storage; + return expr; +} + +void CompilerGLSL::emit_variable_temporary_copies(const SPIRVariable &var) +{ + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + if (var.allocate_temporary_copy && !flushed_phi_variables.count(var.self)) + { + auto &type = get(var.basetype); + auto &flags = get_decoration_bitset(var.self); + statement(flags_to_qualifiers_glsl(type, var.self, flags), variable_decl(type, join("_", var.self, "_copy")), ";"); + flushed_phi_variables.insert(var.self); + } +} + +void CompilerGLSL::flush_variable_declaration(uint32_t id) +{ + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + auto *var = maybe_get(id); + if (var && var->deferred_declaration) + { + string initializer; + if (options.force_zero_initialized_variables && + (var->storage == StorageClassFunction || var->storage == StorageClassGeneric || + var->storage == StorageClassPrivate) && + !var->initializer && type_can_zero_initialize(get_variable_data_type(*var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(*var))); + } + + statement(variable_decl_function_local(*var), initializer, ";"); + var->deferred_declaration = false; + } + if (var) + { + emit_variable_temporary_copies(*var); + } +} + +bool CompilerGLSL::remove_duplicate_swizzle(string &op) +{ + auto pos = op.find_last_of('.'); + if (pos == string::npos || pos == 0) + return false; + + string final_swiz = op.substr(pos + 1, string::npos); + + if (backend.swizzle_is_function) + { + if (final_swiz.size() < 2) + return false; + + if (final_swiz.substr(final_swiz.size() - 2, string::npos) == "()") + final_swiz.erase(final_swiz.size() - 2, string::npos); + else + return false; + } + + // Check if final swizzle is of form .x, .xy, .xyz, .xyzw or similar. + // If so, and previous swizzle is of same length, + // we can drop the final swizzle altogether. + for (uint32_t i = 0; i < final_swiz.size(); i++) + { + static const char expected[] = { 'x', 'y', 'z', 'w' }; + if (i >= 4 || final_swiz[i] != expected[i]) + return false; + } + + auto prevpos = op.find_last_of('.', pos - 1); + if (prevpos == string::npos) + return false; + + prevpos++; + + // Make sure there are only swizzles here ... + for (auto i = prevpos; i < pos; i++) + { + if (op[i] < 'w' || op[i] > 'z') + { + // If swizzles are foo.xyz() like in C++ backend for example, check for that. + if (backend.swizzle_is_function && i + 2 == pos && op[i] == '(' && op[i + 1] == ')') + break; + return false; + } + } + + // If original swizzle is large enough, just carve out the components we need. + // E.g. foobar.wyx.xy will turn into foobar.wy. + if (pos - prevpos >= final_swiz.size()) + { + op.erase(prevpos + final_swiz.size(), string::npos); + + // Add back the function call ... + if (backend.swizzle_is_function) + op += "()"; + } + return true; +} + +// Optimizes away vector swizzles where we have something like +// vec3 foo; +// foo.xyz <-- swizzle expression does nothing. +// This is a very common pattern after OpCompositeCombine. +bool CompilerGLSL::remove_unity_swizzle(uint32_t base, string &op) +{ + auto pos = op.find_last_of('.'); + if (pos == string::npos || pos == 0) + return false; + + string final_swiz = op.substr(pos + 1, string::npos); + + if (backend.swizzle_is_function) + { + if (final_swiz.size() < 2) + return false; + + if (final_swiz.substr(final_swiz.size() - 2, string::npos) == "()") + final_swiz.erase(final_swiz.size() - 2, string::npos); + else + return false; + } + + // Check if final swizzle is of form .x, .xy, .xyz, .xyzw or similar. + // If so, and previous swizzle is of same length, + // we can drop the final swizzle altogether. + for (uint32_t i = 0; i < final_swiz.size(); i++) + { + static const char expected[] = { 'x', 'y', 'z', 'w' }; + if (i >= 4 || final_swiz[i] != expected[i]) + return false; + } + + auto &type = expression_type(base); + + // Sanity checking ... + assert(type.columns == 1 && type.array.empty()); + + if (type.vecsize == final_swiz.size()) + op.erase(pos, string::npos); + return true; +} + +string CompilerGLSL::build_composite_combiner(uint32_t return_type, const uint32_t *elems, uint32_t length) +{ + ID base = 0; + string op; + string subop; + + // Can only merge swizzles for vectors. + auto &type = get(return_type); + bool can_apply_swizzle_opt = type.basetype != SPIRType::Struct && type.array.empty() && type.columns == 1 && + type.op != OpTypeCooperativeMatrixKHR; + bool swizzle_optimization = false; + + for (uint32_t i = 0; i < length; i++) + { + auto *e = maybe_get(elems[i]); + + // If we're merging another scalar which belongs to the same base + // object, just merge the swizzles to avoid triggering more than 1 expression read as much as possible! + if (can_apply_swizzle_opt && e && e->base_expression && e->base_expression == base) + { + // Only supposed to be used for vector swizzle -> scalar. + assert(!e->expression.empty() && e->expression.front() == '.'); + subop += e->expression.substr(1, string::npos); + swizzle_optimization = true; + } + else + { + // We'll likely end up with duplicated swizzles, e.g. + // foobar.xyz.xyz from patterns like + // OpVectorShuffle + // OpCompositeExtract x 3 + // OpCompositeConstruct 3x + other scalar. + // Just modify op in-place. + if (swizzle_optimization) + { + if (backend.swizzle_is_function) + subop += "()"; + + // Don't attempt to remove unity swizzling if we managed to remove duplicate swizzles. + // The base "foo" might be vec4, while foo.xyz is vec3 (OpVectorShuffle) and looks like a vec3 due to the .xyz tacked on. + // We only want to remove the swizzles if we're certain that the resulting base will be the same vecsize. + // Essentially, we can only remove one set of swizzles, since that's what we have control over ... + // Case 1: + // foo.yxz.xyz: Duplicate swizzle kicks in, giving foo.yxz, we are done. + // foo.yxz was the result of OpVectorShuffle and we don't know the type of foo. + // Case 2: + // foo.xyz: Duplicate swizzle won't kick in. + // If foo is vec3, we can remove xyz, giving just foo. + if (!remove_duplicate_swizzle(subop)) + remove_unity_swizzle(base, subop); + + // Strips away redundant parens if we created them during component extraction. + strip_enclosed_expression(subop); + swizzle_optimization = false; + op += subop; + } + else + op += subop; + + if (i) + op += ", "; + + bool uses_buffer_offset = + type.basetype == SPIRType::Struct && has_member_decoration(type.self, i, DecorationOffset); + subop = to_composite_constructor_expression(type, elems[i], uses_buffer_offset); + } + + base = e ? e->base_expression : ID(0); + } + + if (swizzle_optimization) + { + if (backend.swizzle_is_function) + subop += "()"; + + if (!remove_duplicate_swizzle(subop)) + remove_unity_swizzle(base, subop); + // Strips away redundant parens if we created them during component extraction. + strip_enclosed_expression(subop); + } + + op += subop; + return op; +} + +bool CompilerGLSL::skip_argument(uint32_t id) const +{ + if (!combined_image_samplers.empty() || !options.vulkan_semantics) + { + auto &type = expression_type(id); + if (type.basetype == SPIRType::Sampler || (type.basetype == SPIRType::Image && type.image.sampled == 1)) + return true; + } + return false; +} + +bool CompilerGLSL::optimize_read_modify_write(const SPIRType &type, const string &lhs, const string &rhs) +{ + // Do this with strings because we have a very clear pattern we can check for and it avoids + // adding lots of special cases to the code emission. + if (rhs.size() < lhs.size() + 3) + return false; + + // Do not optimize matrices. They are a bit awkward to reason about in general + // (in which order does operation happen?), and it does not work on MSL anyways. + if (type.vecsize > 1 && type.columns > 1) + return false; + + auto index = rhs.find(lhs); + if (index != 0) + return false; + + // TODO: Shift operators, but it's not important for now. + auto op = rhs.find_first_of("+-/*%|&^", lhs.size() + 1); + if (op != lhs.size() + 1) + return false; + + // Check that the op is followed by space. This excludes && and ||. + if (rhs[op + 1] != ' ') + return false; + + char bop = rhs[op]; + auto expr = rhs.substr(lhs.size() + 3); + + // Avoids false positives where we get a = a * b + c. + // Normally, these expressions are always enclosed, but unexpected code paths may end up hitting this. + if (needs_enclose_expression(expr)) + return false; + + // Try to find increments and decrements. Makes it look neater as += 1, -= 1 is fairly rare to see in real code. + // Find some common patterns which are equivalent. + if ((bop == '+' || bop == '-') && (expr == "1" || expr == "uint(1)" || expr == "1u" || expr == "int(1u)")) + statement(lhs, bop, bop, ";"); + else + statement(lhs, " ", bop, "= ", expr, ";"); + return true; +} + +void CompilerGLSL::register_control_dependent_expression(uint32_t expr) +{ + if (forwarded_temporaries.find(expr) == end(forwarded_temporaries)) + return; + + assert(current_emitting_block); + current_emitting_block->invalidate_expressions.push_back(expr); +} + +void CompilerGLSL::emit_block_instructions(SPIRBlock &block) +{ + current_emitting_block = █ + + if (backend.requires_relaxed_precision_analysis) + { + // If PHI variables are consumed in unexpected precision contexts, copy them here. + for (size_t i = 0, n = block.phi_variables.size(); i < n; i++) + { + auto &phi = block.phi_variables[i]; + + // Ensure we only copy once. We know a-priori that this array will lay out + // the same function variables together. + if (i && block.phi_variables[i - 1].function_variable == phi.function_variable) + continue; + + auto itr = temporary_to_mirror_precision_alias.find(phi.function_variable); + if (itr != temporary_to_mirror_precision_alias.end()) + { + // Explicitly, we don't want to inherit RelaxedPrecision state in this CopyObject, + // so it helps to have handle_instruction_precision() on the outside of emit_instruction(). + EmbeddedInstruction inst; + inst.op = OpCopyObject; + inst.length = 3; + inst.ops.push_back(expression_type_id(itr->first)); + inst.ops.push_back(itr->second); + inst.ops.push_back(itr->first); + emit_instruction(inst); + } + } + } + + for (auto &op : block.ops) + { + auto temporary_copy = handle_instruction_precision(op); + emit_instruction(op); + if (temporary_copy.dst_id) + { + // Explicitly, we don't want to inherit RelaxedPrecision state in this CopyObject, + // so it helps to have handle_instruction_precision() on the outside of emit_instruction(). + EmbeddedInstruction inst; + inst.op = OpCopyObject; + inst.length = 3; + inst.ops.push_back(expression_type_id(temporary_copy.src_id)); + inst.ops.push_back(temporary_copy.dst_id); + inst.ops.push_back(temporary_copy.src_id); + + // Never attempt to hoist mirrored temporaries. + // They are hoisted in lock-step with their parents. + block_temporary_hoisting = true; + emit_instruction(inst); + block_temporary_hoisting = false; + } + } + + current_emitting_block = nullptr; +} + +void CompilerGLSL::disallow_forwarding_in_expression_chain(const SPIRExpression &expr) +{ + // Allow trivially forwarded expressions like OpLoad or trivial shuffles, + // these will be marked as having suppressed usage tracking. + // Our only concern is to make sure arithmetic operations are done in similar ways. + if (forced_invariant_temporaries.count(expr.self) == 0) + { + if (!expression_suppresses_usage_tracking(expr.self)) + force_temporary_and_recompile(expr.self); + forced_invariant_temporaries.insert(expr.self); + + for (auto &dependent : expr.invariance_dependencies) + disallow_forwarding_in_expression_chain(get(dependent)); + } +} + +void CompilerGLSL::handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id) +{ + // Variables or access chains marked invariant are complicated. We will need to make sure the code-gen leading up to + // this variable is consistent. The failure case for SPIRV-Cross is when an expression is forced to a temporary + // in one translation unit, but not another, e.g. due to multiple use of an expression. + // This causes variance despite the output variable being marked invariant, so the solution here is to force all dependent + // expressions to be temporaries. + // It is uncertain if this is enough to support invariant in all possible cases, but it should be good enough + // for all reasonable uses of invariant. + if (!has_decoration(store_id, DecorationInvariant)) + return; + + auto *expr = maybe_get(value_id); + if (!expr) + return; + + disallow_forwarding_in_expression_chain(*expr); +} + +void CompilerGLSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) +{ + auto rhs = to_pointer_expression(rhs_expression); + + // Statements to OpStore may be empty if it is a struct with zero members. Just forward the store to /dev/null. + if (!rhs.empty()) + { + handle_store_to_invariant_variable(lhs_expression, rhs_expression); + + if (!unroll_array_to_complex_store(lhs_expression, rhs_expression)) + { + auto lhs = to_dereferenced_expression(lhs_expression); + if (is_descriptor_non_uniform(lhs_expression)) + convert_non_uniform_expression(lhs, lhs_expression); + + // We might need to cast in order to store to a builtin. + cast_to_variable_store(lhs_expression, rhs, expression_type(rhs_expression)); + + // 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. + // Without this, we end up with i = i + 1, which is correct GLSL, but not correct GLES 2.0. + if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + } + register_write(lhs_expression); + } +} + +uint32_t CompilerGLSL::get_integer_width_for_instruction(const Instruction &instr) const +{ + if (instr.length < 3) + return 32; + + auto *ops = stream(instr); + + switch (instr.op) + { + case OpSConvert: + case OpConvertSToF: + case OpUConvert: + case OpConvertUToF: + case OpIEqual: + case OpINotEqual: + case OpSLessThan: + case OpSLessThanEqual: + case OpSGreaterThan: + case OpSGreaterThanEqual: + case OpULessThan: + case OpULessThanEqual: + case OpUGreaterThan: + case OpUGreaterThanEqual: + return expression_type(ops[2]).width; + + case OpSMulExtended: + case OpUMulExtended: + return get(get(ops[0]).member_types[0]).width; + + default: + { + // We can look at result type which is more robust. + auto *type = maybe_get(ops[0]); + if (type && type_is_integral(*type)) + return type->width; + else + return 32; + } + } +} + +uint32_t CompilerGLSL::get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *ops, uint32_t length) const +{ + if (length < 1) + return 32; + + switch (op) + { + case GLSLstd450SAbs: + case GLSLstd450SSign: + case GLSLstd450UMin: + case GLSLstd450SMin: + case GLSLstd450UMax: + case GLSLstd450SMax: + case GLSLstd450UClamp: + case GLSLstd450SClamp: + case GLSLstd450FindSMsb: + case GLSLstd450FindUMsb: + return expression_type(ops[0]).width; + + default: + { + // We don't need to care about other opcodes, just return 32. + return 32; + } + } +} + +void CompilerGLSL::forward_relaxed_precision(uint32_t dst_id, const uint32_t *args, uint32_t length) +{ + // Only GLSL supports RelaxedPrecision directly. + // We cannot implement this in HLSL or MSL because it is tied to the type system. + // In SPIR-V, everything must masquerade as 32-bit. + if (!backend.requires_relaxed_precision_analysis) + return; + + auto input_precision = analyze_expression_precision(args, length); + + // For expressions which are loaded or directly forwarded, we inherit mediump implicitly. + // For dst_id to be analyzed properly, it must inherit any relaxed precision decoration from src_id. + if (input_precision == Options::Mediump) + set_decoration(dst_id, DecorationRelaxedPrecision); +} + +CompilerGLSL::Options::Precision CompilerGLSL::analyze_expression_precision(const uint32_t *args, uint32_t length) const +{ + // Now, analyze the precision at which the arguments would run. + // GLSL rules are such that the precision used to evaluate an expression is equal to the highest precision + // for the inputs. Constants do not have inherent precision and do not contribute to this decision. + // If all inputs are constants, they inherit precision from outer expressions, including an l-value. + // In this case, we'll have to force a temporary for dst_id so that we can bind the constant expression with + // correct precision. + bool expression_has_highp = false; + bool expression_has_mediump = false; + + for (uint32_t i = 0; i < length; i++) + { + uint32_t arg = args[i]; + + auto handle_type = ir.ids[arg].get_type(); + if (handle_type == TypeConstant || handle_type == TypeConstantOp || handle_type == TypeUndef) + continue; + + if (has_decoration(arg, DecorationRelaxedPrecision)) + expression_has_mediump = true; + else + expression_has_highp = true; + } + + if (expression_has_highp) + return Options::Highp; + else if (expression_has_mediump) + return Options::Mediump; + else + return Options::DontCare; +} + +void CompilerGLSL::analyze_precision_requirements(uint32_t type_id, uint32_t dst_id, uint32_t *args, uint32_t length) +{ + if (!backend.requires_relaxed_precision_analysis) + return; + + auto &type = get(type_id); + + // RelaxedPrecision only applies to 32-bit values. + if (type.basetype != SPIRType::Float && type.basetype != SPIRType::Int && type.basetype != SPIRType::UInt) + return; + + bool operation_is_highp = !has_decoration(dst_id, DecorationRelaxedPrecision); + + auto input_precision = analyze_expression_precision(args, length); + if (input_precision == Options::DontCare) + { + consume_temporary_in_precision_context(type_id, dst_id, input_precision); + return; + } + + // In SPIR-V and GLSL, the semantics are flipped for how relaxed precision is determined. + // In SPIR-V, the operation itself marks RelaxedPrecision, meaning that inputs can be truncated to 16-bit. + // However, if the expression is not, inputs must be expanded to 32-bit first, + // since the operation must run at high precision. + // This is the awkward part, because if we have mediump inputs, or expressions which derived from mediump, + // we might have to forcefully bind the source IDs to highp temporaries. This is done by clearing decorations + // and forcing temporaries. Similarly for mediump operations. We bind highp expressions to mediump variables. + if ((operation_is_highp && input_precision == Options::Mediump) || + (!operation_is_highp && input_precision == Options::Highp)) + { + auto precision = operation_is_highp ? Options::Highp : Options::Mediump; + for (uint32_t i = 0; i < length; i++) + { + // Rewrites the opcode so that we consume an ID in correct precision context. + // This is pretty hacky, but it's the most straight forward way of implementing this without adding + // lots of extra passes to rewrite all code blocks. + args[i] = consume_temporary_in_precision_context(expression_type_id(args[i]), args[i], precision); + } + } +} + +// This is probably not exhaustive ... +static bool opcode_is_precision_sensitive_operation(Op op) +{ + switch (op) + { + case OpFAdd: + case OpFSub: + case OpFMul: + case OpFNegate: + case OpIAdd: + case OpISub: + case OpIMul: + case OpSNegate: + case OpFMod: + case OpFDiv: + case OpFRem: + case OpSMod: + case OpSDiv: + case OpSRem: + case OpUMod: + case OpUDiv: + case OpVectorTimesMatrix: + case OpMatrixTimesVector: + case OpMatrixTimesMatrix: + case OpDPdx: + case OpDPdy: + case OpDPdxCoarse: + case OpDPdyCoarse: + case OpDPdxFine: + case OpDPdyFine: + case OpFwidth: + case OpFwidthCoarse: + case OpFwidthFine: + case OpVectorTimesScalar: + case OpMatrixTimesScalar: + case OpOuterProduct: + case OpFConvert: + case OpSConvert: + case OpUConvert: + case OpConvertSToF: + case OpConvertUToF: + case OpConvertFToU: + case OpConvertFToS: + return true; + + default: + return false; + } +} + +// Instructions which just load data but don't do any arithmetic operation should just inherit the decoration. +// SPIR-V doesn't require this, but it's somewhat implied it has to work this way, relaxed precision is only +// relevant when operating on the IDs, not when shuffling things around. +static bool opcode_is_precision_forwarding_instruction(Op op, uint32_t &arg_count) +{ + switch (op) + { + case OpLoad: + case OpAccessChain: + case OpInBoundsAccessChain: + case OpCompositeExtract: + case OpVectorExtractDynamic: + case OpSampledImage: + case OpImage: + case OpCopyObject: + + case OpImageRead: + case OpImageFetch: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleExplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageGather: + case OpImageDrefGather: + case OpImageSparseRead: + case OpImageSparseFetch: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseSampleExplicitLod: + case OpImageSparseSampleProjExplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageSparseGather: + case OpImageSparseDrefGather: + arg_count = 1; + return true; + + case OpVectorShuffle: + arg_count = 2; + return true; + + case OpCompositeConstruct: + return true; + + default: + break; + } + + return false; +} + +CompilerGLSL::TemporaryCopy CompilerGLSL::handle_instruction_precision(const Instruction &instruction) +{ + auto ops = stream_mutable(instruction); + auto opcode = static_cast(instruction.op); + uint32_t length = instruction.length; + + if (backend.requires_relaxed_precision_analysis) + { + if (length > 2) + { + uint32_t forwarding_length = length - 2; + + if (opcode_is_precision_sensitive_operation(opcode)) + analyze_precision_requirements(ops[0], ops[1], &ops[2], forwarding_length); + else if (opcode == OpExtInst && length >= 5 && get(ops[2]).ext == SPIRExtension::GLSL) + analyze_precision_requirements(ops[0], ops[1], &ops[4], forwarding_length - 2); + else if (opcode_is_precision_forwarding_instruction(opcode, forwarding_length)) + forward_relaxed_precision(ops[1], &ops[2], forwarding_length); + } + + uint32_t result_type = 0, result_id = 0; + if (instruction_to_result_type(result_type, result_id, opcode, ops, length)) + { + auto itr = temporary_to_mirror_precision_alias.find(ops[1]); + if (itr != temporary_to_mirror_precision_alias.end()) + return { itr->second, itr->first }; + } + } + + return {}; +} + +static pair split_coopmat_pointer(const string &expr) +{ + auto ptr_expr = expr; + string index_expr; + + if (ptr_expr.back() != ']') + SPIRV_CROSS_THROW("Access chain for coopmat must be indexed into an array."); + + // Strip the access chain. + ptr_expr.pop_back(); + uint32_t counter = 1; + while (counter && !ptr_expr.empty()) + { + if (ptr_expr.back() == ']') + counter++; + else if (ptr_expr.back() == '[') + counter--; + ptr_expr.pop_back(); + } + + if (ptr_expr.empty()) + SPIRV_CROSS_THROW("Invalid pointer expression for coopmat."); + + index_expr = expr.substr(ptr_expr.size() + 1, expr.size() - (ptr_expr.size() + 1) - 1); + return { std::move(ptr_expr), std::move(index_expr) }; +} + +void CompilerGLSL::emit_instruction(const Instruction &instruction) +{ + auto ops = stream(instruction); + auto opcode = static_cast(instruction.op); + uint32_t length = instruction.length; + +#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, \ + opcode_is_sign_invariant(opcode), implicit_integer_promotion) +#define GLSL_UOP(op) emit_unary_op(ops[0], ops[1], ops[2], #op) +#define GLSL_UOP_CAST(op) emit_unary_op_cast(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, opcode_is_sign_invariant(opcode)) +#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) + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(instruction); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + // Handle C implicit integer promotion rules. + // If we get implicit promotion to int, need to make sure we cast by value to intended return type, + // otherwise, future sign-dependent operations and bitcasts will break. + bool implicit_integer_promotion = integer_width < 32 && backend.implicit_c_integer_promotion_rules && + opcode_can_promote_integer_implicitly(opcode) && + get(ops[0]).vecsize == 1; + + opcode = get_remapped_spirv_op(opcode); + + switch (opcode) + { + // Dealing with memory + case OpLoad: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + + flush_variable_declaration(ptr); + + // If we're loading from memory that cannot be changed by the shader, + // just forward the expression directly to avoid needless temporaries. + // If an expression is mutable and forwardable, we speculate that it is immutable. + bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries); + + // Volatile memory access requires the value be read exactly once from + // memory. Do not forward the expression so that re-evaluation at each + // use site cannot re-read potentially modified memory. + // FIXME: To force implementations to actually respect the volatile nature of the load, + // the block itself must be marked volatile, or VulkanMM is used to do an explicit volatile load. + if (forward && length >= 4 && (ops[3] & MemoryAccessVolatileMask) != 0) + forward = false; + + // If loading a non-native row-major matrix, mark the expression as need_transpose. + bool need_transpose = false; + bool old_need_transpose = false; + + auto *ptr_expression = maybe_get(ptr); + + if (forward) + { + // If we're forwarding the load, we're also going to forward transpose state, so don't transpose while + // taking the expression. + if (ptr_expression && ptr_expression->need_transpose) + { + old_need_transpose = true; + ptr_expression->need_transpose = false; + need_transpose = true; + } + else if (is_non_native_row_major_matrix(ptr)) + need_transpose = true; + } + + // If we are forwarding this load, + // don't register the read to access chain here, defer that to when we actually use the expression, + // using the add_implied_read_expression mechanism. + string expr; + + bool is_packed = has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypePacked); + bool is_remapped = has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypeID); + if (forward || (!is_packed && !is_remapped)) + { + // For the simple case, we do not need to deal with repacking. + expr = to_dereferenced_expression(ptr, false); + } + else + { + // If we are not forwarding the expression, we need to unpack and resolve any physical type remapping here before + // storing the expression to a temporary. + expr = to_unpacked_expression(ptr); + } + + auto &type = get(result_type); + auto &expr_type = expression_type(ptr); + + // If the expression has more vector components than the result type, insert + // a swizzle. This shouldn't happen normally on valid SPIR-V, but it might + // happen with e.g. the MSL backend replacing the type of an input variable. + if (expr_type.vecsize > type.vecsize) + expr = enclose_expression(expr + vector_swizzle(type.vecsize, 0)); + + if (forward && ptr_expression) + ptr_expression->need_transpose = old_need_transpose; + + // We might need to cast in order to load from a builtin. + cast_from_variable_load(ptr, expr, type); + + if (forward && ptr_expression) + ptr_expression->need_transpose = false; + + // We might be trying to load a gl_Position[N], where we should be + // doing float4[](gl_in[i].gl_Position, ...) instead. + // Similar workarounds are required for input arrays in tessellation. + // Also, loading from gl_SampleMask array needs special unroll. + unroll_array_from_complex_load(id, ptr, expr); + + if (!type_is_opaque_value(type) && is_descriptor_non_uniform(ptr)) + { + // If we're loading something non-opaque, we need to handle non-uniform descriptor access. + convert_non_uniform_expression(expr, ptr); + } + + if (forward && ptr_expression) + ptr_expression->need_transpose = old_need_transpose; + + bool flattened = ptr_expression && flattened_buffer_blocks.count(ptr_expression->loaded_from) != 0; + + if (backend.needs_row_major_load_workaround && !is_non_native_row_major_matrix(ptr) && !flattened) + rewrite_load_for_wrapped_row_major(expr, result_type, ptr); + + // By default, suppress usage tracking since using same expression multiple times does not imply any extra work. + // However, if we try to load a complex, composite object from a flattened buffer, + // we should avoid emitting the same code over and over and lower the result to a temporary. + bool usage_tracking = flattened && (type.basetype == SPIRType::Struct || (type.columns > 1)); + + SPIRExpression *e = nullptr; + if (!forward && expression_is_non_value_type_array(result_type, ptr)) + { + // Complicated load case where we need to make a copy of ptr, but we cannot, because + // it is an array, and our backend does not support arrays as value types. + // Emit the temporary, and copy it explicitly. + e = &emit_uninitialized_temporary_expression(result_type, id); + emit_array_copy(nullptr, id, ptr, StorageClassFunction, get_expression_effective_storage_class(ptr)); + } + else + e = &emit_op(result_type, id, expr, forward, !usage_tracking); + + e->need_transpose = need_transpose; + register_read(id, ptr, forward); + + if (forward) + { + // Pass through whether the result is of a packed type and the physical type ID. + if (has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypePacked)) + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + if (has_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypeID)) + { + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID, + get_extended_decoration(ptr, SPIRVCrossDecorationPhysicalTypeID)); + } + } + else + { + // This might have been set on an earlier compilation iteration, force it to be unset. + unset_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + unset_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID); + } + + inherit_expression_dependencies(id, ptr); + if (forward) + add_implied_read_expression(*e, ptr); + break; + } + + case OpUntypedPtrAccessChainKHR: + SPIRV_CROSS_THROW("OpUntypedPtrAccessChainKHR is not supported."); + break; + + case OpUntypedAccessChainKHR: + case OpUntypedInBoundsAccessChainKHR: + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + bool untyped = opcode == OpUntypedAccessChainKHR || opcode == OpUntypedInBoundsAccessChainKHR; + + uint32_t type_id = ops[0]; + uint32_t result_id = ops[1]; + uint32_t ptr_id = ops[untyped ? 3 : 2]; + uint32_t indices_start = untyped ? 4 : 3; + + if (untyped) + { + auto *var = maybe_get_backing_variable(ptr_id); + // Buffer pointers stop the loaded from chain to deal with aliasing better, so carve that out specifically. + auto *expr = maybe_get_backing_buffer_pointer(ptr_id); + + if (!expr) + { + if (!var || !has_decoration(var->self, DecorationBuiltIn) || + (BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInResourceHeapEXT && + BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInSamplerHeapEXT)) + { + SPIRV_CROSS_THROW("Untyped pointer access chains are currently only supported for descriptor heap access."); + } + } + } + + auto *var = maybe_get(ptr_id); + if (var) + flush_variable_declaration(var->self); + + // If the base is immutable, the access chain pointer must also be. + // If an expression is mutable and forwardable, we speculate that it is immutable. + AccessChainMeta meta; + bool ptr_chain = opcode == OpPtrAccessChain; + auto &target_type = get(type_id); + auto e = access_chain(ptr_id, &ops[indices_start], length - indices_start, target_type, &meta, ptr_chain, + untyped ? &get(ops[2]) : nullptr); + + if (untyped) + { + auto &data_type = get(ops[2]); + auto *ptr_expr = maybe_get(ptr_id); + if (data_type.basetype == SPIRType::Image || data_type.basetype == SPIRType::Sampler || + data_type.basetype == SPIRType::AccelerationStructure || + (ptr_expr && ptr_expr->buffer_pointer)) + { + // We can resolve this type now. + // For further buffer access chains, we don't do any fixups since we have resolved to proper types. + // For buffer types we only prepend when the access chain starts from a BufferPointerEXT base. + // Multi-stage access chains are not possible for image types. + if (ptr_expr && ptr_expr->buffer_pointer) + e = join(to_buffer_pointer_name_prefix(ptr_expr->self), e); + else + e = join("spv", to_name(data_type.self), e); + } + } + + // If the base is flattened UBO of struct type, the expression has to be a composite. + // In that case, backends which do not support inline syntax need it to be bound to a temporary. + // Otherwise, invalid expressions like ({UBO[0].xyz, UBO[0].w, UBO[1]}).member are emitted. + bool requires_temporary = false; + if (flattened_buffer_blocks.count(ptr_id) && target_type.basetype == SPIRType::Struct) + requires_temporary = !backend.can_declare_struct_inline; + + auto &expr = requires_temporary ? + emit_op(type_id, result_id, std::move(e), false) : + set(result_id, std::move(e), type_id, should_forward(ptr_id)); + + auto *backing_variable = maybe_get_backing_variable(ptr_id); + expr.loaded_from = backing_variable ? backing_variable->self : ID(ptr_id); + expr.need_transpose = meta.need_transpose; + expr.access_chain = true; + expr.access_meshlet_position_y = meta.access_meshlet_position_y; + + // Mark the result as being packed. Some platforms handled packed vectors differently than non-packed. + if (meta.storage_is_packed) + set_extended_decoration(result_id, SPIRVCrossDecorationPhysicalTypePacked); + if (meta.storage_physical_type != 0) + set_extended_decoration(result_id, SPIRVCrossDecorationPhysicalTypeID, meta.storage_physical_type); + if (meta.storage_is_invariant) + set_decoration(result_id, DecorationInvariant); + if (meta.flattened_struct) + flattened_structs[result_id] = true; + if (meta.relaxed_precision && backend.requires_relaxed_precision_analysis) + set_decoration(result_id, DecorationRelaxedPrecision); + if (meta.chain_is_builtin) + set_decoration(result_id, DecorationBuiltIn, meta.builtin); + + // If we have some expression dependencies in our access chain, this access chain is technically a forwarded + // temporary which could be subject to invalidation. + // Need to assume we're forwarded while calling inherit_expression_depdendencies. + forwarded_temporaries.insert(result_id); + // The access chain itself is never forced to a temporary, but its dependencies might. + suppressed_usage_tracking.insert(result_id); + + // Include the base pointer. + for (uint32_t i = indices_start - 1; i < length; i++) + { + inherit_expression_dependencies(result_id, ops[i]); + add_implied_read_expression(expr, ops[i]); + } + + // If we have no dependencies after all, i.e., all indices in the access chain are immutable temporaries, + // we're not forwarded after all. + if (expr.expression_dependencies.empty()) + forwarded_temporaries.erase(result_id); + + break; + } + + case OpStore: + { + auto *var = maybe_get(ops[0]); + + if (var && var->statically_assigned) + var->static_expression = ops[1]; + else if (var && var->loop_variable && !var->loop_variable_enable) + var->static_expression = ops[1]; + else if (var && var->remapped_variable && var->static_expression) + { + // Skip the write. + } + else if (flattened_structs.count(ops[0])) + { + store_flattened_struct(ops[0], ops[1]); + register_write(ops[0]); + } + else + { + emit_store_statement(ops[0], ops[1]); + } + + // Storing a pointer results in a variable pointer, so we must conservatively assume + // we can write through it. + if (expression_type(ops[1]).pointer) + register_write(ops[1]); + break; + } + + case OpUntypedArrayLengthKHR: + case OpArrayLength: + { + bool untyped = opcode == OpUntypedArrayLengthKHR; + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + const SPIRType *untyped_data_type = untyped ? &get(ops[2]) : nullptr; + uint32_t ptr_id = ops[untyped ? 3 : 2]; + uint32_t index_offset = untyped ? 4 : 3; + + auto e = access_chain_internal(ptr_id, &ops[index_offset], length - index_offset, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, + nullptr, untyped_data_type); + + if (untyped) + { + auto *ptr_expr = maybe_get(ptr_id); + if (ptr_expr && ptr_expr->buffer_pointer) + e = join(to_buffer_pointer_name_prefix(ptr_expr->self), e); + } + + if (is_descriptor_non_uniform(ptr_id)) + convert_non_uniform_expression(e, ptr_id); + set(id, join(type_to_glsl(get(result_type)), "(", e, ".length())"), result_type, true); + break; + } + + case OpBufferPointerEXT: + { + uint32_t type_id = ops[0]; + uint32_t result_id = ops[1]; + uint32_t ptr_id = ops[2]; + + auto *backing_variable = maybe_get_backing_variable(ptr_id); + if (!backing_variable) + SPIRV_CROSS_THROW("There is no backing variable for BufferPointerEXT."); + + auto *chain_expr = maybe_get(ptr_id); + if (!chain_expr || !chain_expr->access_chain) + SPIRV_CROSS_THROW("Expected to see access chain for BufferPointerEXT."); + + auto e = to_expression(ptr_id); + + // BufferPointerEXT can return a typed pointer, in which case we need to resolve the heap alias now. + auto &type = get(type_id); + if (type.basetype == SPIRType::Struct) + e = join(to_buffer_pointer_name_prefix(result_id), e); + + auto &expr = set(result_id, std::move(e), type_id, true); + // There isn't any backing variable here. OpBufferPointerEXT is meant to be a memory declaration instruction. + expr.loaded_from = 0; + expr.access_chain = true; + expr.buffer_pointer = true; + expr.implied_read_expressions = chain_expr->implied_read_expressions; + expr.expression_dependencies = chain_expr->expression_dependencies; + expr.immutable = false; + + // If the buffer pointer is marked non-writable, ignore alias tracking by flagging the expression as immutable. + for (auto &heap : descriptor_heap_types) + { + if (heap.buffer_pointer_id == result_id) + { + if (heap.nonwritable) + expr.immutable = true; + break; + } + } + + if (!expr.immutable && ir.get_buffer_block_type_flags(get(type_id)).get(DecorationNonWritable)) + expr.immutable = true; + + // Used for load-store tracking. + buffer_pointer_variables.push_back(result_id); + break; + } + + // Function calls + case OpFunctionCall: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t func = ops[2]; + const auto *arg = &ops[3]; + length -= 3; + + auto &callee = get(func); + auto &return_type = get(callee.return_type); + bool pure = function_is_pure(callee); + bool control_dependent = function_is_control_dependent(callee); + + bool callee_has_out_variables = false; + bool emit_return_value_as_argument = false; + + // Invalidate out variables passed to functions since they can be OpStore'd to. + for (uint32_t i = 0; i < length; i++) + { + if (callee.arguments[i].write_count) + { + register_call_out_argument(arg[i]); + callee_has_out_variables = true; + } + + flush_variable_declaration(arg[i]); + } + + if (!return_type.array.empty() && !backend.can_return_array) + { + callee_has_out_variables = true; + emit_return_value_as_argument = true; + } + + if (!pure) + register_impure_function_call(); + + string funexpr; + SmallVector arglist; + funexpr += to_name(func) + "("; + + if (emit_return_value_as_argument) + { + statement(type_to_glsl(return_type), " ", to_name(id), type_to_array_glsl(return_type, 0), ";"); + arglist.push_back(to_name(id)); + } + + for (uint32_t i = 0; i < length; i++) + { + // Do not pass in separate images or samplers if we're remapping + // to combined image samplers. + if (skip_argument(arg[i])) + continue; + + arglist.push_back(to_func_call_arg(callee.arguments[i], arg[i])); + } + + for (auto &combined : callee.combined_parameters) + { + auto image_id = combined.global_image ? combined.image_id : VariableID(arg[combined.image_id]); + auto sampler_id = combined.global_sampler ? combined.sampler_id : VariableID(arg[combined.sampler_id]); + arglist.push_back(to_combined_image_sampler(image_id, sampler_id)); + } + + append_global_func_args(callee, length, arglist); + + funexpr += merge(arglist); + funexpr += ")"; + + // Check for function call constraints. + check_function_call_constraints(arg, length); + + if (return_type.basetype != SPIRType::Void) + { + // If the function actually writes to an out variable, + // take the conservative route and do not forward. + // The problem is that we might not read the function + // result (and emit the function) before an out variable + // is read (common case when return value is ignored! + // In order to avoid start tracking invalid variables, + // just avoid the forwarding problem altogether. + bool forward = args_will_forward(id, arg, length, pure) && !callee_has_out_variables && pure && + (forced_temporaries.find(id) == end(forced_temporaries)); + + if (emit_return_value_as_argument) + { + statement(funexpr, ";"); + set(id, to_name(id), result_type, true); + } + else + emit_op(result_type, id, funexpr, forward); + + // Function calls are implicit loads from all variables in question. + // Set dependencies for them. + for (uint32_t i = 0; i < length; i++) + register_read(id, arg[i], forward); + + // If we're going to forward the temporary result, + // put dependencies on every variable that must not change. + if (forward) + register_global_read_dependencies(callee, id); + } + else + statement(funexpr, ";"); + + if (control_dependent) + register_control_dependent_expression(id); + + break; + } + + // Composite munging + case OpCompositeConstruct: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + const auto *const elems = &ops[2]; + length -= 2; + + bool forward = true; + for (uint32_t i = 0; i < length; i++) + forward = forward && should_forward(elems[i]); + + auto &out_type = get(result_type); + auto *in_type = length > 0 ? &expression_type(elems[0]) : nullptr; + + // Only splat if we have vector constructors. + // Arrays and structs must be initialized properly in full. + bool composite = !out_type.array.empty() || out_type.basetype == SPIRType::Struct; + + bool splat = false; + bool swizzle_splat = false; + + if (in_type) + { + splat = in_type->vecsize == 1 && in_type->columns == 1 && !composite && backend.use_constructor_splatting; + swizzle_splat = in_type->vecsize == 1 && in_type->columns == 1 && backend.can_swizzle_scalar; + + if (ir.ids[elems[0]].get_type() == TypeConstant && !type_is_floating_point(*in_type)) + { + // Cannot swizzle literal integers as a special case. + swizzle_splat = false; + } + } + + if (splat || swizzle_splat) + { + uint32_t input = elems[0]; + for (uint32_t i = 0; i < length; i++) + { + if (input != elems[i]) + { + splat = false; + swizzle_splat = false; + } + } + } + + if (out_type.basetype == SPIRType::Struct && !backend.can_declare_struct_inline) + forward = false; + if (!out_type.array.empty() && !backend.can_declare_arrays_inline) + forward = false; + if (type_is_empty(out_type) && !backend.supports_empty_struct) + forward = false; + + string constructor_op; + if (backend.use_initializer_list && composite) + { + bool needs_trailing_tracket = false; + // Only use this path if we are building composites. + // This path cannot be used for arithmetic. + if (backend.use_typed_initializer_list && out_type.basetype == SPIRType::Struct && out_type.array.empty()) + constructor_op += type_to_glsl_constructor(get(result_type)); + else if (backend.use_typed_initializer_list && backend.array_is_value_type && !out_type.array.empty()) + { + // MSL path. Array constructor is baked into type here, do not use _constructor variant. + constructor_op += type_to_glsl_constructor(get(result_type)) + "("; + needs_trailing_tracket = true; + } + constructor_op += "{ "; + + if (type_is_empty(out_type) && !backend.supports_empty_struct) + constructor_op += "0"; + else if (splat) + constructor_op += to_unpacked_expression(elems[0]); + else + constructor_op += build_composite_combiner(result_type, elems, length); + constructor_op += " }"; + if (needs_trailing_tracket) + constructor_op += ")"; + } + else if (swizzle_splat && !composite) + { + constructor_op = remap_swizzle(get(result_type), 1, to_unpacked_expression(elems[0])); + } + else + { + constructor_op = type_to_glsl_constructor(get(result_type)) + "("; + if (type_is_empty(out_type) && !backend.supports_empty_struct) + constructor_op += "0"; + else if (splat) + constructor_op += to_unpacked_expression(elems[0]); + else + constructor_op += build_composite_combiner(result_type, elems, length); + constructor_op += ")"; + } + + if (!constructor_op.empty()) + { + emit_op(result_type, id, constructor_op, forward); + for (uint32_t i = 0; i < length; i++) + inherit_expression_dependencies(id, elems[i]); + } + break; + } + + case OpVectorInsertDynamic: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t vec = ops[2]; + uint32_t comp = ops[3]; + uint32_t index = ops[4]; + + flush_variable_declaration(vec); + + // Make a copy, then use access chain to store the variable. + statement(declare_temporary(result_type, id), to_expression(vec), ";"); + set(id, to_name(id), result_type, true); + auto chain = access_chain_internal(id, &index, 1, 0, nullptr, nullptr); + statement(chain, " = ", to_unpacked_expression(comp), ";"); + break; + } + + case OpVectorExtractDynamic: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto expr = access_chain_internal(ops[2], &ops[3], 1, 0, nullptr, nullptr); + emit_op(result_type, id, expr, should_forward(ops[2])); + inherit_expression_dependencies(id, ops[2]); + inherit_expression_dependencies(id, ops[3]); + break; + } + + case OpCompositeExtract: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + length -= 3; + + auto &type = get(result_type); + + // We can only split the expression here if our expression is forwarded as a temporary. + bool allow_base_expression = forced_temporaries.find(id) == end(forced_temporaries); + + // Do not allow base expression for struct members. We risk doing "swizzle" optimizations in this case. + auto &composite_type = expression_type(ops[2]); + bool composite_type_is_complex = composite_type.basetype == SPIRType::Struct || !composite_type.array.empty(); + if (composite_type_is_complex) + allow_base_expression = false; + + if (composite_type.op == OpTypeCooperativeMatrixKHR) + allow_base_expression = false; + + // Packed expressions or physical ID mapped expressions cannot be split up. + if (has_extended_decoration(ops[2], SPIRVCrossDecorationPhysicalTypePacked) || + has_extended_decoration(ops[2], SPIRVCrossDecorationPhysicalTypeID)) + allow_base_expression = false; + + // Cannot use base expression for row-major matrix row-extraction since we need to interleave access pattern + // into the base expression. + if (is_non_native_row_major_matrix(ops[2])) + allow_base_expression = false; + + AccessChainMeta meta; + SPIRExpression *e = nullptr; + auto *c = maybe_get(ops[2]); + + if (c && !c->specialization && !composite_type_is_complex) + { + auto expr = to_extract_constant_composite_expression(result_type, *c, ops + 3, length); + e = &emit_op(result_type, id, expr, true, true); + } + else if (allow_base_expression && should_forward(ops[2]) && type.vecsize == 1 && type.columns == 1 && length == 1) + { + // Only apply this optimization if result is scalar. + + // We want to split the access chain from the base. + // This is so we can later combine different CompositeExtract results + // with CompositeConstruct without emitting code like + // + // vec3 temp = texture(...).xyz + // vec4(temp.x, temp.y, temp.z, 1.0). + // + // when we actually wanted to emit this + // vec4(texture(...).xyz, 1.0). + // + // Including the base will prevent this and would trigger multiple reads + // from expression causing it to be forced to an actual temporary in GLSL. + auto expr = access_chain_internal(ops[2], &ops[3], length, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_CHAIN_ONLY_BIT | + ACCESS_CHAIN_FORCE_COMPOSITE_BIT, &meta, nullptr); + e = &emit_op(result_type, id, expr, true, should_suppress_usage_tracking(ops[2])); + inherit_expression_dependencies(id, ops[2]); + e->base_expression = ops[2]; + + if (meta.relaxed_precision && backend.requires_relaxed_precision_analysis) + set_decoration(ops[1], DecorationRelaxedPrecision); + } + else + { + auto expr = access_chain_internal(ops[2], &ops[3], length, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_FORCE_COMPOSITE_BIT, + &meta, nullptr); + e = &emit_op(result_type, id, expr, should_forward(ops[2]), should_suppress_usage_tracking(ops[2])); + inherit_expression_dependencies(id, ops[2]); + } + + // Pass through some meta information to the loaded expression. + // We can still end up loading a buffer type to a variable, then CompositeExtract from it + // instead of loading everything through an access chain. + e->need_transpose = meta.need_transpose; + if (meta.storage_is_packed) + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypePacked); + if (meta.storage_physical_type != 0) + set_extended_decoration(id, SPIRVCrossDecorationPhysicalTypeID, meta.storage_physical_type); + if (meta.storage_is_invariant) + set_decoration(id, DecorationInvariant); + + break; + } + + case OpCompositeInsert: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t obj = ops[2]; + uint32_t composite = ops[3]; + const auto *elems = &ops[4]; + length -= 4; + + flush_variable_declaration(composite); + + // CompositeInsert requires a copy + modification, but this is very awkward code in HLL. + // Speculate that the input composite is no longer used, and we can modify it in-place. + // There are various scenarios where this is not possible to satisfy. + bool can_modify_in_place = true; + forced_temporaries.insert(id); + + // Cannot safely RMW PHI variables since they have no way to be invalidated, + // forcing temporaries is not going to help. + // This is similar for Constant and Undef inputs. + // The only safe thing to RMW is SPIRExpression. + // If the expression has already been used (i.e. used in a continue block), we have to keep using + // that loop variable, since we won't be able to override the expression after the fact. + // If the composite is hoisted, we might never be able to properly invalidate any usage + // of that composite in a subsequent loop iteration. + if (invalid_expressions.count(composite) || + block_composite_insert_overwrite.count(composite) || + hoisted_temporaries.count(id) || hoisted_temporaries.count(composite) || + maybe_get(composite) == nullptr) + { + can_modify_in_place = false; + } + else if (backend.requires_relaxed_precision_analysis && + has_decoration(composite, DecorationRelaxedPrecision) != + has_decoration(id, DecorationRelaxedPrecision) && + get(result_type).basetype != SPIRType::Struct) + { + // Similarly, if precision does not match for input and output, + // we cannot alias them. If we write a composite into a relaxed precision + // ID, we might get a false truncation. + can_modify_in_place = false; + } + + if (can_modify_in_place) + { + // Have to make sure the modified SSA value is bound to a temporary so we can modify it in-place. + if (!forced_temporaries.count(composite)) + force_temporary_and_recompile(composite); + + auto chain = access_chain_internal(composite, elems, length, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, + nullptr, nullptr); + statement(chain, " = ", to_unpacked_expression(obj), ";"); + set(id, to_expression(composite), result_type, true); + invalid_expressions.insert(composite); + composite_insert_overwritten.insert(composite); + } + else + { + if (maybe_get(composite) != nullptr) + { + emit_uninitialized_temporary_expression(result_type, id); + } + else + { + // Make a copy, then use access chain to store the variable. + statement(declare_temporary(result_type, id), to_expression(composite), ";"); + set(id, to_name(id), result_type, true); + } + + auto chain = access_chain_internal(id, elems, length, ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, nullptr, nullptr); + statement(chain, " = ", to_unpacked_expression(obj), ";"); + } + + break; + } + + case OpCopyMemory: + { + uint32_t lhs = ops[0]; + uint32_t rhs = ops[1]; + if (lhs != rhs) + { + uint32_t &tmp_id = extra_sub_expressions[instruction.offset | EXTRA_SUB_EXPRESSION_TYPE_STREAM_OFFSET]; + if (!tmp_id) + tmp_id = ir.increase_bound_by(1); + uint32_t tmp_type_id = expression_type(rhs).parent_type; + + EmbeddedInstruction fake_load, fake_store; + fake_load.op = OpLoad; + fake_load.length = 3; + fake_load.ops.push_back(tmp_type_id); + fake_load.ops.push_back(tmp_id); + fake_load.ops.push_back(rhs); + + fake_store.op = OpStore; + fake_store.length = 2; + fake_store.ops.push_back(lhs); + fake_store.ops.push_back(tmp_id); + + // Load and Store do a *lot* of workarounds, and we'd like to reuse them as much as possible. + // Synthesize a fake Load and Store pair for CopyMemory. + emit_instruction(fake_load); + emit_instruction(fake_store); + } + break; + } + + case OpCopyLogical: + { + // This is used for copying object of different types, arrays and structs. + // We need to unroll the copy, element-by-element. + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t rhs = ops[2]; + + emit_uninitialized_temporary_expression(result_type, id); + emit_copy_logical_type(id, result_type, rhs, expression_type_id(rhs), {}); + break; + } + + case OpCopyObject: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t rhs = ops[2]; + bool pointer = get(result_type).pointer; + + auto *chain = maybe_get(rhs); + auto *imgsamp = maybe_get(rhs); + if (chain) + { + // Cannot lower to a SPIRExpression, just copy the object. + auto &e = set(id, *chain); + e.self = id; + } + else if (imgsamp) + { + // Cannot lower to a SPIRExpression, just copy the object. + // GLSL does not currently use this type and will never get here, but MSL does. + // Handled here instead of CompilerMSL for better integration and general handling, + // and in case GLSL or other subclasses require it in the future. + auto &e = set(id, *imgsamp); + e.self = id; + } + else if (expression_is_lvalue(rhs) && !pointer) + { + // Need a copy. + // For pointer types, we copy the pointer itself. + emit_op(result_type, id, to_unpacked_expression(rhs), false); + } + else + { + // RHS expression is immutable, so just forward it. + // Copying these things really make no sense, but + // seems to be allowed anyways. + auto &e = emit_op(result_type, id, to_expression(rhs), true, true); + if (pointer) + { + auto *var = maybe_get_backing_variable(rhs); + e.loaded_from = var ? var->self : ID(0); + } + + // If we're copying an access chain, need to inherit the read expressions. + auto *rhs_expr = maybe_get(rhs); + if (rhs_expr) + { + e.implied_read_expressions = rhs_expr->implied_read_expressions; + e.expression_dependencies = rhs_expr->expression_dependencies; + } + } + break; + } + + case OpVectorShuffle: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t vec0 = ops[2]; + uint32_t vec1 = ops[3]; + const auto *elems = &ops[4]; + length -= 4; + + auto &type0 = expression_type(vec0); + + // If we have the undefined swizzle index -1, we need to swizzle in undefined data, + // or in our case, T(0). + bool shuffle = false; + for (uint32_t i = 0; i < length; i++) + if (elems[i] >= type0.vecsize || elems[i] == 0xffffffffu) + shuffle = true; + + // Cannot use swizzles with packed expressions, force shuffle path. + if (!shuffle && has_extended_decoration(vec0, SPIRVCrossDecorationPhysicalTypePacked)) + shuffle = true; + + string expr; + bool should_fwd, trivial_forward; + + if (shuffle) + { + should_fwd = should_forward(vec0) && should_forward(vec1); + trivial_forward = should_suppress_usage_tracking(vec0) && should_suppress_usage_tracking(vec1); + + // Constructor style and shuffling from two different vectors. + SmallVector args; + for (uint32_t i = 0; i < length; i++) + { + if (elems[i] == 0xffffffffu) + { + // Use a constant 0 here. + // We could use the first component or similar, but then we risk propagating + // a value we might not need, and bog down codegen. + SPIRConstant c; + c.constant_type = type0.parent_type; + assert(type0.parent_type != ID(0)); + args.push_back(constant_expression(c)); + } + else if (elems[i] >= type0.vecsize) + args.push_back(to_extract_component_expression(vec1, elems[i] - type0.vecsize)); + else + args.push_back(to_extract_component_expression(vec0, elems[i])); + } + expr += join(type_to_glsl_constructor(get(result_type)), "(", merge(args), ")"); + } + else + { + should_fwd = should_forward(vec0); + trivial_forward = should_suppress_usage_tracking(vec0); + + // We only source from first vector, so can use swizzle. + // If the vector is packed, unpack it before applying a swizzle (needed for MSL) + expr += to_enclosed_unpacked_expression(vec0); + expr += "."; + for (uint32_t i = 0; i < length; i++) + { + assert(elems[i] != 0xffffffffu); + expr += index_to_swizzle(elems[i]); + } + + if (backend.swizzle_is_function && length > 1) + expr += "()"; + } + + // A shuffle is trivial in that it doesn't actually *do* anything. + // We inherit the forwardedness from our arguments to avoid flushing out to temporaries when it's not really needed. + + emit_op(result_type, id, expr, should_fwd, trivial_forward); + + inherit_expression_dependencies(id, vec0); + if (vec0 != vec1) + inherit_expression_dependencies(id, vec1); + break; + } + + // ALU + case OpIsNan: + if (!is_legacy()) + GLSL_UFOP(isnan); + else + { + // Check if the number doesn't equal itself + auto &type = get(ops[0]); + if (type.vecsize > 1) + emit_binary_func_op(ops[0], ops[1], ops[2], ops[2], "notEqual"); + else + emit_binary_op(ops[0], ops[1], ops[2], ops[2], "!="); + } + break; + + case OpIsInf: + if (!is_legacy()) + GLSL_UFOP(isinf); + else + { + // inf * 2 == inf by IEEE 754 rules, note this also applies to 0.0 + // This is more reliable than checking if product with zero is NaN + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t operand = ops[2]; + + auto &type = get(result_type); + std::string expr; + if (type.vecsize > 1) + { + expr = type_to_glsl_constructor(type); + expr += '('; + for (uint32_t i = 0; i < type.vecsize; i++) + { + auto comp = to_extract_component_expression(operand, i); + expr += join(comp, " != 0.0 && 2.0 * ", comp, " == ", comp); + + if (i + 1 < type.vecsize) + expr += ", "; + } + expr += ')'; + } + else + { + // Register an extra read to force writing out a temporary + auto oper = to_enclosed_expression(operand); + track_expression_read(operand); + expr += join(oper, " != 0.0 && 2.0 * ", oper, " == ", oper); + } + emit_op(result_type, result_id, expr, should_forward(operand)); + + inherit_expression_dependencies(result_id, operand); + } + break; + + case OpSNegate: + if (implicit_integer_promotion || expression_type_id(ops[2]) != ops[0]) + GLSL_UOP_CAST(-); + else + GLSL_UOP(-); + break; + + case OpFNegate: + 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; + GLSL_BOP_CAST(+, type); + break; + } + + case OpFAdd: + GLSL_BOP(+); + break; + + case OpISub: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(-, type); + break; + } + + case OpFSub: + GLSL_BOP(-); + break; + + case OpIMul: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(*, type); + break; + } + + case OpVectorTimesMatrix: + case OpMatrixTimesVector: + { + // If the matrix needs transpose, just flip the multiply order. + auto *e = maybe_get(ops[opcode == OpMatrixTimesVector ? 2 : 3]); + if (e && e->need_transpose) + { + e->need_transpose = false; + string expr; + + if (opcode == OpMatrixTimesVector) + expr = join(to_enclosed_unpacked_expression(ops[3]), " * ", + enclose_expression(to_unpacked_row_major_matrix_expression(ops[2]))); + else + expr = join(enclose_expression(to_unpacked_row_major_matrix_expression(ops[3])), " * ", + to_enclosed_unpacked_expression(ops[2])); + + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + emit_op(ops[0], ops[1], expr, forward); + e->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + GLSL_BOP(*); + break; + } + + case OpMatrixTimesMatrix: + { + auto *a = maybe_get(ops[2]); + auto *b = maybe_get(ops[3]); + + // If both matrices need transpose, we can multiply in flipped order and tag the expression as transposed. + // a^T * b^T = (b * a)^T. + if (a && b && a->need_transpose && b->need_transpose) + { + a->need_transpose = false; + b->need_transpose = false; + auto expr = join(enclose_expression(to_unpacked_row_major_matrix_expression(ops[3])), " * ", + enclose_expression(to_unpacked_row_major_matrix_expression(ops[2]))); + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + emit_transposed_op(ops[0], ops[1], expr, forward); + a->need_transpose = true; + b->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + GLSL_BOP(*); + + break; + } + + case OpMatrixTimesScalar: + { + auto *a = maybe_get(ops[2]); + + // If the matrix need transpose, just mark the result as needing so. + if (a && a->need_transpose) + { + a->need_transpose = false; + auto expr = join(enclose_expression(to_unpacked_row_major_matrix_expression(ops[2])), " * ", + to_enclosed_unpacked_expression(ops[3])); + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + emit_transposed_op(ops[0], ops[1], expr, forward); + a->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + GLSL_BOP(*); + break; + } + + case OpFMul: + case OpVectorTimesScalar: + GLSL_BOP(*); + break; + + case OpOuterProduct: + if (options.version < 120) // Matches GLSL 1.10 / ESSL 1.00 + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2]; + uint32_t b = ops[3]; + + auto &type = get(result_type); + string expr = type_to_glsl_constructor(type); + expr += "("; + for (uint32_t col = 0; col < type.columns; col++) + { + expr += to_enclosed_expression(a); + expr += " * "; + expr += to_extract_component_expression(b, col); + if (col + 1 < type.columns) + expr += ", "; + } + expr += ")"; + emit_op(result_type, id, expr, should_forward(a) && should_forward(b)); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + } + else + GLSL_BFOP(outerProduct); + break; + + case OpDot: + GLSL_BFOP(dot); + break; + + case OpTranspose: + if (options.version < 120) // Matches GLSL 1.10 / ESSL 1.00 + { + // transpose() is not available, so instead, flip need_transpose, + // which can later be turned into an emulated transpose op by + // convert_row_major_matrix(), if necessary. + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t input = ops[2]; + + // Force need_transpose to false temporarily to prevent + // to_expression() from doing the transpose. + bool need_transpose = false; + auto *input_e = maybe_get(input); + if (input_e) + swap(need_transpose, input_e->need_transpose); + + bool forward = should_forward(input); + auto &e = emit_op(result_type, result_id, to_expression(input), forward); + e.need_transpose = !need_transpose; + + // Restore the old need_transpose flag. + if (input_e) + input_e->need_transpose = need_transpose; + } + else + GLSL_UFOP(transpose); + break; + + case OpSRem: + { + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + + auto &out_type = get(result_type); + + bool forward = should_forward(op0) && should_forward(op1); + string cast_op0, cast_op1; + auto expected_type = binary_op_bitcast_helper(cast_op0, cast_op1, int_type, op0, op1, false); + + // Needs special handling. + auto expr = join(cast_op0, " - ", cast_op1, " * ", "(", cast_op0, " / ", cast_op1, ")"); + + if (implicit_integer_promotion) + { + expr = join(type_to_glsl(get(result_type)), '(', expr, ')'); + } + else if (out_type.basetype != int_type) + { + expected_type.basetype = int_type; + expr = join(bitcast_glsl_op(out_type, expected_type), '(', expr, ')'); + } + + emit_op(result_type, result_id, expr, forward); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + break; + } + + case OpSDiv: + GLSL_BOP_CAST(/, int_type); + break; + + case OpUDiv: + GLSL_BOP_CAST(/, uint_type); + break; + + case OpIAddCarry: + case OpISubBorrow: + { + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Extended arithmetic is only available from ESSL 310."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Extended arithmetic is only available from GLSL 400."); + + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, result_id); + const char *op = opcode == OpIAddCarry ? "uaddCarry" : "usubBorrow"; + + statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", op, "(", to_expression(op0), ", ", + to_expression(op1), ", ", to_expression(result_id), ".", to_member_name(type, 1), ");"); + break; + } + + case OpUMulExtended: + case OpSMulExtended: + { + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Extended arithmetic is only available from ESSL 310."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Extended arithmetic is only available from GLSL 4000."); + + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, result_id); + const char *op = opcode == OpUMulExtended ? "umulExtended" : "imulExtended"; + + statement(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(result_id), ".", + to_member_name(type, 1), ", ", to_expression(result_id), ".", to_member_name(type, 0), ");"); + break; + } + + case OpFDiv: + GLSL_BOP(/); + break; + + case OpShiftRightLogical: + GLSL_BOP_CAST(>>, uint_type); + break; + + case OpShiftRightArithmetic: + GLSL_BOP_CAST(>>, int_type); + break; + + case OpShiftLeftLogical: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(<<, type); + break; + } + + case OpBitwiseOr: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(|, type); + break; + } + + case OpBitwiseXor: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(^, type); + break; + } + + case OpBitwiseAnd: + { + auto type = get(ops[0]).basetype; + GLSL_BOP_CAST(&, type); + break; + } + + case OpNot: + if (implicit_integer_promotion || expression_type_id(ops[2]) != ops[0]) + GLSL_UOP_CAST(~); + else + GLSL_UOP(~); + break; + + case OpUMod: + GLSL_BOP_CAST(%, uint_type); + break; + + case OpSMod: + GLSL_BOP_CAST(%, int_type); + break; + + case OpFMod: + GLSL_BFOP(mod); + break; + + case OpFRem: + { + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + + // Needs special handling. + bool forward = should_forward(op0) && should_forward(op1); + std::string expr; + if (!is_legacy()) + { + expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", "trunc(", + to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), ")"); + } + else + { + // Legacy GLSL has no trunc, emulate by casting to int and back + auto &op0_type = expression_type(op0); + auto via_type = op0_type; + via_type.basetype = SPIRType::Int; + expr = join(to_enclosed_expression(op0), " - ", to_enclosed_expression(op1), " * ", + type_to_glsl(op0_type), "(", type_to_glsl(via_type), "(", + to_enclosed_expression(op0), " / ", to_enclosed_expression(op1), "))"); + } + + emit_op(result_type, result_id, expr, forward); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); + break; + } + + // Relational + case OpAny: + GLSL_UFOP(any); + break; + + case OpAll: + GLSL_UFOP(all); + break; + + case OpSelect: + emit_mix_op(ops[0], ops[1], ops[4], ops[3], ops[2]); + break; + + case OpLogicalOr: + { + // No vector variant in GLSL for logical OR. + auto result_type = ops[0]; + auto id = ops[1]; + auto &type = get(result_type); + + if (type.vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "||", false, SPIRType::Unknown); + else + GLSL_BOP(||); + break; + } + + case OpLogicalAnd: + { + // No vector variant in GLSL for logical AND. + auto result_type = ops[0]; + auto id = ops[1]; + auto &type = get(result_type); + + if (type.vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "&&", false, SPIRType::Unknown); + else + GLSL_BOP(&&); + break; + } + + case OpLogicalNot: + { + auto &type = get(ops[0]); + if (type.vecsize > 1) + GLSL_UFOP(not ); + else + GLSL_UOP(!); + break; + } + + case OpIEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(equal, int_type); + else + GLSL_BOP_CAST(==, int_type); + break; + } + + case OpLogicalEqual: + case OpFOrdEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(equal); + else + GLSL_BOP(==); + break; + } + + case OpINotEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(notEqual, int_type); + else + GLSL_BOP_CAST(!=, int_type); + break; + } + + case OpLogicalNotEqual: + case OpFOrdNotEqual: + case OpFUnordNotEqual: + { + // GLSL is fuzzy on what to do with ordered vs unordered not equal. + // glslang started emitting UnorderedNotEqual some time ago to harmonize with IEEE, + // but this means we have no easy way of implementing ordered not equal. + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(notEqual); + else + GLSL_BOP(!=); + break; + } + + case OpUGreaterThan: + case OpSGreaterThan: + { + auto type = opcode == OpUGreaterThan ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(greaterThan, type); + else + GLSL_BOP_CAST(>, type); + break; + } + + case OpFOrdGreaterThan: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(greaterThan); + else + GLSL_BOP(>); + break; + } + + case OpUGreaterThanEqual: + case OpSGreaterThanEqual: + { + auto type = opcode == OpUGreaterThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(greaterThanEqual, type); + else + GLSL_BOP_CAST(>=, type); + break; + } + + case OpFOrdGreaterThanEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(greaterThanEqual); + else + GLSL_BOP(>=); + break; + } + + case OpULessThan: + case OpSLessThan: + { + auto type = opcode == OpULessThan ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(lessThan, type); + else + GLSL_BOP_CAST(<, type); + break; + } + + case OpFOrdLessThan: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(lessThan); + else + GLSL_BOP(<); + break; + } + + case OpULessThanEqual: + case OpSLessThanEqual: + { + auto type = opcode == OpULessThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP_CAST(lessThanEqual, type); + else + GLSL_BOP_CAST(<=, type); + break; + } + + case OpFOrdLessThanEqual: + { + if (expression_type(ops[2]).vecsize > 1) + GLSL_BFOP(lessThanEqual); + else + GLSL_BOP(<=); + break; + } + + // Conversion + case OpSConvert: + case OpConvertSToF: + case OpUConvert: + case OpConvertUToF: + { + auto input_type = opcode == OpSConvert || opcode == OpConvertSToF ? int_type : uint_type; + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto &type = get(result_type); + auto &arg_type = expression_type(ops[2]); + auto func = type_to_glsl_constructor(type); + + if (arg_type.width < type.width || type_is_floating_point(type)) + emit_unary_func_op_cast(result_type, id, ops[2], func.c_str(), input_type, type.basetype); + else + emit_unary_func_op(result_type, id, ops[2], func.c_str()); + break; + } + + case OpConvertFToU: + case OpConvertFToS: + { + // Cast to expected arithmetic type, then potentially bitcast away to desired signedness. + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto &type = get(result_type); + auto expected_type = type; + auto &float_type = expression_type(ops[2]); + expected_type.basetype = + opcode == OpConvertFToS ? to_signed_basetype(type.width) : to_unsigned_basetype(type.width); + + auto func = type_to_glsl_constructor(expected_type); + emit_unary_func_op_cast(result_type, id, ops[2], func.c_str(), float_type.basetype, expected_type.basetype); + break; + } + + case OpCooperativeMatrixConvertNV: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("CooperativeMatrixConvertNV requires vulkan semantics."); + require_extension_internal("GL_NV_cooperative_matrix2"); + // fallthrough + case OpFConvert: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto &type = get(result_type); + + if (type.op == OpTypeCooperativeMatrixKHR && opcode == OpFConvert) + { + auto &expr_type = expression_type(ops[2]); + if (get(type.ext.cooperative.use_id).scalar() != + get(expr_type.ext.cooperative.use_id).scalar()) + { + // Somewhat questionable with spec constant uses. + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("NV_cooperative_matrix2 requires vulkan semantics."); + require_extension_internal("GL_NV_cooperative_matrix2"); + } + } + + if ((type.basetype == SPIRType::FloatE4M3 || type.basetype == SPIRType::FloatE5M2) && + has_decoration(id, DecorationSaturatedToLargestFloat8NormalConversionEXT)) + { + emit_uninitialized_temporary_expression(result_type, id); + statement("saturatedConvertEXT(", to_expression(id), ", ", to_unpacked_expression(ops[2]), ");"); + } + else + { + auto func = type_to_glsl_constructor(type); + emit_unary_func_op(result_type, id, ops[2], func.c_str()); + } + break; + } + + case OpBitcast: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t arg = ops[2]; + + if (!emit_complex_bitcast(result_type, id, arg)) + { + auto op = bitcast_glsl_op(get(result_type), expression_type(arg)); + emit_unary_func_op(result_type, id, arg, op.c_str()); + } + break; + } + + case OpQuantizeToF16: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t arg = ops[2]; + + string op; + auto &type = get(result_type); + + switch (type.vecsize) + { + case 1: + op = join("unpackHalf2x16(packHalf2x16(vec2(", to_expression(arg), "))).x"); + break; + case 2: + op = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), "))"); + break; + case 3: + { + auto op0 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".xy))"); + auto op1 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".zz)).x"); + op = join("vec3(", op0, ", ", op1, ")"); + break; + } + case 4: + { + auto op0 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".xy))"); + auto op1 = join("unpackHalf2x16(packHalf2x16(", to_expression(arg), ".zw))"); + op = join("vec4(", op0, ", ", op1, ")"); + break; + } + default: + SPIRV_CROSS_THROW("Illegal argument to OpQuantizeToF16."); + } + + emit_op(result_type, id, op, should_forward(arg)); + inherit_expression_dependencies(id, arg); + break; + } + + // Derivatives + case OpDPdx: + GLSL_UFOP(dFdx); + if (is_legacy_es()) + require_extension_internal("GL_OES_standard_derivatives"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdy: + GLSL_UFOP(dFdy); + if (is_legacy_es()) + require_extension_internal("GL_OES_standard_derivatives"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxFine: + GLSL_UFOP(dFdxFine); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyFine: + GLSL_UFOP(dFdyFine); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxCoarse: + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + GLSL_UFOP(dFdxCoarse); + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyCoarse: + GLSL_UFOP(dFdyCoarse); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidth: + GLSL_UFOP(fwidth); + if (is_legacy_es()) + require_extension_internal("GL_OES_standard_derivatives"); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidthCoarse: + GLSL_UFOP(fwidthCoarse); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidthFine: + GLSL_UFOP(fwidthFine); + if (options.es) + { + SPIRV_CROSS_THROW("GL_ARB_derivative_control is unavailable in OpenGL ES."); + } + if (options.version < 450) + require_extension_internal("GL_ARB_derivative_control"); + register_control_dependent_expression(ops[1]); + break; + + // Bitfield + case OpBitFieldInsert: + { + emit_bitfield_insert_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], "bitfieldInsert", SPIRType::Int); + break; + } + + case OpBitFieldSExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "bitfieldExtract", int_type, int_type, + SPIRType::Int, SPIRType::Int); + break; + } + + case OpBitFieldUExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "bitfieldExtract", uint_type, uint_type, + SPIRType::Int, SPIRType::Int); + break; + } + + case OpBitReverse: + // BitReverse does not have issues with sign since result type must match input type. + GLSL_UFOP(bitfieldReverse); + break; + + case OpBitCount: + { + auto basetype = expression_type(ops[2]).basetype; + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "bitCount", basetype, int_type); + break; + } + + // Atomics + case OpAtomicExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + // Ignore semantics for now, probably only relevant to CL. + uint32_t val = ops[5]; + const char *op = check_atomic_image(ptr) ? "imageAtomicExchange" : "atomicExchange"; + + emit_atomic_func_op(result_type, id, ptr, val, op); + break; + } + + case OpAtomicCompareExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t val = ops[6]; + uint32_t comp = ops[7]; + const char *op = check_atomic_image(ptr) ? "imageAtomicCompSwap" : "atomicCompSwap"; + + emit_atomic_func_op(result_type, id, ptr, comp, val, op); + break; + } + + case OpAtomicLoad: + { + // In plain GLSL, we have no atomic loads, so emulate this by fetch adding by 0 and hope compiler figures it out. + // Alternatively, we could rely on KHR_memory_model, but that's not very helpful for GL. + auto &type = expression_type(ops[2]); + forced_temporaries.insert(ops[1]); + bool atomic_image = check_atomic_image(ops[2]); + bool unsigned_type = (type.basetype == SPIRType::UInt) || + (atomic_image && get(type.image.type).basetype == SPIRType::UInt); + const char *op = atomic_image ? "imageAtomicAdd" : "atomicAdd"; + const char *increment = unsigned_type ? "0u" : "0"; + emit_op(ops[0], ops[1], + join(op, "(", + to_atomic_ptr_expression(ops[2]), ", ", increment, ")"), false); + flush_all_atomic_capable_variables(); + + if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64) + require_extension_internal("GL_EXT_shader_atomic_int64"); + break; + } + + case OpAtomicStore: + { + // In plain GLSL, we have no atomic stores, so emulate this with an atomic exchange where we don't consume the result. + // Alternatively, we could rely on KHR_memory_model, but that's not very helpful for GL. + uint32_t ptr = ops[0]; + // Ignore semantics for now, probably only relevant to CL. + uint32_t val = ops[3]; + const char *op = check_atomic_image(ptr) ? "imageAtomicExchange" : "atomicExchange"; + statement(op, "(", to_atomic_ptr_expression(ptr), ", ", to_expression(val), ");"); + flush_all_atomic_capable_variables(); + + auto &type = expression_type(ptr); + if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64) + require_extension_internal("GL_EXT_shader_atomic_int64"); + break; + } + + case OpAtomicIIncrement: + case OpAtomicIDecrement: + { + forced_temporaries.insert(ops[1]); + auto &type = expression_type(ops[2]); + if (type.storage == StorageClassAtomicCounter) + { + // Legacy GLSL stuff, not sure if this is relevant to support. + if (opcode == OpAtomicIIncrement) + GLSL_UFOP(atomicCounterIncrement); + else + GLSL_UFOP(atomicCounterDecrement); + } + else + { + bool atomic_image = check_atomic_image(ops[2]); + bool unsigned_type = (type.basetype == SPIRType::UInt) || + (atomic_image && get(type.image.type).basetype == SPIRType::UInt); + const char *op = atomic_image ? "imageAtomicAdd" : "atomicAdd"; + + const char *increment = nullptr; + if (opcode == OpAtomicIIncrement && unsigned_type) + increment = "1u"; + else if (opcode == OpAtomicIIncrement) + increment = "1"; + else if (unsigned_type) + increment = "uint(-1)"; + else + increment = "-1"; + + emit_op(ops[0], ops[1], + join(op, "(", to_atomic_ptr_expression(ops[2]), ", ", increment, ")"), false); + + if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64) + require_extension_internal("GL_EXT_shader_atomic_int64"); + } + + flush_all_atomic_capable_variables(); + break; + } + + case OpAtomicIAdd: + case OpAtomicFAddEXT: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd"; + emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op); + break; + } + + case OpAtomicISub: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicAdd" : "atomicAdd"; + forced_temporaries.insert(ops[1]); + auto expr = join(op, "(", to_atomic_ptr_expression(ops[2]), ", -", to_enclosed_expression(ops[5]), ")"); + emit_op(ops[0], ops[1], expr, should_forward(ops[2]) && should_forward(ops[5])); + flush_all_atomic_capable_variables(); + + auto &type = get(ops[0]); + if (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64) + require_extension_internal("GL_EXT_shader_atomic_int64"); + break; + } + + case OpAtomicSMin: + case OpAtomicUMin: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicMin" : "atomicMin"; + emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op); + break; + } + + case OpAtomicSMax: + case OpAtomicUMax: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicMax" : "atomicMax"; + emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op); + break; + } + + case OpAtomicAnd: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicAnd" : "atomicAnd"; + emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op); + break; + } + + case OpAtomicOr: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicOr" : "atomicOr"; + emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op); + break; + } + + case OpAtomicXor: + { + const char *op = check_atomic_image(ops[2]) ? "imageAtomicXor" : "atomicXor"; + emit_atomic_func_op(ops[0], ops[1], ops[2], ops[5], op); + break; + } + + // Geometry shaders + case OpEmitVertex: + statement("EmitVertex();"); + break; + + case OpEndPrimitive: + statement("EndPrimitive();"); + break; + + case OpEmitStreamVertex: + { + if (options.es) + SPIRV_CROSS_THROW("Multi-stream geometry shaders not supported in ES."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Multi-stream geometry shaders only supported in GLSL 400."); + + auto stream_expr = to_expression(ops[0]); + if (expression_type(ops[0]).basetype != SPIRType::Int) + stream_expr = join("int(", stream_expr, ")"); + statement("EmitStreamVertex(", stream_expr, ");"); + break; + } + + case OpEndStreamPrimitive: + { + if (options.es) + SPIRV_CROSS_THROW("Multi-stream geometry shaders not supported in ES."); + else if (!options.es && options.version < 400) + SPIRV_CROSS_THROW("Multi-stream geometry shaders only supported in GLSL 400."); + + auto stream_expr = to_expression(ops[0]); + if (expression_type(ops[0]).basetype != SPIRType::Int) + stream_expr = join("int(", stream_expr, ")"); + statement("EndStreamPrimitive(", stream_expr, ");"); + break; + } + + // Textures + case OpImageSampleExplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageFetch: + case OpImageGather: + case OpImageDrefGather: + // Gets a bit hairy, so move this to a separate instruction. + emit_texture_op(instruction, false); + break; + + case OpImageSparseSampleExplicitLod: + case OpImageSparseSampleProjExplicitLod: + case OpImageSparseSampleDrefExplicitLod: + case OpImageSparseSampleProjDrefExplicitLod: + case OpImageSparseSampleImplicitLod: + case OpImageSparseSampleProjImplicitLod: + case OpImageSparseSampleDrefImplicitLod: + case OpImageSparseSampleProjDrefImplicitLod: + case OpImageSparseFetch: + case OpImageSparseGather: + case OpImageSparseDrefGather: + // Gets a bit hairy, so move this to a separate instruction. + emit_texture_op(instruction, true); + break; + + case OpImageSparseTexelsResident: + if (options.es) + SPIRV_CROSS_THROW("Sparse feedback is not supported in GLSL."); + require_extension_internal("GL_ARB_sparse_texture2"); + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "sparseTexelsResidentARB", int_type, SPIRType::Boolean); + break; + + case OpImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + // Suppress usage tracking. + auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true); + + // When using the image, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + e.loaded_from = var ? var->self : ID(0); + break; + } + + case OpImageQueryLod: + { + const char *op = nullptr; + if (!options.es && options.version < 400) + { + require_extension_internal("GL_ARB_texture_query_lod"); + // For some reason, the ARB spec is all-caps. + op = "textureQueryLOD"; + } + else if (options.es) + { + if (options.version < 300) + SPIRV_CROSS_THROW("textureQueryLod not supported in legacy ES"); + require_extension_internal("GL_EXT_texture_query_lod"); + op = "textureQueryLOD"; + } + else + op = "textureQueryLod"; + + auto sampler_expr = to_expression(ops[2]); + if (is_descriptor_non_uniform(ops[2])) + { + if (maybe_get_backing_variable(ops[2])) + convert_non_uniform_expression(sampler_expr, ops[2]); + else if (*backend.nonuniform_qualifier != '\0') + sampler_expr = join(backend.nonuniform_qualifier, "(", sampler_expr, ")"); + } + + bool forward = should_forward(ops[3]); + emit_op(ops[0], ops[1], + join(op, "(", sampler_expr, ", ", to_unpacked_expression(ops[3]), ")"), + forward); + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + register_control_dependent_expression(ops[1]); + break; + } + + case OpImageQueryLevels: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_texture_query_levels"); + if (options.es) + SPIRV_CROSS_THROW("textureQueryLevels not supported in ES profile."); + + auto expr = join("textureQueryLevels(", convert_separate_image_to_expression(ops[2]), ")"); + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpImageQuerySamples: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (options.es) + SPIRV_CROSS_THROW("textureSamples and imageSamples not supported in ES profile."); + else if (options.version < 450) + require_extension_internal("GL_ARB_texture_query_samples"); + + string expr; + if (type.image.sampled == 2) + expr = join("imageSamples(", to_non_uniform_aware_expression(ops[2]), ")"); + else + expr = join("textureSamples(", convert_separate_image_to_expression(ops[2]), ")"); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpSampledImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_sampled_image_op(result_type, id, ops[2], ops[3]); + inherit_expression_dependencies(id, ops[2]); + inherit_expression_dependencies(id, ops[3]); + break; + } + + case OpImageQuerySizeLod: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t img = ops[2]; + auto &type = expression_type(img); + auto &imgtype = get(type.self); + + std::string fname = "textureSize"; + if (is_legacy_desktop()) + { + fname = legacy_tex_op(fname, imgtype, img); + } + else if (is_legacy_es()) + SPIRV_CROSS_THROW("textureSize is not supported in ESSL 100."); + + auto expr = join(fname, "(", convert_separate_image_to_expression(img), ", ", + bitcast_expression(SPIRType::Int, ops[3]), ")"); + + // ES needs to emulate 1D images as 2D. + if (type.image.dim == Dim1D && options.es) + expr = join(expr, ".x"); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + break; + } + + // Image load/store + case OpImageRead: + case OpImageSparseRead: + { + // We added Nonreadable speculatively to the OpImage variable due to glslangValidator + // not adding the proper qualifiers. + // If it turns out we need to read the image after all, remove the qualifier and recompile. + auto *var = maybe_get_backing_variable(ops[2]); + if (var) + { + auto &flags = get_decoration_bitset(var->self); + if (flags.get(DecorationNonReadable)) + { + unset_decoration(var->self, DecorationNonReadable); + force_recompile(); + } + } + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + bool pure; + string imgexpr; + auto &type = expression_type(ops[2]); + + if (var && var->remapped_variable) // Remapped input, just read as-is without any op-code + { + if (type.image.ms) + SPIRV_CROSS_THROW("Trying to remap multisampled image to variable, this is not possible."); + + auto itr = + find_if(begin(pls_inputs), end(pls_inputs), [var](const PlsRemap &pls) { return pls.id == var->self; }); + + if (itr == end(pls_inputs)) + { + // For non-PLS inputs, we rely on subpass type remapping information to get it right + // since ImageRead always returns 4-component vectors and the backing type is opaque. + if (!var->remapped_components) + SPIRV_CROSS_THROW("subpassInput was remapped, but remap_components is not set correctly."); + imgexpr = remap_swizzle(get(result_type), var->remapped_components, to_expression(ops[2])); + } + else + { + // PLS input could have different number of components than what the SPIR expects, swizzle to + // the appropriate vector size. + uint32_t components = pls_format_to_components(itr->format); + imgexpr = remap_swizzle(get(result_type), components, to_expression(ops[2])); + } + pure = true; + } + else if (type.image.dim == DimSubpassData) + { + if (var && subpass_input_is_framebuffer_fetch(var->self)) + { + imgexpr = to_expression(var->self); + } + else if (options.vulkan_semantics) + { + // With Vulkan semantics, use the proper Vulkan GLSL construct. + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = join("subpassLoad(", to_non_uniform_aware_expression(ops[2]), ", ", to_expression(samples), ")"); + } + else + imgexpr = join("subpassLoad(", to_non_uniform_aware_expression(ops[2]), ")"); + } + else + { + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = join("texelFetch(", to_non_uniform_aware_expression(ops[2]), ", ivec2(gl_FragCoord.xy), ", + to_expression(samples), ")"); + } + else + { + // Implement subpass loads via texture barrier style sampling. + imgexpr = join("texelFetch(", to_non_uniform_aware_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)"); + } + } + imgexpr = remap_swizzle(get(result_type), 4, imgexpr); + pure = true; + } + else + { + bool sparse = opcode == OpImageSparseRead; + uint32_t sparse_code_id = 0; + uint32_t sparse_texel_id = 0; + if (sparse) + emit_sparse_feedback_temporaries(ops[0], ops[1], sparse_code_id, sparse_texel_id); + + // imageLoad only accepts int coords, not uint. + auto coord_expr = to_expression(ops[3]); + auto target_coord_type = expression_type(ops[3]); + target_coord_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(target_coord_type, expression_type(ops[3]).basetype, coord_expr); + + // ES needs to emulate 1D images as 2D. + if (type.image.dim == Dim1D && options.es) + coord_expr = join("ivec2(", coord_expr, ", 0)"); + + // Plain image load/store. + if (sparse) + { + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_non_uniform_aware_expression(ops[2]), ", ", + coord_expr, ", ", to_expression(samples), ", ", to_expression(sparse_texel_id), ");"); + } + else + { + statement(to_expression(sparse_code_id), " = sparseImageLoadARB(", to_non_uniform_aware_expression(ops[2]), ", ", + coord_expr, ", ", to_expression(sparse_texel_id), ");"); + } + imgexpr = join(type_to_glsl(get(result_type)), "(", to_expression(sparse_code_id), ", ", + to_expression(sparse_texel_id), ")"); + } + else + { + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected " + "operand mask was used."); + + uint32_t samples = ops[5]; + imgexpr = + join("imageLoad(", to_non_uniform_aware_expression(ops[2]), ", ", coord_expr, ", ", to_expression(samples), ")"); + } + else + imgexpr = join("imageLoad(", to_non_uniform_aware_expression(ops[2]), ", ", coord_expr, ")"); + } + + if (!sparse) + imgexpr = remap_swizzle(get(result_type), 4, imgexpr); + pure = false; + } + + if (var) + { + bool forward = forced_temporaries.find(id) == end(forced_temporaries); + auto &e = emit_op(result_type, id, imgexpr, forward); + + // We only need to track dependencies if we're reading from image load/store. + if (!pure) + { + e.loaded_from = var->self; + if (forward) + var->dependees.push_back(id); + } + } + else + emit_op(result_type, id, imgexpr, false); + + inherit_expression_dependencies(id, ops[2]); + if (type.image.ms) + inherit_expression_dependencies(id, ops[5]); + break; + } + + case OpUntypedImageTexelPointerEXT: + case OpImageTexelPointer: + { + bool untyped = opcode == OpUntypedImageTexelPointerEXT; + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + uint32_t image_id = ops[untyped ? 3 : 2]; + uint32_t coord_id = ops[untyped ? 4 : 3]; + + auto coord_expr = to_expression(coord_id); + auto target_coord_type = expression_type(coord_id); + target_coord_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(target_coord_type, expression_type(coord_id).basetype, coord_expr); + + auto expr = join(to_expression(image_id), ", ", coord_expr); + auto &e = set(id, expr, result_type, true); + + // When using the pointer, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(image_id); + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, coord_id); + break; + } + + case OpImageWrite: + { + // We added Nonwritable speculatively to the OpImage variable due to glslangValidator + // not adding the proper qualifiers. + // If it turns out we need to write to the image after all, remove the qualifier and recompile. + auto *var = maybe_get_backing_variable(ops[0]); + if (var) + { + if (has_decoration(var->self, DecorationNonWritable)) + { + unset_decoration(var->self, DecorationNonWritable); + force_recompile(); + } + } + + auto &type = expression_type(ops[0]); + auto &value_type = expression_type(ops[2]); + auto store_type = value_type; + store_type.vecsize = 4; + + // imageStore only accepts int coords, not uint. + auto coord_expr = to_expression(ops[1]); + auto target_coord_type = expression_type(ops[1]); + target_coord_type.basetype = SPIRType::Int; + coord_expr = bitcast_expression(target_coord_type, expression_type(ops[1]).basetype, coord_expr); + + // ES needs to emulate 1D images as 2D. + if (type.image.dim == Dim1D && options.es) + coord_expr = join("ivec2(", coord_expr, ", 0)"); + + if (type.image.ms) + { + uint32_t operands = ops[3]; + if (operands != ImageOperandsSampleMask || length != 5) + SPIRV_CROSS_THROW("Multisampled image used in OpImageWrite, but unexpected operand mask was used."); + uint32_t samples = ops[4]; + statement("imageStore(", to_non_uniform_aware_expression(ops[0]), ", ", coord_expr, ", ", to_expression(samples), ", ", + remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");"); + } + else + statement("imageStore(", to_non_uniform_aware_expression(ops[0]), ", ", coord_expr, ", ", + remap_swizzle(store_type, value_type.vecsize, to_expression(ops[2])), ");"); + + if (var && variable_storage_is_aliased(*var)) + flush_all_aliased_variables(); + break; + } + + case OpImageQuerySize: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (type.basetype == SPIRType::Image) + { + string expr; + if (type.image.sampled == 2) + { + if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_shader_image_size"); + else if (options.es && options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for imageSize."); + + // The size of an image is always constant. + expr = join("imageSize(", to_non_uniform_aware_expression(ops[2]), ")"); + } + else + { + // This path is hit for samplerBuffers and multisampled images which do not have LOD. + std::string fname = "textureSize"; + if (is_legacy()) + { + auto &imgtype = get(type.self); + fname = legacy_tex_op(fname, imgtype, ops[2]); + } + expr = join(fname, "(", convert_separate_image_to_expression(ops[2]), ")"); + } + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::Int, expr); + emit_op(result_type, id, expr, true); + } + else + SPIRV_CROSS_THROW("Invalid type for OpImageQuerySize."); + break; + } + + case OpImageSampleWeightedQCOM: + case OpImageBoxFilterQCOM: + case OpImageBlockMatchSSDQCOM: + case OpImageBlockMatchSADQCOM: + { + require_extension_internal("GL_QCOM_image_processing"); + uint32_t result_type_id = ops[0]; + uint32_t id = ops[1]; + string expr; + switch (opcode) + { + case OpImageSampleWeightedQCOM: + expr = "textureWeightedQCOM"; + break; + case OpImageBoxFilterQCOM: + expr = "textureBoxFilterQCOM"; + break; + case OpImageBlockMatchSSDQCOM: + expr = "textureBlockMatchSSDQCOM"; + break; + case OpImageBlockMatchSADQCOM: + expr = "textureBlockMatchSADQCOM"; + break; + default: + SPIRV_CROSS_THROW("Invalid opcode for QCOM_image_processing."); + } + expr += "("; + + bool forward = false; + expr += to_expression(ops[2]); + expr += ", " + to_expression(ops[3]); + + switch (opcode) + { + case OpImageSampleWeightedQCOM: + expr += ", " + to_non_uniform_aware_expression(ops[4]); + break; + case OpImageBoxFilterQCOM: + expr += ", " + to_expression(ops[4]); + break; + case OpImageBlockMatchSSDQCOM: + case OpImageBlockMatchSADQCOM: + expr += ", " + to_non_uniform_aware_expression(ops[4]); + expr += ", " + to_expression(ops[5]); + expr += ", " + to_expression(ops[6]); + break; + default: + SPIRV_CROSS_THROW("Invalid opcode for QCOM_image_processing."); + } + + expr += ")"; + emit_op(result_type_id, id, expr, forward); + + inherit_expression_dependencies(id, ops[3]); + if (opcode == OpImageBlockMatchSSDQCOM || opcode == OpImageBlockMatchSADQCOM) + inherit_expression_dependencies(id, ops[5]); + + break; + } + + case OpImageBlockMatchWindowSSDQCOM: + case OpImageBlockMatchWindowSADQCOM: + case OpImageBlockMatchGatherSSDQCOM: + case OpImageBlockMatchGatherSADQCOM: + { + require_extension_internal("GL_QCOM_image_processing2"); + uint32_t result_type_id = ops[0]; + uint32_t id = ops[1]; + string expr; + switch (opcode) + { + case OpImageBlockMatchWindowSSDQCOM: + expr = "textureBlockMatchWindowSSDQCOM"; + break; + case OpImageBlockMatchWindowSADQCOM: + expr = "textureBlockMatchWindowSADQCOM"; + break; + case OpImageBlockMatchGatherSSDQCOM: + expr = "textureBlockMatchGatherSSDQCOM"; + break; + case OpImageBlockMatchGatherSADQCOM: + expr = "textureBlockMatchGatherSADQCOM"; + break; + default: + SPIRV_CROSS_THROW("Invalid opcode for QCOM_image_processing2."); + } + expr += "("; + + bool forward = false; + expr += to_expression(ops[2]); + expr += ", " + to_expression(ops[3]); + + expr += ", " + to_non_uniform_aware_expression(ops[4]); + expr += ", " + to_expression(ops[5]); + expr += ", " + to_expression(ops[6]); + + expr += ")"; + emit_op(result_type_id, id, expr, forward); + + inherit_expression_dependencies(id, ops[3]); + inherit_expression_dependencies(id, ops[5]); + break; + } + + // Compute + case OpControlBarrier: + case OpMemoryBarrier: + { + uint32_t execution_scope = 0; + uint32_t memory; + uint32_t semantics; + + if (opcode == OpMemoryBarrier) + { + memory = evaluate_constant_u32(ops[0]); + semantics = evaluate_constant_u32(ops[1]); + } + else + { + execution_scope = evaluate_constant_u32(ops[0]); + memory = evaluate_constant_u32(ops[1]); + semantics = evaluate_constant_u32(ops[2]); + } + + if (execution_scope == ScopeSubgroup || memory == ScopeSubgroup) + { + // OpControlBarrier with ScopeSubgroup is subgroupBarrier() + if (opcode != OpControlBarrier) + { + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupMemBarrier); + } + else + { + request_subgroup_feature(ShaderSubgroupSupportHelper::SubgroupBarrier); + } + } + + if (execution_scope != ScopeSubgroup && get_entry_point().model == ExecutionModelTessellationControl) + { + // Control shaders only have barriers, and it implies memory barriers. + if (opcode == OpControlBarrier) + statement("barrier();"); + break; + } + + // We only care about these flags, acquire/release and friends are not relevant to GLSL. + semantics = mask_relevant_memory_semantics(semantics); + + if (opcode == OpMemoryBarrier) + { + // If we are a memory barrier, and the next instruction is a control barrier, check if that memory barrier + // does what we need, so we avoid redundant barriers. + const Instruction *next = get_next_instruction_in_block(instruction); + if (next && next->op == OpControlBarrier) + { + auto *next_ops = stream(*next); + uint32_t next_memory = evaluate_constant_u32(next_ops[1]); + uint32_t next_semantics = evaluate_constant_u32(next_ops[2]); + next_semantics = mask_relevant_memory_semantics(next_semantics); + + bool memory_scope_covered = false; + if (next_memory == memory) + memory_scope_covered = true; + else if (next_semantics == MemorySemanticsWorkgroupMemoryMask) + { + // If we only care about workgroup memory, either Device or Workgroup scope is fine, + // scope does not have to match. + if ((next_memory == ScopeDevice || next_memory == ScopeWorkgroup) && + (memory == ScopeDevice || memory == ScopeWorkgroup)) + { + memory_scope_covered = true; + } + } + else if (memory == ScopeWorkgroup && next_memory == ScopeDevice) + { + // The control barrier has device scope, but the memory barrier just has workgroup scope. + memory_scope_covered = true; + } + + // If we have the same memory scope, and all memory types are covered, we're good. + if (memory_scope_covered && (semantics & next_semantics) == semantics) + break; + } + } + + // We are synchronizing some memory or syncing execution, + // so we cannot forward any loads beyond the memory barrier. + if (semantics || opcode == OpControlBarrier) + { + assert(current_emitting_block); + flush_control_dependent_expressions(current_emitting_block->self); + flush_all_active_variables(); + } + + if (memory == ScopeWorkgroup) // Only need to consider memory within a group + { + if (semantics == MemorySemanticsWorkgroupMemoryMask) + { + // OpControlBarrier implies a memory barrier for shared memory as well. + bool implies_shared_barrier = opcode == OpControlBarrier && execution_scope == ScopeWorkgroup; + if (!implies_shared_barrier) + statement("memoryBarrierShared();"); + } + else if (semantics != 0) + statement("groupMemoryBarrier();"); + } + else if (memory == ScopeSubgroup) + { + const uint32_t all_barriers = + MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | MemorySemanticsImageMemoryMask; + + if (semantics & (MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask)) + { + // These are not relevant for GLSL, but assume it means memoryBarrier(). + // memoryBarrier() does everything, so no need to test anything else. + statement("subgroupMemoryBarrier();"); + } + else if ((semantics & all_barriers) == all_barriers) + { + // Short-hand instead of emitting 3 barriers. + statement("subgroupMemoryBarrier();"); + } + else + { + // Pick out individual barriers. + if (semantics & MemorySemanticsWorkgroupMemoryMask) + statement("subgroupMemoryBarrierShared();"); + if (semantics & MemorySemanticsUniformMemoryMask) + statement("subgroupMemoryBarrierBuffer();"); + if (semantics & MemorySemanticsImageMemoryMask) + statement("subgroupMemoryBarrierImage();"); + } + } + else + { + const uint32_t all_barriers = + MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | MemorySemanticsImageMemoryMask; + + if (semantics & (MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask)) + { + // These are not relevant for GLSL, but assume it means memoryBarrier(). + // memoryBarrier() does everything, so no need to test anything else. + statement("memoryBarrier();"); + } + else if ((semantics & all_barriers) == all_barriers) + { + // Short-hand instead of emitting 4 barriers. + statement("memoryBarrier();"); + } + else + { + // Pick out individual barriers. + if (semantics & MemorySemanticsWorkgroupMemoryMask) + statement("memoryBarrierShared();"); + if (semantics & MemorySemanticsUniformMemoryMask) + statement("memoryBarrierBuffer();"); + if (semantics & MemorySemanticsImageMemoryMask) + statement("memoryBarrierImage();"); + } + } + + if (opcode == OpControlBarrier) + { + if (execution_scope == ScopeSubgroup) + statement("subgroupBarrier();"); + else + statement("barrier();"); + } + break; + } + + case OpExtInstWithForwardRefsKHR: + { + uint32_t extension_set = ops[2]; + auto ext = get(extension_set).ext; + if (ext != SPIRExtension::SPV_debug_info && + ext != SPIRExtension::NonSemanticShaderDebugInfo && + ext != SPIRExtension::NonSemanticGeneric) + { + SPIRV_CROSS_THROW("Unexpected use of ExtInstWithForwardRefsKHR."); + } + + break; + } + + case OpExtInst: + { + uint32_t extension_set = ops[2]; + auto ext = get(extension_set).ext; + + if (ext == SPIRExtension::GLSL) + { + emit_glsl_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (ext == SPIRExtension::SPV_AMD_shader_ballot) + { + emit_spv_amd_shader_ballot_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (ext == SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter) + { + emit_spv_amd_shader_explicit_vertex_parameter_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (ext == SPIRExtension::SPV_AMD_shader_trinary_minmax) + { + emit_spv_amd_shader_trinary_minmax_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (ext == SPIRExtension::SPV_AMD_gcn_shader) + { + emit_spv_amd_gcn_shader_op(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (ext == SPIRExtension::NonSemanticShaderDebugInfo) + { + emit_non_semantic_shader_debug_info(ops[0], ops[1], ops[3], &ops[4], length - 4); + } + else if (ext == SPIRExtension::SPV_debug_info || + ext == SPIRExtension::NonSemanticGeneric) + { + break; // Ignore SPIR-V debug information extended instructions. + } + else if (ext == SPIRExtension::NonSemanticDebugPrintf) + { + // Operation 1 is printf. + if (ops[3] == 1) + { + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Debug printf is only supported in Vulkan GLSL.\n"); + require_extension_internal("GL_EXT_debug_printf"); + auto &format_string = get(ops[4]).str; + string expr = join(backend.printf_function, "(\"", format_string, "\""); + for (uint32_t i = 5; i < length; i++) + { + expr += ", "; + expr += to_expression(ops[i]); + } + statement(expr, ");"); + } + } + else + { + statement("// unimplemented ext op ", instruction.op); + break; + } + + break; + } + + // Legacy sub-group stuff ... + case OpSubgroupBallotKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + string expr; + expr = join("uvec4(unpackUint2x32(ballotARB(" + to_expression(ops[2]) + ")), 0u, 0u)"); + emit_op(result_type, id, expr, should_forward(ops[2])); + + require_extension_internal("GL_ARB_shader_ballot"); + inherit_expression_dependencies(id, ops[2]); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupFirstInvocationKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "readFirstInvocationARB"); + + require_extension_internal("GL_ARB_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupReadInvocationKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_binary_func_op(result_type, id, ops[2], ops[3], "readInvocationARB"); + + require_extension_internal("GL_ARB_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupAllKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "allInvocationsARB"); + + require_extension_internal("GL_ARB_shader_group_vote"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupAnyKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "anyInvocationARB"); + + require_extension_internal("GL_ARB_shader_group_vote"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpSubgroupAllEqualKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[2], "allInvocationsEqualARB"); + + require_extension_internal("GL_ARB_shader_group_vote"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpGroupIAddNonUniformAMD: + case OpGroupFAddNonUniformAMD: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[4], "addInvocationsNonUniformAMD"); + + require_extension_internal("GL_AMD_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpGroupFMinNonUniformAMD: + case OpGroupUMinNonUniformAMD: + case OpGroupSMinNonUniformAMD: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[4], "minInvocationsNonUniformAMD"); + + require_extension_internal("GL_AMD_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpGroupFMaxNonUniformAMD: + case OpGroupUMaxNonUniformAMD: + case OpGroupSMaxNonUniformAMD: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_unary_func_op(result_type, id, ops[4], "maxInvocationsNonUniformAMD"); + + require_extension_internal("GL_AMD_shader_ballot"); + register_control_dependent_expression(ops[1]); + break; + } + + case OpFragmentMaskFetchAMD: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (type.image.dim == DimSubpassData) + { + emit_unary_func_op(result_type, id, ops[2], "fragmentMaskFetchAMD"); + } + else + { + emit_binary_func_op(result_type, id, ops[2], ops[3], "fragmentMaskFetchAMD"); + } + + require_extension_internal("GL_AMD_shader_fragment_mask"); + break; + } + + case OpFragmentFetchAMD: + { + auto &type = expression_type(ops[2]); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + if (type.image.dim == DimSubpassData) + { + emit_binary_func_op(result_type, id, ops[2], ops[4], "fragmentFetchAMD"); + } + else + { + emit_trinary_func_op(result_type, id, ops[2], ops[3], ops[4], "fragmentFetchAMD"); + } + + require_extension_internal("GL_AMD_shader_fragment_mask"); + break; + } + + // Vulkan 1.1 sub-group stuff ... + case OpGroupNonUniformElect: + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformBroadcastFirst: + case OpGroupNonUniformBallot: + case OpGroupNonUniformInverseBallot: + case OpGroupNonUniformBallotBitExtract: + case OpGroupNonUniformBallotBitCount: + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + case OpGroupNonUniformAll: + case OpGroupNonUniformAny: + case OpGroupNonUniformAllEqual: + case OpGroupNonUniformFAdd: + case OpGroupNonUniformIAdd: + case OpGroupNonUniformFMul: + case OpGroupNonUniformIMul: + case OpGroupNonUniformFMin: + case OpGroupNonUniformFMax: + case OpGroupNonUniformSMin: + case OpGroupNonUniformSMax: + case OpGroupNonUniformUMin: + case OpGroupNonUniformUMax: + case OpGroupNonUniformBitwiseAnd: + case OpGroupNonUniformBitwiseOr: + case OpGroupNonUniformBitwiseXor: + case OpGroupNonUniformLogicalAnd: + case OpGroupNonUniformLogicalOr: + case OpGroupNonUniformLogicalXor: + case OpGroupNonUniformQuadSwap: + case OpGroupNonUniformQuadBroadcast: + case OpGroupNonUniformQuadAllKHR: + case OpGroupNonUniformQuadAnyKHR: + case OpGroupNonUniformRotateKHR: + emit_subgroup_op(instruction); + break; + + case OpFUnordEqual: + case OpFUnordLessThan: + case OpFUnordGreaterThan: + case OpFUnordLessThanEqual: + case OpFUnordGreaterThanEqual: + { + // GLSL doesn't specify if floating point comparisons are ordered or unordered, + // but glslang always emits ordered floating point compares for GLSL. + // To get unordered compares, we can test the opposite thing and invert the result. + // This way, we force true when there is any NaN present. + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + + string expr; + if (expression_type(op0).vecsize > 1) + { + const char *comp_op = nullptr; + switch (opcode) + { + case OpFUnordEqual: + comp_op = "notEqual"; + break; + + case OpFUnordLessThan: + comp_op = "greaterThanEqual"; + break; + + case OpFUnordLessThanEqual: + comp_op = "greaterThan"; + break; + + case OpFUnordGreaterThan: + comp_op = "lessThanEqual"; + break; + + case OpFUnordGreaterThanEqual: + comp_op = "lessThan"; + break; + + default: + assert(0); + break; + } + + expr = join("not(", comp_op, "(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), "))"); + } + else + { + const char *comp_op = nullptr; + switch (opcode) + { + case OpFUnordEqual: + comp_op = " != "; + break; + + case OpFUnordLessThan: + comp_op = " >= "; + break; + + case OpFUnordLessThanEqual: + comp_op = " > "; + break; + + case OpFUnordGreaterThan: + comp_op = " <= "; + break; + + case OpFUnordGreaterThanEqual: + comp_op = " < "; + break; + + default: + assert(0); + break; + } + + expr = join("!(", to_enclosed_unpacked_expression(op0), comp_op, to_enclosed_unpacked_expression(op1), ")"); + } + + emit_op(ops[0], ops[1], expr, should_forward(op0) && should_forward(op1)); + inherit_expression_dependencies(ops[1], op0); + inherit_expression_dependencies(ops[1], op1); + break; + } + + case OpReportIntersectionKHR: + // NV is same opcode. + forced_temporaries.insert(ops[1]); + if (ray_tracing_is_khr) + GLSL_BFOP(reportIntersectionEXT); + else + GLSL_BFOP(reportIntersectionNV); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpIgnoreIntersectionNV: + // KHR variant is a terminator. + statement("ignoreIntersectionNV();"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpTerminateRayNV: + // KHR variant is a terminator. + statement("terminateRayNV();"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpTraceNV: + statement("traceNV(", to_non_uniform_aware_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ", + to_expression(ops[3]), ", ", to_expression(ops[4]), ", ", to_expression(ops[5]), ", ", + to_expression(ops[6]), ", ", to_expression(ops[7]), ", ", to_expression(ops[8]), ", ", + to_expression(ops[9]), ", ", to_expression(ops[10]), ");"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpTraceRayKHR: + if (!has_decoration(ops[10], DecorationLocation)) + SPIRV_CROSS_THROW("A memory declaration object must be used in TraceRayKHR."); + statement("traceRayEXT(", to_non_uniform_aware_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ", ", + to_expression(ops[3]), ", ", to_expression(ops[4]), ", ", to_expression(ops[5]), ", ", + to_expression(ops[6]), ", ", to_expression(ops[7]), ", ", to_expression(ops[8]), ", ", + to_expression(ops[9]), ", ", get_decoration(ops[10], DecorationLocation), ");"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpExecuteCallableNV: + statement("executeCallableNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + case OpExecuteCallableKHR: + if (!has_decoration(ops[1], DecorationLocation)) + SPIRV_CROSS_THROW("A memory declaration object must be used in ExecuteCallableKHR."); + statement("executeCallableEXT(", to_expression(ops[0]), ", ", get_decoration(ops[1], DecorationLocation), ");"); + flush_control_dependent_expressions(current_emitting_block->self); + break; + + // Don't bother forwarding temporaries. Avoids having to test expression invalidation with ray query objects. + case OpRayQueryInitializeKHR: + flush_variable_declaration(ops[0]); + statement("rayQueryInitializeEXT(", + to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", + to_expression(ops[2]), ", ", to_expression(ops[3]), ", ", + to_expression(ops[4]), ", ", to_expression(ops[5]), ", ", + to_expression(ops[6]), ", ", to_expression(ops[7]), ");"); + break; + case OpRayQueryProceedKHR: + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join("rayQueryProceedEXT(", to_expression(ops[2]), ")"), false); + break; + case OpRayQueryTerminateKHR: + flush_variable_declaration(ops[0]); + statement("rayQueryTerminateEXT(", to_expression(ops[0]), ");"); + break; + case OpRayQueryGenerateIntersectionKHR: + flush_variable_declaration(ops[0]); + statement("rayQueryGenerateIntersectionEXT(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");"); + break; + case OpRayQueryConfirmIntersectionKHR: + flush_variable_declaration(ops[0]); + statement("rayQueryConfirmIntersectionEXT(", to_expression(ops[0]), ");"); + break; + case OpRayQueryGetIntersectionTriangleVertexPositionsKHR: + flush_variable_declaration(ops[1]); + emit_uninitialized_temporary_expression(ops[0], ops[1]); + statement("rayQueryGetIntersectionTriangleVertexPositionsEXT(", to_expression(ops[2]), ", bool(", to_expression(ops[3]), "), ", to_expression(ops[1]), ");"); + break; +#define GLSL_RAY_QUERY_GET_OP(op) \ + case OpRayQueryGet##op##KHR: \ + flush_variable_declaration(ops[2]); \ + emit_op(ops[0], ops[1], join("rayQueryGet" #op "EXT(", to_expression(ops[2]), ")"), false); \ + break +#define GLSL_RAY_QUERY_GET_OP2(op) \ + case OpRayQueryGet##op##KHR: \ + flush_variable_declaration(ops[2]); \ + emit_op(ops[0], ops[1], join("rayQueryGet" #op "EXT(", to_expression(ops[2]), ", ", "bool(", to_expression(ops[3]), "))"), false); \ + break + GLSL_RAY_QUERY_GET_OP(RayTMin); + GLSL_RAY_QUERY_GET_OP(RayFlags); + GLSL_RAY_QUERY_GET_OP(WorldRayOrigin); + GLSL_RAY_QUERY_GET_OP(WorldRayDirection); + GLSL_RAY_QUERY_GET_OP(IntersectionCandidateAABBOpaque); + GLSL_RAY_QUERY_GET_OP2(IntersectionType); + GLSL_RAY_QUERY_GET_OP2(IntersectionT); + GLSL_RAY_QUERY_GET_OP2(IntersectionInstanceCustomIndex); + GLSL_RAY_QUERY_GET_OP2(IntersectionInstanceId); + GLSL_RAY_QUERY_GET_OP2(IntersectionInstanceShaderBindingTableRecordOffset); + GLSL_RAY_QUERY_GET_OP2(IntersectionGeometryIndex); + GLSL_RAY_QUERY_GET_OP2(IntersectionPrimitiveIndex); + GLSL_RAY_QUERY_GET_OP2(IntersectionBarycentrics); + GLSL_RAY_QUERY_GET_OP2(IntersectionFrontFace); + GLSL_RAY_QUERY_GET_OP2(IntersectionObjectRayDirection); + GLSL_RAY_QUERY_GET_OP2(IntersectionObjectRayOrigin); + GLSL_RAY_QUERY_GET_OP2(IntersectionObjectToWorld); + GLSL_RAY_QUERY_GET_OP2(IntersectionWorldToObject); +#undef GLSL_RAY_QUERY_GET_OP +#undef GLSL_RAY_QUERY_GET_OP2 + case OpRayQueryGetClusterIdNV: + flush_variable_declaration(ops[2]); + emit_op(ops[0], ops[1], join("rayQueryGetIntersectionClusterIdNV(", to_expression(ops[2]), ", ", "bool(", to_expression(ops[3]), "))"), false); + break; + case OpTensorQuerySizeARM: + flush_variable_declaration(ops[1]); + // tensorSizeARM(tensor, dimension) + emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], "tensorSizeARM"); + break; + case OpTensorReadARM: + { + flush_variable_declaration(ops[1]); + emit_uninitialized_temporary_expression(ops[0], ops[1]); + + SmallVector args { + to_expression(ops[2]), // tensor + to_expression(ops[3]), // coordinates + to_expression(ops[1]), // out value + }; + if (length > 4) + { + std::string tensor_operands; + if (ops[4] == 0) + tensor_operands = "0x0u"; + else if (ops[4] == TensorOperandsNontemporalARMMask) + tensor_operands = "gl_TensorOperandsNonTemporalARM"; + else if (ops[4] == TensorOperandsOutOfBoundsValueARMMask) + tensor_operands = "gl_TensorOperandsOutOfBoundsValueARM"; + else if (ops[4] == (TensorOperandsNontemporalARMMask | TensorOperandsOutOfBoundsValueARMMask)) + tensor_operands = "gl_TensorOperandsNonTemporalARM | gl_TensorOperandsOutOfBoundsValueARM"; + else + SPIRV_CROSS_THROW("Invalid tensorOperands for tensorReadARM."); + if ((ops[4] & TensorOperandsOutOfBoundsValueARMMask) && length != 6) + SPIRV_CROSS_THROW("gl_TensorOperandsOutOfBoundsValueARM requires an outOfBoundsValue argument."); + args.push_back(tensor_operands); // tensorOperands + } + if (length >= 6) + { + if ((length > 6) || (ops[4] & TensorOperandsOutOfBoundsValueARMMask) == 0) + SPIRV_CROSS_THROW("Too many arguments to tensorReadARM."); + args.push_back(to_expression(ops[5])); // outOfBoundsValue + } + + // tensorRead(tensor, sizeof(type), coordinates, value, operand, ...) + statement("tensorReadARM(", merge(args), ");"); + break; + } + case OpTensorWriteARM: + { + flush_variable_declaration(ops[0]); + + SmallVector args { + to_expression(ops[0]), // tensor + to_expression(ops[1]), // coordinates + to_expression(ops[2]), // out value + }; + + if (length > 3) + { + std::string tensor_operands; + if (ops[3] == 0) + tensor_operands = "0x0u"; + else if (ops[3] == TensorOperandsNontemporalARMMask) + tensor_operands = "gl_TensorOperandsNonTemporalARM"; + else + SPIRV_CROSS_THROW("Invalid tensorOperands for tensorWriteARM."); + args.push_back(tensor_operands); // tensorOperands + } + if (length > 4) + SPIRV_CROSS_THROW("Too many arguments to tensorWriteARM."); + + // tensorWrite(tensor, sizeof(type), coordinates, value) + statement("tensorWriteARM(", merge(args), ");"); + break; + } + case OpConvertUToAccelerationStructureKHR: + { + require_extension_internal("GL_EXT_ray_tracing"); + + bool elide_temporary = should_forward(ops[2]) && forced_temporaries.count(ops[1]) == 0 && + !hoisted_temporaries.count(ops[1]); + + if (elide_temporary) + { + GLSL_UFOP(accelerationStructureEXT); + } + else + { + // Force this path in subsequent iterations. + forced_temporaries.insert(ops[1]); + + // We cannot declare a temporary acceleration structure in GLSL. + // If we get to this point, we'll have to emit a temporary uvec2, + // and cast to RTAS on demand. + statement(declare_temporary(expression_type_id(ops[2]), ops[1]), to_unpacked_expression(ops[2]), ";"); + // Use raw SPIRExpression interface to block all usage tracking. + set(ops[1], join("accelerationStructureEXT(", to_name(ops[1]), ")"), ops[0], true); + } + break; + } + + case OpConvertUToPtr: + { + auto &type = get(ops[0]); + if (type.storage != StorageClassPhysicalStorageBuffer) + SPIRV_CROSS_THROW("Only StorageClassPhysicalStorageBuffer is supported by OpConvertUToPtr."); + + auto &in_type = expression_type(ops[2]); + if (in_type.vecsize == 2) + require_extension_internal("GL_EXT_buffer_reference_uvec2"); + + auto op = type_to_glsl(type); + emit_unary_func_op(ops[0], ops[1], ops[2], op.c_str()); + break; + } + + case OpConvertPtrToU: + { + auto &type = get(ops[0]); + auto &ptr_type = expression_type(ops[2]); + if (ptr_type.storage != StorageClassPhysicalStorageBuffer) + SPIRV_CROSS_THROW("Only StorageClassPhysicalStorageBuffer is supported by OpConvertPtrToU."); + + if (type.vecsize == 2) + require_extension_internal("GL_EXT_buffer_reference_uvec2"); + + auto op = type_to_glsl(type); + emit_unary_func_op(ops[0], ops[1], ops[2], op.c_str()); + break; + } + + case OpUndef: + // Undefined value has been declared. + break; + + case OpLine: + { + emit_line_directive(ops[0], ops[1]); + break; + } + + case OpNoLine: + break; + + case OpDemoteToHelperInvocationEXT: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL."); + require_extension_internal("GL_EXT_demote_to_helper_invocation"); + statement(backend.demote_literal, ";"); + break; + + case OpIsHelperInvocationEXT: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("GL_EXT_demote_to_helper_invocation is only supported in Vulkan GLSL."); + require_extension_internal("GL_EXT_demote_to_helper_invocation"); + // Helper lane state with demote is volatile by nature. + // Do not forward this. + emit_op(ops[0], ops[1], "helperInvocationEXT()", false); + break; + + case OpBeginInvocationInterlockEXT: + // If the interlock is complex, we emit this elsewhere. + if (!interlocked_is_complex) + { + statement("SPIRV_Cross_beginInvocationInterlock();"); + flush_all_active_variables(); + // Make sure forwarding doesn't propagate outside interlock region. + } + break; + + case OpEndInvocationInterlockEXT: + // If the interlock is complex, we emit this elsewhere. + if (!interlocked_is_complex) + { + statement("SPIRV_Cross_endInvocationInterlock();"); + flush_all_active_variables(); + // Make sure forwarding doesn't propagate outside interlock region. + } + break; + + case OpSetMeshOutputsEXT: + statement("SetMeshOutputsEXT(", to_unpacked_expression(ops[0]), ", ", to_unpacked_expression(ops[1]), ");"); + break; + + case OpReadClockKHR: + { + auto &type = get(ops[0]); + auto scope = static_cast(evaluate_constant_u32(ops[2])); + const char *op = nullptr; + // Forwarding clock statements leads to a scenario where an SSA value can take on different + // values every time it's evaluated. Block any forwarding attempt. + // We also might want to invalidate all expressions to function as a sort of optimization + // barrier, but might be overkill for now. + if (scope == ScopeDevice) + { + require_extension_internal("GL_EXT_shader_realtime_clock"); + if (type.basetype == SPIRType::BaseType::UInt64) + op = "clockRealtimeEXT()"; + else if (type.basetype == SPIRType::BaseType::UInt && type.vecsize == 2) + op = "clockRealtime2x32EXT()"; + else + SPIRV_CROSS_THROW("Unsupported result type for OpReadClockKHR opcode."); + } + else if (scope == ScopeSubgroup) + { + require_extension_internal("GL_ARB_shader_clock"); + if (type.basetype == SPIRType::BaseType::UInt64) + op = "clockARB()"; + else if (type.basetype == SPIRType::BaseType::UInt && type.vecsize == 2) + op = "clock2x32ARB()"; + else + SPIRV_CROSS_THROW("Unsupported result type for OpReadClockKHR opcode."); + } + else + SPIRV_CROSS_THROW("Unsupported scope for OpReadClockKHR opcode."); + + emit_op(ops[0], ops[1], op, false); + break; + } + + case OpCooperativeVectorLoadNV: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + emit_uninitialized_temporary_expression(result_type, id); + + statement("coopVecLoadNV(", to_expression(id), ", ", to_expression(ops[2]), ", ", to_expression(ops[3]), ");"); + register_read(id, ops[2], false); + break; + } + + case OpCooperativeVectorStoreNV: + { + uint32_t id = ops[0]; + + statement("coopVecStoreNV(", to_expression(ops[2]), ", ", to_expression(id), ", ", to_expression(ops[1]), ");"); + register_write(ops[2]); + break; + } + + case OpCooperativeVectorOuterProductAccumulateNV: + { + auto buf = ops[0]; + auto offset = ops[1]; + auto v1 = ops[2]; + auto v2 = ops[3]; + auto matrix_layout_id = ops[4]; + auto matrix_iterpretation_id = ops[5]; + auto matrix_stride_id = length >= 6 ? ops[6] : 0; + statement(join("coopVecOuterProductAccumulateNV(", to_expression(v1), ", ", to_expression(v2), ", ", + to_expression(buf), ", ", to_expression(offset), ", ", + matrix_stride_id ? to_expression(matrix_stride_id) : "0", + ", ", to_pretty_expression_if_int_constant( + matrix_layout_id, std::begin(CoopVecMatrixLayoutNames), std::end(CoopVecMatrixLayoutNames)), + ", ", to_pretty_expression_if_int_constant( + matrix_iterpretation_id, std::begin(CoopVecComponentTypeNames), std::end(CoopVecComponentTypeNames)), + ");")); + register_write(ops[0]); + break; + } + + case OpCooperativeVectorReduceSumAccumulateNV: + { + auto buf = ops[0]; + auto offset = ops[1]; + auto v1 = ops[2]; + statement(join("coopVecReduceSumAccumulateNV(", to_expression(v1), ", ", to_expression(buf), ", ", + to_expression(offset), ");")); + register_write(ops[0]); + break; + } + + case OpCooperativeVectorMatrixMulNV: + case OpCooperativeVectorMatrixMulAddNV: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + emit_uninitialized_temporary_expression(result_type, id); + + std::string stmt; + switch (opcode) + { + case OpCooperativeVectorMatrixMulAddNV: + stmt += "coopVecMatMulAddNV("; + break; + case OpCooperativeVectorMatrixMulNV: + stmt += "coopVecMatMulNV("; + break; + default: + SPIRV_CROSS_THROW("Invalid op code for coopvec instruction."); + } + for (uint32_t i = 1; i < length; i++) + { + // arguments 3, 6 and in case of MulAddNv also 9 use component type int constants + if (i == 3 || i == 6 || (i == 9 && opcode == OpCooperativeVectorMatrixMulAddNV)) + { + stmt += to_pretty_expression_if_int_constant( + ops[i], std::begin(CoopVecComponentTypeNames), std::end(CoopVecComponentTypeNames)); + } + else if ((i == 12 && opcode == OpCooperativeVectorMatrixMulAddNV) || + (i == 9 && opcode == OpCooperativeVectorMatrixMulNV)) + { + stmt += to_pretty_expression_if_int_constant( + ops[i], std::begin(CoopVecMatrixLayoutNames), std::end(CoopVecMatrixLayoutNames)); + } + else + stmt += to_expression(ops[i]); + + if (i < length - 1) + stmt += ", "; + } + stmt += ");"; + statement(stmt); + break; + } + + case OpCooperativeMatrixLengthKHR: + { + // Need to synthesize a dummy temporary, since the SPIR-V opcode is based on the type. + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + set( + id, join(type_to_glsl(get(result_type)), + "(", type_to_glsl(get(ops[2])), "(0).length())"), + result_type, true); + break; + } + + case OpCooperativeMatrixLoadKHR: + { + // Spec contradicts itself if stride is optional or not. + if (length < 5) + SPIRV_CROSS_THROW("Stride is not provided."); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + emit_uninitialized_temporary_expression(result_type, id); + + auto expr = to_expression(ops[2]); + pair split_expr; + if (!is_forcing_recompilation()) + split_expr = split_coopmat_pointer(expr); + + string layout_expr = to_pretty_expression_if_int_constant( + ops[3], std::begin(CoopMatMatrixLayoutNames), std::end(CoopMatMatrixLayoutNames)); + statement("coopMatLoad(", to_expression(id), ", ", split_expr.first, ", ", split_expr.second, ", ", + to_expression(ops[4]), ", ", layout_expr, ");"); + + register_read(id, ops[2], false); + break; + } + + case OpCooperativeMatrixStoreKHR: + { + // Spec contradicts itself if stride is optional or not. + if (length < 4) + SPIRV_CROSS_THROW("Stride is not provided."); + + // SPIR-V and GLSL don't agree how to pass the expression. + // In SPIR-V it's a pointer, but in GLSL it's reference to array + index. + + auto expr = to_expression(ops[0]); + pair split_expr; + if (!is_forcing_recompilation()) + split_expr = split_coopmat_pointer(expr); + + string layout_expr = to_pretty_expression_if_int_constant( + ops[2], std::begin(CoopMatMatrixLayoutNames), std::end(CoopMatMatrixLayoutNames)); + + statement("coopMatStore(", to_expression(ops[1]), ", ", split_expr.first, ", ", split_expr.second, ", ", + to_expression(ops[3]), ", ", layout_expr, ");"); + + // TODO: Do we care about memory operands? + + register_write(ops[0]); + break; + } + + case OpCooperativeMatrixMulAddKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t A = ops[2]; + uint32_t B = ops[3]; + uint32_t C = ops[4]; + bool forward = should_forward(A) && should_forward(B) && should_forward(C); + emit_op(result_type, id, + join("coopMatMulAdd(", + to_unpacked_expression(A), ", ", + to_unpacked_expression(B), ", ", + to_unpacked_expression(C), ", ", + (length >= 6 ? ops[5] : 0), + ")"), + forward); + + inherit_expression_dependencies(id, A); + inherit_expression_dependencies(id, B); + inherit_expression_dependencies(id, C); + break; + } + + case OpCompositeConstructReplicateEXT: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto &type = get(result_type); + auto value_to_replicate = to_expression(ops[2]); + std::string rhs; + // Matrices don't have a replicating constructor for vectors. Need to manually replicate + if (type.op == OpTypeMatrix || type.op == OpTypeArray) + { + if (type.op == OpTypeArray && type.array.size() != 1) + { + SPIRV_CROSS_THROW( + "Multi-dimensional arrays currently not supported for OpCompositeConstructReplicateEXT"); + } + uint32_t num_elements = type.op == OpTypeMatrix ? type.columns : type.array[0]; + if (backend.use_initializer_list && type.op == OpTypeArray) + { + rhs += "{"; + } + else + { + rhs += type_to_glsl_constructor(type); + rhs += "("; + } + for (uint32_t i = 0; i < num_elements; i++) + { + rhs += value_to_replicate; + if (i < num_elements - 1) + rhs += ", "; + } + if (backend.use_initializer_list && type.op == OpTypeArray) + rhs += "}"; + else + rhs += ")"; + } + else + { + rhs = join(type_to_glsl(type), "(", to_expression(ops[2]), ")"); + } + emit_op(result_type, id, rhs, true); + break; + } + + default: + statement("// unimplemented op ", instruction.op); + break; + } +} + +// Appends function arguments, mapped from global variables, beyond the specified arg index. +// This is used when a function call uses fewer arguments than the function defines. +// This situation may occur if the function signature has been dynamically modified to +// extract global variables referenced from within the function, and convert them to +// function arguments. This is necessary for shader languages that do not support global +// access to shader input content from within a function (eg. Metal). Each additional +// function args uses the name of the global variable. Function nesting will modify the +// functions and function calls all the way up the nesting chain. +void CompilerGLSL::append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist) +{ + auto &args = func.arguments; + uint32_t arg_cnt = uint32_t(args.size()); + for (uint32_t arg_idx = index; arg_idx < arg_cnt; arg_idx++) + { + auto &arg = args[arg_idx]; + assert(arg.alias_global_variable); + + // If the underlying variable needs to be declared + // (ie. a local variable with deferred declaration), do so now. + uint32_t var_id = get(arg.id).basevariable; + if (var_id) + flush_variable_declaration(var_id); + + arglist.push_back(to_func_call_arg(arg, arg.id)); + } +} + +string CompilerGLSL::to_member_name(const SPIRType &type, uint32_t index) +{ + if (type.type_alias != TypeID(0) && + !has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + { + return to_member_name(get(type.type_alias), index); + } + + auto &memb = ir.meta[type.self].members; + if (index < memb.size() && !memb[index].alias.empty()) + return memb[index].alias; + else + return join("_m", index); +} + +string CompilerGLSL::to_member_reference(uint32_t, const SPIRType &type, uint32_t index, bool) +{ + return join(".", to_member_name(type, index)); +} + +string CompilerGLSL::to_multi_member_reference(const SPIRType &type, const SmallVector &indices) +{ + string ret; + auto *member_type = &type; + for (auto &index : indices) + { + ret += join(".", to_member_name(*member_type, index)); + member_type = &get(member_type->member_types[index]); + } + return ret; +} + +void CompilerGLSL::add_member_name(SPIRType &type, uint32_t index) +{ + auto &memb = ir.meta[type.self].members; + if (index < memb.size() && !memb[index].alias.empty()) + { + auto &name = memb[index].alias; + if (name.empty()) + return; + + ParsedIR::sanitize_identifier(name, true, true); + update_name_cache(type.member_name_cache, name); + } +} + +// Checks whether the ID is a row_major matrix that requires conversion before use +bool CompilerGLSL::is_non_native_row_major_matrix(uint32_t id) +{ + // Natively supported row-major matrices do not need to be converted. + // Legacy targets do not support row major. + if (backend.native_row_major_matrix && !is_legacy()) + return false; + + auto *e = maybe_get(id); + if (e) + return e->need_transpose; + else + return has_decoration(id, DecorationRowMajor); +} + +// Checks whether the member is a row_major matrix that requires conversion before use +bool CompilerGLSL::member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) +{ + // Natively supported row-major matrices do not need to be converted. + if (backend.native_row_major_matrix && !is_legacy()) + return false; + + // Non-matrix or column-major matrix types do not need to be converted. + if (!has_member_decoration(type.self, index, DecorationRowMajor)) + return false; + + // Only square row-major matrices can be converted at this time. + // Converting non-square matrices will require defining custom GLSL function that + // swaps matrix elements while retaining the original dimensional form of the matrix. + const auto mbr_type = get(type.member_types[index]); + if (mbr_type.columns != mbr_type.vecsize) + SPIRV_CROSS_THROW("Row-major matrices must be square on this platform."); + + return true; +} + +// Checks if we need to remap physical type IDs when declaring the type in a buffer. +bool CompilerGLSL::member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const +{ + return has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypeID); +} + +// Checks whether the member is in packed data type, that might need to be unpacked. +bool CompilerGLSL::member_is_packed_physical_type(const SPIRType &type, uint32_t index) const +{ + return has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypePacked); +} + +// Wraps the expression string in a function call that converts the +// row_major matrix result of the expression to a column_major matrix. +// Base implementation uses the standard library transpose() function. +// Subclasses may override to use a different function. +string CompilerGLSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t /* physical_type_id */, + bool /*is_packed*/, bool relaxed) +{ + strip_enclosed_expression(exp_str); + if (!is_matrix(exp_type)) + { + auto column_index = exp_str.find_last_of('['); + if (column_index == string::npos) + return exp_str; + + auto column_expr = exp_str.substr(column_index); + exp_str.resize(column_index); + + auto end_deferred_index = column_expr.find_last_of(']'); + if (end_deferred_index != string::npos && end_deferred_index + 1 != column_expr.size()) + { + // If we have any data member fixups, it must be transposed so that it refers to this index. + // E.g. [0].data followed by [1] would be shuffled to [1][0].data which is wrong, + // and needs to be [1].data[0] instead. + end_deferred_index++; + column_expr = column_expr.substr(end_deferred_index) + + column_expr.substr(0, end_deferred_index); + } + + auto transposed_expr = type_to_glsl_constructor(exp_type) + "("; + + // Loading a column from a row-major matrix. Unroll the load. + for (uint32_t c = 0; c < exp_type.vecsize; c++) + { + transposed_expr += join(exp_str, '[', c, ']', column_expr); + if (c + 1 < exp_type.vecsize) + transposed_expr += ", "; + } + + transposed_expr += ")"; + return transposed_expr; + } + else if (options.version < 120) + { + // GLSL 110, ES 100 do not have transpose(), so emulate it. Note that + // these GLSL versions do not support non-square matrices. + if (exp_type.vecsize == 2 && exp_type.columns == 2) + require_polyfill(PolyfillTranspose2x2, relaxed); + else if (exp_type.vecsize == 3 && exp_type.columns == 3) + require_polyfill(PolyfillTranspose3x3, relaxed); + else if (exp_type.vecsize == 4 && exp_type.columns == 4) + require_polyfill(PolyfillTranspose4x4, relaxed); + else + SPIRV_CROSS_THROW("Non-square matrices are not supported in legacy GLSL, cannot transpose."); + return join("spvTranspose", (options.es && relaxed) ? "MP" : "", "(", exp_str, ")"); + } + else + return join("transpose(", exp_str, ")"); +} + +string CompilerGLSL::variable_decl(const SPIRType &type, const string &name, uint32_t id) +{ + string type_name = type_to_glsl(type, id); + remap_variable_type_name(type, name, type_name); + return join(type_name, " ", name, type_to_array_glsl(type, id)); +} + +bool CompilerGLSL::variable_decl_is_remapped_storage(const SPIRVariable &var, StorageClass storage) const +{ + return var.storage == storage; +} + +// Emit a structure member. Subclasses may override to modify output, +// or to dynamically add a padding member if needed. +void CompilerGLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier, uint32_t) +{ + auto &membertype = get(member_type_id); + + Bitset memberflags; + auto &memb = ir.meta[type.self].members; + if (index < memb.size()) + memberflags = memb[index].decoration_flags; + + string qualifiers; + bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + + if (is_block) + qualifiers = to_interpolation_qualifiers(memberflags); + + statement(layout_for_member(type, index), qualifiers, qualifier, flags_to_qualifiers_glsl(membertype, 0, memberflags), + variable_decl(membertype, to_member_name(type, index)), ";"); +} + +string CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, uint32_t id, const Bitset &flags) +{ + // GL_EXT_buffer_reference variables can be marked as restrict. + if (flags.get(DecorationRestrictPointerEXT)) + return "restrict "; + + string qual; + + if (type_is_floating_point(type) && + (flags.get(DecorationNoContraction) || (type.self && has_legacy_nocontract(type.self, id))) && + backend.support_precise_qualifier) + { + qual = "precise "; + } + + // Structs do not have precision qualifiers, neither do doubles (desktop only anyways, so no mediump/highp). + bool type_supports_precision = + type.basetype == SPIRType::Float || type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt || + type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage || + type.basetype == SPIRType::Sampler; + + if (!type_supports_precision) + return qual; + + if (options.es) + { + auto &execution = get_entry_point(); + + if (type.basetype == SPIRType::UInt && is_legacy_es()) + { + // HACK: This is a bool. See comment in type_to_glsl(). + qual += "lowp "; + } + else if (flags.get(DecorationRelaxedPrecision)) + { + bool implied_fmediump = type.basetype == SPIRType::Float && + options.fragment.default_float_precision == Options::Mediump && + execution.model == ExecutionModelFragment; + + bool implied_imediump = (type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt) && + options.fragment.default_int_precision == Options::Mediump && + execution.model == ExecutionModelFragment; + + qual += (implied_fmediump || implied_imediump) ? "" : "mediump "; + } + else + { + bool implied_fhighp = + type.basetype == SPIRType::Float && ((options.fragment.default_float_precision == Options::Highp && + execution.model == ExecutionModelFragment) || + (execution.model != ExecutionModelFragment)); + + bool implied_ihighp = (type.basetype == SPIRType::Int || type.basetype == SPIRType::UInt) && + ((options.fragment.default_int_precision == Options::Highp && + execution.model == ExecutionModelFragment) || + (execution.model != ExecutionModelFragment)); + + qual += (implied_fhighp || implied_ihighp) ? "" : "highp "; + } + } + else if (backend.allow_precision_qualifiers) + { + // Vulkan GLSL supports precision qualifiers, even in desktop profiles, which is convenient. + // The default is highp however, so only emit mediump in the rare case that a shader has these. + if (flags.get(DecorationRelaxedPrecision)) + qual += "mediump "; + } + + return qual; +} + +string CompilerGLSL::to_precision_qualifiers_glsl(uint32_t id) +{ + auto &type = expression_type(id); + bool use_precision_qualifiers = backend.allow_precision_qualifiers; + if (use_precision_qualifiers && (type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage)) + { + // Force mediump for the sampler type. We cannot declare 16-bit or smaller image types. + auto &result_type = get(type.image.type); + if (result_type.width < 32) + return "mediump "; + } + return flags_to_qualifiers_glsl(type, id, ir.meta[id].decoration.decoration_flags); +} + +void CompilerGLSL::fixup_io_block_patch_primitive_qualifiers(const SPIRVariable &var) +{ + // Works around weird behavior in glslangValidator where + // a patch out block is translated to just block members getting the decoration. + // To make glslang not complain when we compile again, we have to transform this back to a case where + // the variable itself has Patch decoration, and not members. + // Same for perprimitiveEXT. + auto &type = get(var.basetype); + if (has_decoration(type.self, DecorationBlock)) + { + uint32_t member_count = uint32_t(type.member_types.size()); + Decoration promoted_decoration = {}; + bool do_promote_decoration = false; + for (uint32_t i = 0; i < member_count; i++) + { + if (has_member_decoration(type.self, i, DecorationPatch)) + { + promoted_decoration = DecorationPatch; + do_promote_decoration = true; + break; + } + else if (has_member_decoration(type.self, i, DecorationPerPrimitiveEXT)) + { + promoted_decoration = DecorationPerPrimitiveEXT; + do_promote_decoration = true; + break; + } + } + + if (do_promote_decoration) + { + set_decoration(var.self, promoted_decoration); + for (uint32_t i = 0; i < member_count; i++) + unset_member_decoration(type.self, i, promoted_decoration); + } + } +} + +string CompilerGLSL::to_qualifiers_glsl(uint32_t id) +{ + auto &flags = get_decoration_bitset(id); + string res; + + auto *var = maybe_get(id); + + if (var && var->storage == StorageClassWorkgroup && !backend.shared_is_implied) + res += "shared "; + else if (var && var->storage == StorageClassTaskPayloadWorkgroupEXT && !backend.shared_is_implied) + res += "taskPayloadSharedEXT "; + + res += to_interpolation_qualifiers(flags); + if (var) + res += to_storage_qualifiers_glsl(*var); + + auto &type = expression_type(id); + if (type.image.dim != DimSubpassData && type.image.sampled == 2) + { + if (flags.get(DecorationCoherent)) + res += "coherent "; + if (flags.get(DecorationRestrict)) + res += "restrict "; + + if (flags.get(DecorationNonWritable)) + res += "readonly "; + + bool formatted_load = type.image.format == ImageFormatUnknown; + if (flags.get(DecorationNonReadable)) + { + res += "writeonly "; + formatted_load = false; + } + + if (formatted_load) + { + if (!options.es) + require_extension_internal("GL_EXT_shader_image_load_formatted"); + else + SPIRV_CROSS_THROW("Cannot use GL_EXT_shader_image_load_formatted in ESSL."); + } + } + else if (type.basetype == SPIRType::Tensor) + { + if (flags.get(DecorationNonWritable)) + res += "readonly "; + if (flags.get(DecorationNonReadable)) + res += "writeonly "; + } + + res += to_precision_qualifiers_glsl(id); + + return res; +} + +string CompilerGLSL::argument_decl(const SPIRFunction::Parameter &arg) +{ + // glslangValidator seems to make all arguments pointer no matter what which is rather bizarre ... + auto &type = expression_type(arg.id); + const char *direction = ""; + + if (is_pointer(type) && + (type.storage == StorageClassFunction || + type.storage == StorageClassPrivate || + type.storage == StorageClassOutput)) + { + // If we're passing around block types to function, we really mean reference in a pointer sense, + // but DXC does not like inout for mesh blocks, so workaround that. out is technically not correct, + // but it works in practice due to legalization. It's ... not great, but you gotta do what you gotta do. + // GLSL will never hit this case since it's not valid. + if (type.storage == StorageClassOutput && get_execution_model() == ExecutionModelMeshEXT && + has_decoration(type.self, DecorationBlock) && is_builtin_type(type) && arg.write_count) + { + direction = "out "; + } + else if (arg.write_count && arg.read_count) + direction = "inout "; + else if (arg.write_count) + direction = "out "; + } + + return join(direction, to_qualifiers_glsl(arg.id), variable_decl(type, to_name(arg.id), arg.id)); +} + +string CompilerGLSL::to_initializer_expression(const SPIRVariable &var) +{ + return to_unpacked_expression(var.initializer); +} + +string CompilerGLSL::to_zero_initialized_expression(uint32_t type_id) +{ +#ifndef NDEBUG + auto &type = get(type_id); + assert(type.storage == StorageClassPrivate || type.storage == StorageClassFunction || + type.storage == StorageClassGeneric); +#endif + uint32_t id = ir.increase_bound_by(1); + ir.make_constant_null(id, type_id, false); + return constant_expression(get(id)); +} + +bool CompilerGLSL::type_can_zero_initialize(const SPIRType &type) const +{ + if (type.pointer) + return false; + + if (!type.array.empty() && options.flatten_multidimensional_arrays) + return false; + + for (auto &literal : type.array_size_literal) + if (!literal) + return false; + + for (auto &memb : type.member_types) + if (!type_can_zero_initialize(get(memb))) + return false; + + return true; +} + +string CompilerGLSL::variable_decl(const SPIRVariable &variable) +{ + // Ignore the pointer type since GLSL doesn't have pointers. + auto &type = get_variable_data_type(variable); + + if (type.pointer_depth > 1 && !backend.support_pointer_to_pointer) + SPIRV_CROSS_THROW("Cannot declare pointer-to-pointer types."); + + auto res = join(to_qualifiers_glsl(variable.self), variable_decl(type, to_name(variable.self), variable.self)); + + if (variable.loop_variable && variable.static_expression) + { + uint32_t expr = variable.static_expression; + if (ir.ids[expr].get_type() != TypeUndef) + res += join(" = ", to_unpacked_expression(variable.static_expression)); + else if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + res += join(" = ", to_zero_initialized_expression(get_variable_data_type_id(variable))); + } + else if (variable.initializer) + { + if (!variable_decl_is_remapped_storage(variable, StorageClassWorkgroup)) + { + uint32_t expr = variable.initializer; + if (ir.ids[expr].get_type() != TypeUndef) + res += join(" = ", to_initializer_expression(variable)); + else if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + res += join(" = ", to_zero_initialized_expression(get_variable_data_type_id(variable))); + } + else + { + // Workgroup memory requires special handling. First, it can only be Null-Initialized. + // GLSL will handle this with null initializer, while others require more work after the decl + require_extension_internal("GL_EXT_null_initializer"); + if (!backend.constant_null_initializer.empty()) + res += join(" = ", backend.constant_null_initializer); + } + } + + return res; +} + +const char *CompilerGLSL::to_pls_qualifiers_glsl(const SPIRVariable &variable) +{ + auto &flags = get_decoration_bitset(variable.self); + if (flags.get(DecorationRelaxedPrecision)) + return "mediump "; + else + return "highp "; +} + +string CompilerGLSL::pls_decl(const PlsRemap &var) +{ + auto &variable = get(var.id); + + auto op_and_basetype = pls_format_to_basetype(var.format); + + SPIRType type { op_and_basetype.first }; + type.basetype = op_and_basetype.second; + auto vecsize = pls_format_to_components(var.format); + if (vecsize > 1) + { + type.op = OpTypeVector; + type.vecsize = vecsize; + } + + return join(to_pls_layout(var.format), to_pls_qualifiers_glsl(variable), type_to_glsl(type), " ", + to_name(variable.self)); +} + +uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type) const +{ + return to_array_size_literal(type, uint32_t(type.array.size() - 1)); +} + +uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type, uint32_t index) const +{ + assert(type.array.size() == type.array_size_literal.size()); + + if (type.array_size_literal[index]) + { + return type.array[index]; + } + else + { + // Use the default spec constant value. + // This is the best we can do. + return evaluate_constant_u32(type.array[index]); + } +} + +string CompilerGLSL::to_array_size(const SPIRType &type, uint32_t index) +{ + assert(type.array.size() == type.array_size_literal.size()); + + auto &size = type.array[index]; + if (!type.array_size_literal[index]) + return to_expression(size); + else if (size) + return convert_to_string(size); + else if (!backend.unsized_array_supported) + { + // For runtime-sized arrays, we can work around + // lack of standard support for this by simply having + // a single element array. + // + // Runtime length arrays must always be the last element + // in an interface block. + return "1"; + } + else + return ""; +} + +string CompilerGLSL::type_to_array_glsl(const SPIRType &type, uint32_t) +{ + if (type.pointer && type.storage == StorageClassPhysicalStorageBuffer && type.basetype != SPIRType::Struct) + { + // We are using a wrapped pointer type, and we should not emit any array declarations here. + return ""; + } + + if (type.array.empty()) + return ""; + + if (options.flatten_multidimensional_arrays) + { + string res; + res += "["; + for (auto i = uint32_t(type.array.size()); i; i--) + { + res += enclose_expression(to_array_size(type, i - 1)); + if (i > 1) + res += " * "; + } + res += "]"; + return res; + } + else + { + if (type.array.size() > 1) + { + if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_arrays_of_arrays"); + else if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310. " + "Try using --flatten-multidimensional-arrays or set " + "options.flatten_multidimensional_arrays to true."); + } + + string res; + for (auto i = uint32_t(type.array.size()); i; i--) + { + res += "["; + res += to_array_size(type, i - 1); + res += "]"; + } + return res; + } +} + +string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t id, bool /*member*/) +{ + auto &imagetype = get(type.image.type); + string res; + + switch (imagetype.basetype) + { + case SPIRType::Int64: + res = "i64"; + require_extension_internal("GL_EXT_shader_image_int64"); + break; + case SPIRType::UInt64: + res = "u64"; + require_extension_internal("GL_EXT_shader_image_int64"); + break; + case SPIRType::Int: + case SPIRType::Short: + case SPIRType::SByte: + res = "i"; + break; + case SPIRType::UInt: + case SPIRType::UShort: + case SPIRType::UByte: + res = "u"; + break; + default: + break; + } + + // For half image types, we will force mediump for the sampler, and cast to f16 after any sampling operation. + // We cannot express a true half texture type in GLSL. Neither for short integer formats for that matter. + + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics) + return res + "subpassInput" + (type.image.ms ? "MS" : ""); + else if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && + subpass_input_is_framebuffer_fetch(id)) + { + SPIRType sampled_type = get(type.image.type); + sampled_type.vecsize = 4; + return type_to_glsl(sampled_type); + } + + // If we're emulating subpassInput with samplers, force sampler2D + // so we don't have to specify format. + if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData) + { + // Sampler buffers are always declared as samplerBuffer even though they might be separate images in the SPIR-V. + if (type.image.dim == DimBuffer && type.image.sampled == 1) + res += "sampler"; + else + res += type.image.sampled == 2 ? "image" : "texture"; + } + else + res += "sampler"; + + switch (type.image.dim) + { + case Dim1D: + // ES doesn't support 1D. Fake it with 2D. + res += options.es ? "2D" : "1D"; + break; + case Dim2D: + res += "2D"; + break; + case Dim3D: + res += "3D"; + break; + case DimCube: + res += "Cube"; + break; + case DimRect: + if (options.es) + SPIRV_CROSS_THROW("Rectangle textures are not supported on OpenGL ES."); + + if (is_legacy_desktop()) + require_extension_internal("GL_ARB_texture_rectangle"); + + res += "2DRect"; + break; + + case DimBuffer: + if (options.es && options.version < 320) + require_extension_internal("GL_EXT_texture_buffer"); + else if (!options.es && options.version < 140) + require_extension_internal("GL_EXT_texture_buffer_object"); + res += "Buffer"; + break; + + case DimSubpassData: + res += "2D"; + break; + default: + SPIRV_CROSS_THROW("Only 1D, 2D, 2DRect, 3D, Buffer, InputTarget and Cube textures supported."); + } + + if (type.image.ms) + res += "MS"; + if (type.image.arrayed) + { + if (is_legacy_desktop()) + require_extension_internal("GL_EXT_texture_array"); + res += "Array"; + } + + // "Shadow" state in GLSL only exists for samplers and combined image samplers. + if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) && + is_depth_image(type, id)) + { + res += "Shadow"; + + if (type.image.dim == DimCube && is_legacy()) + { + if (!options.es) + require_extension_internal("GL_EXT_gpu_shader4"); + else + { + require_extension_internal("GL_NV_shadow_samplers_cube"); + res += "NV"; + } + } + } + + return res; +} + +string CompilerGLSL::type_to_glsl_constructor(const SPIRType &type) +{ + if (backend.use_array_constructor && type.array.size() > 1) + { + if (options.flatten_multidimensional_arrays) + SPIRV_CROSS_THROW("Cannot flatten constructors of multidimensional array constructors, " + "e.g. float[][]()."); + else if (!options.es && options.version < 430) + require_extension_internal("GL_ARB_arrays_of_arrays"); + else if (options.es && options.version < 310) + SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310."); + } + + auto e = type_to_glsl(type); + if (backend.use_array_constructor) + { + for (uint32_t i = 0; i < type.array.size(); i++) + e += "[]"; + } + return e; +} + +// The optional id parameter indicates the object whose type we are trying +// to find the description for. It is optional. Most type descriptions do not +// depend on a specific object's use of that type. +string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) +{ + if (is_physical_pointer(type) && !is_physical_pointer_to_buffer_block(type)) + { + // Need to create a magic type name which compacts the entire type information. + auto *parent = &get_pointee_type(type); + string name = type_to_glsl(*parent); + + uint32_t array_stride = get_decoration(type.parent_type, DecorationArrayStride); + + // Resolve all array dimensions in one go since once we lose the pointer type, + // array information is left to to_array_type_glsl. The base type loses array information. + while (is_array(*parent)) + { + if (parent->array_size_literal.back()) + name += join(type.array.back(), "_"); + else + name += join("id", type.array.back(), "_"); + + name += "stride_" + std::to_string(array_stride); + + array_stride = get_decoration(parent->parent_type, DecorationArrayStride); + parent = &get(parent->parent_type); + } + + name += "Pointer"; + return name; + } + + switch (type.basetype) + { + case SPIRType::Struct: + // Need OpName lookup here to get a "sensible" name for a struct. + if (backend.explicit_struct_type) + return join("struct ", to_name(type.self)); + else + return to_name(type.self); + + case SPIRType::Image: + case SPIRType::SampledImage: + return image_type_glsl(type, 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_ids.count(id) ? "samplerShadow" : "sampler"; + + case SPIRType::AccelerationStructure: + return ray_tracing_is_khr ? "accelerationStructureEXT" : "accelerationStructureNV"; + + case SPIRType::RayQuery: + return "rayQueryEXT"; + + case SPIRType::Tensor: + if (type.ext.tensor.rank == 0) + SPIRV_CROSS_THROW("GLSL tensors must have a Rank."); + if (type.ext.tensor.shape != 0) + SPIRV_CROSS_THROW("GLSL tensors cannot have a Shape."); + return join("tensorARM<", type_to_glsl(get(type.ext.tensor.type)), ", ", + to_expression(type.ext.tensor.rank), ">"); + + case SPIRType::Void: + return "void"; + + default: + break; + } + + if (type.basetype == SPIRType::UInt && is_legacy()) + { + if (options.es) + // HACK: spirv-cross changes bools into uints and generates code which compares them to + // zero. Input code will have already been validated as not to have contained any uints, + // so any remaining uints must in fact be bools. However, simply returning "bool" here + // will result in invalid code. Instead, return an int. + return backend.basic_int_type; + else + require_extension_internal("GL_EXT_gpu_shader4"); + } + + if (type.basetype == SPIRType::AtomicCounter) + { + if (options.es && options.version < 310) + SPIRV_CROSS_THROW("At least ESSL 3.10 required for atomic counters."); + else if (!options.es && options.version < 420) + require_extension_internal("GL_ARB_shader_atomic_counters"); + } + + if (type.op == OpTypeCooperativeVectorNV) + { + require_extension_internal("GL_NV_cooperative_vector"); + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Cooperative vector NV only available in Vulkan."); + + std::string component_type_str = type_to_glsl(get(type.ext.coopVecNV.component_type_id)); + + return join("coopvecNV<", component_type_str, ", ", to_expression(type.ext.coopVecNV.component_count_id), ">"); + } + + const SPIRType *coop_type = &type; + while (is_pointer(*coop_type) || is_array(*coop_type)) + coop_type = &get(coop_type->parent_type); + + if (coop_type->op == OpTypeCooperativeMatrixKHR) + { + require_extension_internal("GL_KHR_cooperative_matrix"); + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("Cooperative matrix only available in Vulkan."); + // GLSL doesn't support this as spec constant, which makes sense ... + uint32_t use_type = get(coop_type->ext.cooperative.use_id).scalar(); + + const char *use = nullptr; + switch (use_type) + { + case CooperativeMatrixUseMatrixAKHR: + use = "gl_MatrixUseA"; + break; + + case CooperativeMatrixUseMatrixBKHR: + use = "gl_MatrixUseB"; + break; + + case CooperativeMatrixUseMatrixAccumulatorKHR: + use = "gl_MatrixUseAccumulator"; + break; + + default: + SPIRV_CROSS_THROW("Invalid matrix use."); + } + + string scope_expr; + if (const auto *scope = maybe_get(coop_type->ext.cooperative.scope_id)) + { + if (!scope->specialization) + { + require_extension_internal("GL_KHR_memory_scope_semantics"); + if (scope->scalar() == ScopeSubgroup) + scope_expr = "gl_ScopeSubgroup"; + else if (scope->scalar() == ScopeWorkgroup) + scope_expr = "gl_ScopeWorkgroup"; + else + SPIRV_CROSS_THROW("Invalid scope for cooperative matrix."); + } + } + + if (scope_expr.empty()) + scope_expr = to_expression(coop_type->ext.cooperative.scope_id); + + return join("coopmat<", type_to_glsl(get(coop_type->parent_type)), ", ", + scope_expr, ", ", + to_expression(coop_type->ext.cooperative.rows_id), ", ", + to_expression(coop_type->ext.cooperative.columns_id), ", ", use, ">"); + } + + if (type.vecsize == 1 && type.columns == 1) // Scalar builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return "bool"; + case SPIRType::SByte: + return backend.basic_int8_type; + case SPIRType::UByte: + return backend.basic_uint8_type; + case SPIRType::Short: + return backend.basic_int16_type; + case SPIRType::UShort: + return backend.basic_uint16_type; + case SPIRType::Int: + return backend.basic_int_type; + case SPIRType::UInt: + return backend.basic_uint_type; + case SPIRType::AtomicCounter: + return "atomic_uint"; + case SPIRType::Half: + return "float16_t"; + case SPIRType::BFloat16: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("bfloat16 requires Vulkan semantics."); + require_extension_internal("GL_EXT_bfloat16"); + return "bfloat16_t"; + case SPIRType::FloatE4M3: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("floate4m3_t requires Vulkan semantics."); + require_extension_internal("GL_EXT_float_e4m3"); + return "floate4m3_t"; + case SPIRType::FloatE5M2: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("floate5m2_t requires Vulkan semantics."); + require_extension_internal("GL_EXT_float_e5m2"); + return "floate5m2_t"; + case SPIRType::Float: + return "float"; + case SPIRType::Double: + return "double"; + case SPIRType::Int64: + return "int64_t"; + case SPIRType::UInt64: + return "uint64_t"; + default: + return "???"; + } + } + else if (type.vecsize > 1 && type.columns == 1) // Vector builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bvec", type.vecsize); + case SPIRType::SByte: + return join("i8vec", type.vecsize); + case SPIRType::UByte: + return join("u8vec", type.vecsize); + case SPIRType::Short: + return join("i16vec", type.vecsize); + case SPIRType::UShort: + return join("u16vec", type.vecsize); + case SPIRType::Int: + return join("ivec", type.vecsize); + case SPIRType::UInt: + return join("uvec", type.vecsize); + case SPIRType::Half: + return join("f16vec", type.vecsize); + case SPIRType::BFloat16: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("bfloat16 requires Vulkan semantics."); + require_extension_internal("GL_EXT_bfloat16"); + return join("bf16vec", type.vecsize); + case SPIRType::FloatE4M3: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("floate4m3_t requires Vulkan semantics."); + require_extension_internal("GL_EXT_float_e4m3"); + return join("fe4m3vec", type.vecsize); + case SPIRType::FloatE5M2: + if (!options.vulkan_semantics) + SPIRV_CROSS_THROW("floate5m2_t requires Vulkan semantics."); + require_extension_internal("GL_EXT_float_e5m2"); + return join("fe5m2vec", type.vecsize); + case SPIRType::Float: + return join("vec", type.vecsize); + case SPIRType::Double: + return join("dvec", type.vecsize); + case SPIRType::Int64: + return join("i64vec", type.vecsize); + case SPIRType::UInt64: + return join("u64vec", type.vecsize); + default: + return "???"; + } + } + else if (type.vecsize == type.columns) // Simple Matrix builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bmat", type.vecsize); + case SPIRType::Int: + return join("imat", type.vecsize); + case SPIRType::UInt: + return join("umat", type.vecsize); + case SPIRType::Half: + return join("f16mat", type.vecsize); + case SPIRType::Float: + return join("mat", type.vecsize); + case SPIRType::Double: + return join("dmat", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } + else + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bmat", type.columns, "x", type.vecsize); + case SPIRType::Int: + return join("imat", type.columns, "x", type.vecsize); + case SPIRType::UInt: + return join("umat", type.columns, "x", type.vecsize); + case SPIRType::Half: + return join("f16mat", type.columns, "x", type.vecsize); + case SPIRType::Float: + return join("mat", type.columns, "x", type.vecsize); + case SPIRType::Double: + return join("dmat", type.columns, "x", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } +} + +void CompilerGLSL::add_variable(unordered_set &variables_primary, + const unordered_set &variables_secondary, string &name) +{ + if (name.empty()) + return; + + ParsedIR::sanitize_underscores(name); + if (ParsedIR::is_globally_reserved_identifier(name, true)) + { + name.clear(); + return; + } + + update_name_cache(variables_primary, variables_secondary, name); +} + +void CompilerGLSL::add_local_variable_name(uint32_t id) +{ + add_variable(local_variable_names, block_names, ir.meta[id].decoration.alias); +} + +void CompilerGLSL::add_resource_name(uint32_t id) +{ + add_variable(resource_names, block_names, ir.meta[id].decoration.alias); +} + +void CompilerGLSL::add_header_line(const std::string &line) +{ + header_lines.push_back(line); +} + +bool CompilerGLSL::has_extension(const std::string &ext) const +{ + auto itr = find(begin(forced_extensions), end(forced_extensions), ext); + return itr != end(forced_extensions); +} + +void CompilerGLSL::require_extension(const std::string &ext) +{ + if (!has_extension(ext)) + forced_extensions.push_back(ext); +} + +const SmallVector &CompilerGLSL::get_required_extensions() const +{ + return forced_extensions; +} + +void CompilerGLSL::require_extension_internal(const string &ext) +{ + if (backend.supports_extensions && !has_extension(ext)) + { + forced_extensions.push_back(ext); + force_recompile(); + } +} + +void CompilerGLSL::flatten_buffer_block(VariableID id) +{ + auto &var = get(id); + auto &type = get(var.basetype); + auto name = to_name(type.self, false); + auto &flags = get_decoration_bitset(type.self); + + if (!type.array.empty()) + SPIRV_CROSS_THROW(name + " is an array of UBOs."); + if (type.basetype != SPIRType::Struct) + SPIRV_CROSS_THROW(name + " is not a struct."); + if (!flags.get(DecorationBlock)) + SPIRV_CROSS_THROW(name + " is not a block."); + if (type.member_types.empty()) + SPIRV_CROSS_THROW(name + " is an empty struct."); + + flattened_buffer_blocks.insert(id); +} + +bool CompilerGLSL::builtin_translates_to_nonarray(BuiltIn /*builtin*/) const +{ + return false; // GLSL itself does not need to translate array builtin types to non-array builtin types +} + +bool CompilerGLSL::is_user_type_structured(uint32_t /*id*/) const +{ + return false; // GLSL itself does not have structured user type, but HLSL does with StructuredBuffer and RWStructuredBuffer resources. +} + +bool CompilerGLSL::check_atomic_image(uint32_t id) +{ + auto &type = expression_type(id); + if (type.storage == StorageClassImage) + { + if (options.es && options.version < 320) + require_extension_internal("GL_OES_shader_image_atomic"); + + auto *var = maybe_get_backing_variable(id); + if (var) + { + if (has_decoration(var->self, DecorationNonWritable) || has_decoration(var->self, DecorationNonReadable)) + { + unset_decoration(var->self, DecorationNonWritable); + unset_decoration(var->self, DecorationNonReadable); + force_recompile(); + } + } + return true; + } + else + return false; +} + +void CompilerGLSL::add_function_overload(const SPIRFunction &func) +{ + Hasher hasher; + for (auto &arg : func.arguments) + { + // 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 = get_pointee_type_id(arg.type); + + // Workaround glslang bug. It seems to only consider the base type when resolving overloads. + if (get(type_id).op == OpTypeCooperativeMatrixKHR) + type_id = get(type_id).parent_type; + + auto &type = get(type_id); + + if (!combined_image_samplers.empty()) + { + // If we have combined image samplers, we cannot really trust the image and sampler arguments + // 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) + { + continue; + } + } + + hasher.u32(type_id); + } + uint64_t types_hash = hasher.get(); + + auto function_name = to_name(func.self); + auto itr = function_overloads.find(function_name); + if (itr != end(function_overloads)) + { + // There exists a function with this name already. + auto &overloads = itr->second; + if (overloads.count(types_hash) != 0) + { + // Overload conflict, assign a new name. + add_resource_name(func.self); + function_overloads[to_name(func.self)].insert(types_hash); + } + else + { + // Can reuse the name. + overloads.insert(types_hash); + } + } + else + { + // First time we see this function name. + add_resource_name(func.self); + function_overloads[to_name(func.self)].insert(types_hash); + } +} + +void CompilerGLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + // Avoid shadow declarations. + local_variable_names = resource_names; + + string decl; + + auto &type = get(func.return_type); + decl += flags_to_qualifiers_glsl(type, 0, return_flags); + decl += type_to_glsl(type); + decl += type_to_array_glsl(type, 0); + decl += " "; + + if (func.self == ir.default_entry_point) + { + // If we need complex fallback in GLSL, we just wrap main() in a function + // and interlock the entire shader ... + if (interlocked_is_complex) + decl += "spvMainInterlockedBody"; + else if (options.use_entry_point_name) + decl += get_entry_point().name; + else + decl += "main"; + + processing_entry_point = true; + } + else + decl += to_name(func.self); + + decl += "("; + SmallVector arglist; + for (auto &arg : func.arguments) + { + // Do not pass in separate images or samplers if we're remapping + // to combined image samplers. + if (skip_argument(arg.id)) + continue; + + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + for (auto &arg : func.shadow_arguments) + { + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + decl += merge(arglist); + decl += ")"; + statement(decl); +} + +void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) +{ + // Avoid potential cycles. + if (func.active) + return; + func.active = true; + + // If we depend on a function, emit that function before we emit our own function. + for (auto block : func.blocks) + { + auto &b = get(block); + for (auto &i : b.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + if (op == OpFunctionCall) + { + // Recursively emit functions which are called. + uint32_t id = ops[2]; + + emit_function(get(id), ir.meta[ops[1]].decoration.decoration_flags); + } + } + } + + if (func.entry_line.file_id != 0) + emit_line_directive(func.entry_line.file_id, func.entry_line.line_literal); + emit_function_prototype(func, return_flags); + begin_scope(); + + if (func.self == ir.default_entry_point) + emit_entry_point_declarations(); + + current_function = &func; + auto &entry_block = get(func.entry_block); + + sort(begin(func.constant_arrays_needed_on_stack), end(func.constant_arrays_needed_on_stack)); + for (auto &array : func.constant_arrays_needed_on_stack) + { + auto &c = get(array); + auto &type = get(c.constant_type); + statement(variable_decl(type, join("_", array, "_array_copy")), " = ", constant_expression(c), ";"); + } + + for (auto &v : func.local_variables) + { + auto &var = get(v); + var.deferred_declaration = false; + if (var.storage == StorageClassTaskPayloadWorkgroupEXT) + continue; + + if (variable_decl_is_remapped_storage(var, StorageClassWorkgroup)) + { + // Special variable type which cannot have initializer, + // need to be declared as standalone variables. + // Comes from MSL which can push global variables as local variables in main function. + add_local_variable_name(var.self); + statement(variable_decl(var), ";"); + + // "Real" workgroup variables in compute shaders needs extra caretaking. + // They need to be initialized with an extra routine as they come in arbitrary form. + if (var.storage == StorageClassWorkgroup && var.initializer) + emit_workgroup_initialization(var); + + var.deferred_declaration = false; + } + else if (var.storage == StorageClassPrivate) + { + // These variables will not have had their CFG usage analyzed, so move it to the entry block. + // Comes from MSL which can push global variables as local variables in main function. + // We could just declare them right now, but we would miss out on an important initialization case which is + // LUT declaration in MSL. + // If we don't declare the variable when it is assigned we're forced to go through a helper function + // which copies elements one by one. + add_local_variable_name(var.self); + + if (var.initializer) + { + statement(variable_decl(var), ";"); + var.deferred_declaration = false; + } + else + { + auto &dominated = entry_block.dominated_variables; + if (find(begin(dominated), end(dominated), var.self) == end(dominated)) + entry_block.dominated_variables.push_back(var.self); + var.deferred_declaration = true; + } + } + else if (var.storage == StorageClassFunction && var.remapped_variable && var.static_expression) + { + // No need to declare this variable, it has a static expression. + var.deferred_declaration = false; + } + else if (expression_is_lvalue(v)) + { + add_local_variable_name(var.self); + + // Loop variables should never be declared early, they are explicitly emitted in a loop. + if (var.initializer && !var.loop_variable) + statement(variable_decl_function_local(var), ";"); + else + { + // Don't declare variable until first use to declutter the GLSL output quite a lot. + // If we don't touch the variable before first branch, + // declare it then since we need variable declaration to be in top scope. + var.deferred_declaration = true; + } + } + else + { + // HACK: SPIR-V in older glslang output likes to use samplers and images as local variables, but GLSL does not allow this. + // For these types (non-lvalue), we enforce forwarding through a shadowed variable. + // This means that when we OpStore to these variables, we just write in the expression ID directly. + // This breaks any kind of branching, since the variable must be statically assigned. + // Branching on samplers and images would be pretty much impossible to fake in GLSL. + var.statically_assigned = true; + } + + var.loop_variable_enable = false; + + // Loop variables are never declared outside their for-loop, so block any implicit declaration. + if (var.loop_variable) + { + var.deferred_declaration = false; + // Need to reset the static expression so we can fallback to initializer if need be. + var.static_expression = 0; + } + } + + // Enforce declaration order for regression testing purposes. + for (auto &block_id : func.blocks) + { + auto &block = get(block_id); + sort(begin(block.dominated_variables), end(block.dominated_variables)); + } + + for (auto &line : current_function->fixup_hooks_in) + line(); + + emit_block_chain(entry_block); + + end_scope(); + processing_entry_point = false; + statement(""); + + // Make sure deferred declaration state for local variables is cleared when we are done with function. + // We risk declaring Private/Workgroup variables in places we are not supposed to otherwise. + for (auto &v : func.local_variables) + { + auto &var = get(v); + var.deferred_declaration = false; + } +} + +void CompilerGLSL::emit_fixup() +{ + if (is_vertex_like_shader()) + { + if (options.vertex.fixup_clipspace) + { + const char *suffix = backend.float_literal_suffix ? "f" : ""; + statement("gl_Position.z = 2.0", suffix, " * gl_Position.z - gl_Position.w;"); + } + + if (options.vertex.flip_vert_y) + statement("gl_Position.y = -gl_Position.y;"); + } +} + +void CompilerGLSL::emit_workgroup_initialization(const SPIRVariable &) +{ +} + +void CompilerGLSL::flush_phi(BlockID from, BlockID to) +{ + auto &child = get(to); + if (child.ignore_phi_from_block == from) + return; + + unordered_set temporary_phi_variables; + + for (auto itr = begin(child.phi_variables); itr != end(child.phi_variables); ++itr) + { + auto &phi = *itr; + + if (phi.parent == from) + { + auto &var = get(phi.function_variable); + + // A Phi variable might be a loop variable, so flush to static expression. + if (var.loop_variable && !var.loop_variable_enable) + var.static_expression = phi.local_variable; + else + { + flush_variable_declaration(phi.function_variable); + + // Check if we are going to write to a Phi variable that another statement will read from + // as part of another Phi node in our target block. + // For this case, we will need to copy phi.function_variable to a temporary, and use that for future reads. + // This is judged to be extremely rare, so deal with it here using a simple, but suboptimal algorithm. + bool need_saved_temporary = + find_if(itr + 1, end(child.phi_variables), [&](const SPIRBlock::Phi &future_phi) -> bool { + return future_phi.local_variable == ID(phi.function_variable) && future_phi.parent == from; + }) != end(child.phi_variables); + + if (need_saved_temporary) + { + // Need to make sure we declare the phi variable with a copy at the right scope. + // We cannot safely declare a temporary here since we might be inside a continue block. + if (!var.allocate_temporary_copy) + { + var.allocate_temporary_copy = true; + force_recompile(); + } + statement("_", phi.function_variable, "_copy", " = ", to_name(phi.function_variable), ";"); + temporary_phi_variables.insert(phi.function_variable); + } + + // This might be called in continue block, so make sure we + // use this to emit ESSL 1.0 compliant increments/decrements. + auto lhs = to_expression(phi.function_variable); + + string rhs; + if (temporary_phi_variables.count(phi.local_variable)) + rhs = join("_", phi.local_variable, "_copy"); + else + rhs = to_pointer_expression(phi.local_variable); + + if (!optimize_read_modify_write(get(var.basetype), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + } + + register_write(phi.function_variable); + } + } +} + +void CompilerGLSL::branch_to_continue(BlockID from, BlockID to) +{ + auto &to_block = get(to); + if (from == to) + return; + + assert(is_continue(to)); + if (to_block.complex_continue) + { + // Just emit the whole block chain as is. + auto usage_counts = expression_usage_counts; + + emit_block_chain(to_block); + + // Expression usage counts are moot after returning from the continue block. + expression_usage_counts = usage_counts; + } + else + { + auto &from_block = get(from); + bool outside_control_flow = false; + uint32_t loop_dominator = 0; + + // FIXME: Refactor this to not use the old loop_dominator tracking. + if (from_block.merge_block) + { + // If we are a loop header, we don't set the loop dominator, + // so just use "self" here. + loop_dominator = from; + } + else if (from_block.loop_dominator != BlockID(SPIRBlock::NoDominator)) + { + loop_dominator = from_block.loop_dominator; + } + + if (loop_dominator != 0) + { + auto &cfg = get_cfg_for_current_function(); + + // For non-complex continue blocks, we implicitly branch to the continue block + // by having the continue block be part of the loop header in for (; ; continue-block). + outside_control_flow = cfg.node_terminates_control_flow_in_sub_graph(loop_dominator, from); + } + + // Some simplification for for-loops. We always end up with a useless continue; + // statement since we branch to a loop block. + // Walk the CFG, if we unconditionally execute the block calling continue assuming we're in the loop block, + // we can avoid writing out an explicit continue statement. + // Similar optimization to return statements if we know we're outside flow control. + if (!outside_control_flow) + statement("continue;"); + } +} + +void CompilerGLSL::branch(BlockID from, BlockID to) +{ + flush_phi(from, to); + flush_control_dependent_expressions(from); + + bool to_is_continue = is_continue(to); + + // This is only a continue if we branch to our loop dominator. + if ((ir.block_meta[to] & ParsedIR::BLOCK_META_LOOP_HEADER_BIT) != 0 && get(from).loop_dominator == to) + { + // This can happen if we had a complex continue block which was emitted. + // Once the continue block tries to branch to the loop header, just emit continue; + // and end the chain here. + statement("continue;"); + } + else if (from != to && is_break(to)) + { + // We cannot break to ourselves, so check explicitly for from != to. + // This case can trigger if a loop header is all three of these things: + // - Continue block + // - Loop header + // - Break merge target all at once ... + + // Very dirty workaround. + // Switch constructs are able to break, but they cannot break out of a loop at the same time, + // yet SPIR-V allows it. + // Only sensible solution is to make a ladder variable, which we declare at the top of the switch block, + // write to the ladder here, and defer the break. + // The loop we're breaking out of must dominate the switch block, or there is no ladder breaking case. + if (is_loop_break(to)) + { + for (size_t n = current_emitting_switch_stack.size(); n; n--) + { + auto *current_emitting_switch = current_emitting_switch_stack[n - 1]; + + if (current_emitting_switch && + current_emitting_switch->loop_dominator != BlockID(SPIRBlock::NoDominator) && + get(current_emitting_switch->loop_dominator).merge_block == to) + { + if (!current_emitting_switch->need_ladder_break) + { + force_recompile(); + current_emitting_switch->need_ladder_break = true; + } + + statement("_", current_emitting_switch->self, "_ladder_break = true;"); + } + else + break; + } + } + statement("break;"); + } + else if (to_is_continue || from == to) + { + // For from == to case can happen for a do-while loop which branches into itself. + // We don't mark these cases as continue blocks, but the only possible way to branch into + // ourselves is through means of continue blocks. + + // If we are merging to a continue block, there is no need to emit the block chain for continue here. + // We can branch to the continue block after we merge execution. + + // Here we make use of structured control flow rules from spec: + // 2.11: - the merge block declared by a header block cannot be a merge block declared by any other header block + // - each header block must strictly dominate its merge block, unless the merge block is unreachable in the CFG + // If we are branching to a merge block, we must be inside a construct which dominates the merge block. + auto &block_meta = ir.block_meta[to]; + bool branching_to_merge = + (block_meta & (ParsedIR::BLOCK_META_SELECTION_MERGE_BIT | ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT | + ParsedIR::BLOCK_META_LOOP_MERGE_BIT)) != 0; + if (!to_is_continue || !branching_to_merge) + branch_to_continue(from, to); + } + else if (!is_conditional(to)) + emit_block_chain(get(to)); + + // It is important that we check for break before continue. + // A block might serve two purposes, a break block for the inner scope, and + // a continue block in the outer scope. + // Inner scope always takes precedence. +} + +void CompilerGLSL::branch(BlockID from, uint32_t cond, BlockID true_block, BlockID false_block) +{ + auto &from_block = get(from); + BlockID merge_block = from_block.merge == SPIRBlock::MergeSelection ? from_block.next_block : BlockID(0); + + // If we branch directly to our selection merge target, we don't need a code path. + bool true_block_needs_code = true_block != merge_block || flush_phi_required(from, true_block); + bool false_block_needs_code = false_block != merge_block || flush_phi_required(from, false_block); + + if (!true_block_needs_code && !false_block_needs_code) + return; + + // We might have a loop merge here. Only consider selection flattening constructs. + // Loop hints are handled explicitly elsewhere. + if (from_block.hint == SPIRBlock::HintFlatten || from_block.hint == SPIRBlock::HintDontFlatten) + emit_block_hints(from_block); + + if (true_block_needs_code) + { + statement("if (", to_expression(cond), ")"); + begin_scope(); + branch(from, true_block); + end_scope(); + + if (false_block_needs_code) + { + statement("else"); + begin_scope(); + branch(from, false_block); + end_scope(); + } + } + else if (false_block_needs_code) + { + // Only need false path, use negative conditional. + statement("if (!", to_enclosed_expression(cond), ")"); + begin_scope(); + branch(from, false_block); + end_scope(); + } +} + +// FIXME: This currently cannot handle complex continue blocks +// as in do-while. +// This should be seen as a "trivial" continue block. +string CompilerGLSL::emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block) +{ + auto *block = &get(continue_block); + + // While emitting the continue block, declare_temporary will check this + // if we have to emit temporaries. + current_continue_block = block; + + SmallVector statements; + + // Capture all statements into our list. + auto *old = redirect_statement; + redirect_statement = &statements; + + // Stamp out all blocks one after each other. + while ((ir.block_meta[block->self] & ParsedIR::BLOCK_META_LOOP_HEADER_BIT) == 0) + { + // Write out all instructions we have in this block. + emit_block_instructions(*block); + + // For plain branchless for/while continue blocks. + if (block->next_block) + { + flush_phi(continue_block, block->next_block); + block = &get(block->next_block); + } + // For do while blocks. The last block will be a select block. + else if (block->true_block && follow_true_block) + { + flush_phi(continue_block, block->true_block); + block = &get(block->true_block); + } + else if (block->false_block && follow_false_block) + { + flush_phi(continue_block, block->false_block); + block = &get(block->false_block); + } + else + { + SPIRV_CROSS_THROW("Invalid continue block detected!"); + } + } + + // Restore old pointer. + redirect_statement = old; + + // Somewhat ugly, strip off the last ';' since we use ',' instead. + // Ideally, we should select this behavior in statement(). + for (auto &s : statements) + { + if (!s.empty() && s.back() == ';') + s.erase(s.size() - 1, 1); + } + + current_continue_block = nullptr; + return merge(statements); +} + +// Loop variable with OpUndef init: zero-init instead of leaving uninitialized (FXC X4555/X4000). +std::string CompilerGLSL::undef_loop_variable_initializer_suffix(const SPIRVariable &var) +{ + if (!backend.requires_phi_undef_zero_init) + return ""; + + uint32_t expr = var.static_expression; + if (expr == 0 || ir.ids[expr].get_type() != TypeUndef) + return ""; + + auto &type = get(var.basetype); + if (!type_can_zero_initialize(type)) + return ""; + + return join(" = ", to_zero_initialized_expression(var.basetype)); +} + +void CompilerGLSL::emit_while_loop_initializers(const SPIRBlock &block) +{ + // While loops do not take initializers, so declare all of them outside. + for (auto &loop_var : block.loop_variables) + { + auto &var = get(loop_var); + statement(variable_decl(var), undef_loop_variable_initializer_suffix(var), ";"); + } +} + +string CompilerGLSL::emit_for_loop_initializers(const SPIRBlock &block) +{ + if (block.loop_variables.empty()) + return ""; + + bool same_types = for_loop_initializers_are_same_type(block); + // We can only declare for loop initializers if all variables are of same type. + // If we cannot do this, declare individual variables before the loop header. + + // We might have a loop variable candidate which was not assigned to for some reason. + uint32_t missing_initializers = 0; + for (auto &variable : block.loop_variables) + { + uint32_t expr = get(variable).static_expression; + + // Sometimes loop variables are initialized with OpUndef, but we can just declare + // a plain variable without initializer in this case. + if (expr == 0 || ir.ids[expr].get_type() == TypeUndef) + missing_initializers++; + } + + if (block.loop_variables.size() == 1 && missing_initializers == 0) + { + return variable_decl(get(block.loop_variables.front())); + } + else if (!same_types || missing_initializers == uint32_t(block.loop_variables.size())) + { + for (auto &loop_var : block.loop_variables) + { + auto &var = get(loop_var); + statement(variable_decl(var), undef_loop_variable_initializer_suffix(var), ";"); + } + return ""; + } + else + { + // We have a mix of loop variables, either ones with a clear initializer, or ones without. + // Separate the two streams. + string expr; + + for (auto &loop_var : block.loop_variables) + { + auto &var_for_undef = get(loop_var); + uint32_t static_expr = var_for_undef.static_expression; + if (static_expr == 0 || ir.ids[static_expr].get_type() == TypeUndef) + { + statement(variable_decl(var_for_undef), undef_loop_variable_initializer_suffix(var_for_undef), ";"); + } + else + { + auto &var = get(loop_var); + auto &type = get_variable_data_type(var); + if (expr.empty()) + { + // For loop initializers are of the form (block.true_block), get(block.merge_block))) + condition = join("!", enclose_expression(condition)); + + statement("while (", condition, ")"); + break; + } + + default: + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + + begin_scope(); + return true; + } + else + { + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + } + else if (method == SPIRBlock::MergeToDirectForLoop) + { + auto &child = get(block.next_block); + + // This block may be a dominating block, so make sure we flush undeclared variables before building the for loop header. + flush_undeclared_variables(child); + + uint32_t current_count = statement_count; + + // If we're trying to create a true for loop, + // we need to make sure that all opcodes before branch statement do not actually emit any code. + // We can then take the condition expression and create a for (; cond ; ) { body; } structure instead. + emit_block_instructions_with_masked_debug(child); + + bool condition_is_temporary = forced_temporaries.find(child.condition) == end(forced_temporaries); + + bool flushes_phi = flush_phi_required(child.self, child.true_block) || + flush_phi_required(child.self, child.false_block); + + if (!flushes_phi && current_count == statement_count && condition_is_temporary) + { + uint32_t target_block = child.true_block; + + switch (continue_type) + { + case SPIRBlock::ForLoop: + { + // Important that we do this in this order because + // emitting the continue block can invalidate the condition expression. + auto initializer = emit_for_loop_initializers(block); + auto condition = to_expression(child.condition); + + // Condition might have to be inverted. + if (execution_is_noop(get(child.true_block), get(block.merge_block))) + { + condition = join("!", enclose_expression(condition)); + target_block = child.false_block; + } + + auto continue_block = emit_continue_block(block.continue_block, false, false); + emit_block_hints(block); + statement("for (", initializer, "; ", condition, "; ", continue_block, ")"); + break; + } + + case SPIRBlock::WhileLoop: + { + emit_while_loop_initializers(block); + emit_block_hints(block); + + auto condition = to_expression(child.condition); + // Condition might have to be inverted. + if (execution_is_noop(get(child.true_block), get(block.merge_block))) + { + condition = join("!", enclose_expression(condition)); + target_block = child.false_block; + } + + statement("while (", condition, ")"); + break; + } + + default: + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + + begin_scope(); + branch(child.self, target_block); + return true; + } + else + { + block.disable_block_optimization = true; + force_recompile(); + begin_scope(); // We'll see an end_scope() later. + return false; + } + } + else + return false; +} + +void CompilerGLSL::flush_undeclared_variables(SPIRBlock &block) +{ + for (auto &v : block.dominated_variables) + flush_variable_declaration(v); +} + +void CompilerGLSL::emit_hoisted_temporaries(SmallVector> &temporaries) +{ + // If we need to force temporaries for certain IDs due to continue blocks, do it before starting loop header. + // Need to sort these to ensure that reference output is stable. + sort(begin(temporaries), end(temporaries), + [](const pair &a, const pair &b) { return a.second < b.second; }); + + for (auto &tmp : temporaries) + { + auto &type = get(tmp.first); + + // There are some rare scenarios where we are asked to declare pointer types as hoisted temporaries. + // This should be ignored unless we're doing actual variable pointers and backend supports it. + // Access chains cannot normally be lowered to temporaries in GLSL and HLSL. + if (type.pointer && !backend.native_pointers) + continue; + + add_local_variable_name(tmp.second); + auto &flags = get_decoration_bitset(tmp.second); + + // Not all targets support pointer literals, so don't bother with that case. + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(tmp.first)); + + statement(flags_to_qualifiers_glsl(type, tmp.second, flags), variable_decl(type, to_name(tmp.second)), initializer, ";"); + + hoisted_temporaries.insert(tmp.second); + forced_temporaries.insert(tmp.second); + + // The temporary might be read from before it's assigned, set up the expression now. + set(tmp.second, to_name(tmp.second), tmp.first, true); + + // If we have hoisted temporaries in multi-precision contexts, emit that here too ... + // We will not be able to analyze hoisted-ness for dependent temporaries that we hallucinate here. + auto mirrored_precision_itr = temporary_to_mirror_precision_alias.find(tmp.second); + if (mirrored_precision_itr != temporary_to_mirror_precision_alias.end()) + { + uint32_t mirror_id = mirrored_precision_itr->second; + auto &mirror_flags = get_decoration_bitset(mirror_id); + statement(flags_to_qualifiers_glsl(type, mirror_id, mirror_flags), + variable_decl(type, to_name(mirror_id)), + initializer, ";"); + // The temporary might be read from before it's assigned, set up the expression now. + set(mirror_id, to_name(mirror_id), tmp.first, true); + hoisted_temporaries.insert(mirror_id); + } + } +} + +void CompilerGLSL::emit_block_chain(SPIRBlock &block) +{ + SmallVector cleanup_stack; + BlockID next_block = emit_block_chain_inner(block); + + while (next_block != 0) + { + cleanup_stack.push_back(next_block); + next_block = emit_block_chain_inner(get(next_block)); + } + + while (!cleanup_stack.empty()) + { + emit_block_chain_cleanup(get(cleanup_stack.back())); + cleanup_stack.pop_back(); + } + + emit_block_chain_cleanup(block); +} + +BlockID CompilerGLSL::emit_block_chain_inner(SPIRBlock &block) +{ + bool select_branch_to_true_block = false; + bool select_branch_to_false_block = false; + bool skip_direct_branch = false; + bool emitted_loop_header_variables = false; + bool force_complex_continue_block = false; + ValueSaver loop_level_saver(current_loop_level); + + if (block.merge == SPIRBlock::MergeLoop) + add_loop_level(); + + // If we're emitting PHI variables with precision aliases, we have to emit them as hoisted temporaries. + for (auto var_id : block.dominated_variables) + { + auto &var = get(var_id); + if (var.phi_variable) + { + auto mirrored_precision_itr = temporary_to_mirror_precision_alias.find(var_id); + if (mirrored_precision_itr != temporary_to_mirror_precision_alias.end() && + find_if(block.declare_temporary.begin(), block.declare_temporary.end(), + [mirrored_precision_itr](const std::pair &p) { + return p.second == mirrored_precision_itr->second; + }) == block.declare_temporary.end()) + { + block.declare_temporary.push_back({ var.basetype, mirrored_precision_itr->second }); + } + } + } + + emit_hoisted_temporaries(block.declare_temporary); + + SPIRBlock::ContinueBlockType continue_type = SPIRBlock::ContinueNone; + if (block.continue_block) + { + continue_type = continue_block_type(get(block.continue_block)); + // If we know we cannot emit a loop, mark the block early as a complex loop so we don't force unnecessary recompiles. + if (continue_type == SPIRBlock::ComplexLoop) + block.complex_continue = true; + } + + // If we have loop variables, stop masking out access to the variable now. + for (auto var_id : block.loop_variables) + { + auto &var = get(var_id); + var.loop_variable_enable = true; + // We're not going to declare the variable directly, so emit a copy here. + emit_variable_temporary_copies(var); + } + + // Remember deferred declaration state. We will restore it before returning. + assert(block.rearm_dominated_variables.empty()); + block.rearm_dominated_variables.resize(block.dominated_variables.size()); + for (size_t i = 0; i < block.dominated_variables.size(); i++) + { + uint32_t var_id = block.dominated_variables[i]; + auto &var = get(var_id); + block.rearm_dominated_variables[i] = var.deferred_declaration; + } + + // This is the method often used by spirv-opt to implement loops. + // The loop header goes straight into the continue block. + // However, don't attempt this on ESSL 1.0, because if a loop variable is used in a continue block, + // it *MUST* be used in the continue block. This loop method will not work. + if (!is_legacy_es() && block_is_loop_candidate(block, SPIRBlock::MergeToSelectContinueForLoop)) + { + flush_undeclared_variables(block); + if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectContinueForLoop)) + { + if (execution_is_noop(get(block.true_block), get(block.merge_block))) + select_branch_to_false_block = true; + else + select_branch_to_true_block = true; + + emitted_loop_header_variables = true; + force_complex_continue_block = true; + } + } + // This is the older loop behavior in glslang which branches to loop body directly from the loop header. + else if (block_is_loop_candidate(block, SPIRBlock::MergeToSelectForLoop)) + { + flush_undeclared_variables(block); + if (attempt_emit_loop_header(block, SPIRBlock::MergeToSelectForLoop)) + { + // The body of while, is actually just the true (or false) block, so always branch there unconditionally. + if (execution_is_noop(get(block.true_block), get(block.merge_block))) + select_branch_to_false_block = true; + else + select_branch_to_true_block = true; + + emitted_loop_header_variables = true; + } + } + // This is the newer loop behavior in glslang which branches from Loop header directly to + // a new block, which in turn has a OpBranchSelection without a selection merge. + else if (block_is_loop_candidate(block, SPIRBlock::MergeToDirectForLoop)) + { + flush_undeclared_variables(block); + if (attempt_emit_loop_header(block, SPIRBlock::MergeToDirectForLoop)) + { + skip_direct_branch = true; + emitted_loop_header_variables = true; + } + } + else if (continue_type == SPIRBlock::DoWhileLoop) + { + flush_undeclared_variables(block); + emit_while_loop_initializers(block); + emitted_loop_header_variables = true; + // We have some temporaries where the loop header is the dominator. + // We risk a case where we have code like: + // for (;;) { create-temporary; break; } consume-temporary; + // so force-declare temporaries here. + emit_hoisted_temporaries(block.potential_declare_temporary); + statement("do"); + begin_scope(); + + emit_block_instructions(block); + } + else if (block.merge == SPIRBlock::MergeLoop) + { + flush_undeclared_variables(block); + emit_while_loop_initializers(block); + emitted_loop_header_variables = true; + + // We have a generic loop without any distinguishable pattern like for, while or do while. + get(block.continue_block).complex_continue = true; + continue_type = SPIRBlock::ComplexLoop; + + // We have some temporaries where the loop header is the dominator. + // We risk a case where we have code like: + // for (;;) { create-temporary; break; } consume-temporary; + // so force-declare temporaries here. + emit_hoisted_temporaries(block.potential_declare_temporary); + emit_block_hints(block); + statement("for (;;)"); + begin_scope(); + + emit_block_instructions(block); + } + else + { + emit_block_instructions(block); + } + + // If we didn't successfully emit a loop header and we had loop variable candidates, we have a problem + // as writes to said loop variables might have been masked out, we need a recompile. + if (!emitted_loop_header_variables && !block.loop_variables.empty()) + { + force_recompile_guarantee_forward_progress(); + for (auto var : block.loop_variables) + get(var).loop_variable = false; + block.loop_variables.clear(); + } + + flush_undeclared_variables(block); + bool emit_next_block = true; + + // Handle end of block. + switch (block.terminator) + { + case SPIRBlock::Direct: + // True when emitting complex continue block. + if (block.loop_dominator == block.next_block) + { + branch(block.self, block.next_block); + emit_next_block = false; + } + // True if MergeToDirectForLoop succeeded. + else if (skip_direct_branch) + emit_next_block = false; + else if (is_continue(block.next_block) || is_break(block.next_block) || is_conditional(block.next_block)) + { + branch(block.self, block.next_block); + emit_next_block = false; + } + break; + + case SPIRBlock::Select: + // True if MergeToSelectForLoop or MergeToSelectContinueForLoop succeeded. + if (select_branch_to_true_block) + { + if (force_complex_continue_block) + { + assert(block.true_block == block.continue_block); + + // We're going to emit a continue block directly here, so make sure it's marked as complex. + auto &complex_continue = get(block.continue_block).complex_continue; + bool old_complex = complex_continue; + complex_continue = true; + branch(block.self, block.true_block); + complex_continue = old_complex; + } + else + branch(block.self, block.true_block); + } + else if (select_branch_to_false_block) + { + if (force_complex_continue_block) + { + assert(block.false_block == block.continue_block); + + // We're going to emit a continue block directly here, so make sure it's marked as complex. + auto &complex_continue = get(block.continue_block).complex_continue; + bool old_complex = complex_continue; + complex_continue = true; + branch(block.self, block.false_block); + complex_continue = old_complex; + } + else + branch(block.self, block.false_block); + } + else + branch(block.self, block.condition, block.true_block, block.false_block); + break; + + case SPIRBlock::MultiSelect: + { + auto &type = expression_type(block.condition); + bool unsigned_case = type.basetype == SPIRType::UInt || type.basetype == SPIRType::UShort || + type.basetype == SPIRType::UByte || type.basetype == SPIRType::UInt64; + + if (block.merge == SPIRBlock::MergeNone) + SPIRV_CROSS_THROW("Switch statement is not structured"); + + if (!backend.support_64bit_switch && (type.basetype == SPIRType::UInt64 || type.basetype == SPIRType::Int64)) + { + // SPIR-V spec suggests this is allowed, but we cannot support it in higher level languages. + SPIRV_CROSS_THROW("Cannot use 64-bit switch selectors."); + } + + const char *label_suffix = ""; + if (type.basetype == SPIRType::UInt && backend.uint32_t_literal_suffix) + label_suffix = "u"; + else if (type.basetype == SPIRType::Int64 && backend.support_64bit_switch) + label_suffix = "l"; + else if (type.basetype == SPIRType::UInt64 && backend.support_64bit_switch) + label_suffix = "ul"; + else if (type.basetype == SPIRType::UShort) + label_suffix = backend.uint16_t_literal_suffix; + else if (type.basetype == SPIRType::Short) + label_suffix = backend.int16_t_literal_suffix; + + current_emitting_switch_stack.push_back(&block); + + if (block.need_ladder_break) + statement("bool _", block.self, "_ladder_break = false;"); + + // Find all unique case constructs. + unordered_map> case_constructs; + SmallVector block_declaration_order; + SmallVector literals_to_merge; + + // If a switch case branches to the default block for some reason, we can just remove that literal from consideration + // and let the default: block handle it. + // 2.11 in SPIR-V spec states that for fall-through cases, there is a very strict declaration order which we can take advantage of here. + // We only need to consider possible fallthrough if order[i] branches to order[i + 1]. + auto &cases = get_case_list(block); + for (auto &c : cases) + { + if (c.block != block.next_block && c.block != block.default_block) + { + if (!case_constructs.count(c.block)) + block_declaration_order.push_back(c.block); + case_constructs[c.block].push_back(c.value); + } + else if (c.block == block.next_block && block.default_block != block.next_block) + { + // We might have to flush phi inside specific case labels. + // If we can piggyback on default:, do so instead. + literals_to_merge.push_back(c.value); + } + } + + // Empty literal array -> default. + if (block.default_block != block.next_block) + { + auto &default_block = get(block.default_block); + + // We need to slide in the default block somewhere in this chain + // if there are fall-through scenarios since the default is declared separately in OpSwitch. + // Only consider trivial fall-through cases here. + size_t num_blocks = block_declaration_order.size(); + bool injected_block = false; + + for (size_t i = 0; i < num_blocks; i++) + { + auto &case_block = get(block_declaration_order[i]); + if (execution_is_direct_branch(case_block, default_block)) + { + // Fallthrough to default block, we must inject the default block here. + block_declaration_order.insert(begin(block_declaration_order) + i + 1, block.default_block); + injected_block = true; + break; + } + else if (execution_is_direct_branch(default_block, case_block)) + { + // Default case is falling through to another case label, we must inject the default block here. + block_declaration_order.insert(begin(block_declaration_order) + i, block.default_block); + injected_block = true; + break; + } + } + + // Order does not matter. + if (!injected_block) + block_declaration_order.push_back(block.default_block); + else if (is_legacy_es()) + SPIRV_CROSS_THROW("Default case label fallthrough to other case label is not supported in ESSL 1.0."); + + case_constructs[block.default_block] = {}; + } + + size_t num_blocks = block_declaration_order.size(); + + const auto to_case_label = [](uint64_t literal, uint32_t width, bool is_unsigned_case) -> string + { + if (is_unsigned_case) + return convert_to_string(literal); + + // For smaller cases, the literals are compiled as 32 bit wide + // literals so we don't need to care for all sizes specifically. + if (width <= 32) + { + return convert_to_string(int64_t(int32_t(literal))); + } + + return convert_to_string(int64_t(literal)); + }; + + const auto to_legacy_case_label = [&](uint32_t condition, const SmallVector &labels, + const char *suffix) -> string { + string ret; + size_t count = labels.size(); + for (size_t i = 0; i < count; i++) + { + if (i) + ret += " || "; + ret += join(count > 1 ? "(" : "", to_enclosed_expression(condition), " == ", labels[i], suffix, + count > 1 ? ")" : ""); + } + return ret; + }; + + // We need to deal with a complex scenario for OpPhi. If we have case-fallthrough and Phi in the picture, + // we need to flush phi nodes outside the switch block in a branch, + // and skip any Phi handling inside the case label to make fall-through work as expected. + // This kind of code-gen is super awkward and it's a last resort. Normally we would want to handle this + // inside the case label if at all possible. + for (size_t i = 1; backend.support_case_fallthrough && i < num_blocks; i++) + { + if (flush_phi_required(block.self, block_declaration_order[i]) && + flush_phi_required(block_declaration_order[i - 1], block_declaration_order[i])) + { + uint32_t target_block = block_declaration_order[i]; + + // Make sure we flush Phi, it might have been marked to be ignored earlier. + get(target_block).ignore_phi_from_block = 0; + + auto &literals = case_constructs[target_block]; + + if (literals.empty()) + { + // Oh boy, gotta make a complete negative test instead! o.o + // Find all possible literals that would *not* make us enter the default block. + // If none of those literals match, we flush Phi ... + SmallVector conditions; + for (size_t j = 0; j < num_blocks; j++) + { + auto &negative_literals = case_constructs[block_declaration_order[j]]; + for (auto &case_label : negative_literals) + conditions.push_back(join(to_enclosed_expression(block.condition), + " != ", to_case_label(case_label, type.width, unsigned_case))); + } + + statement("if (", merge(conditions, " && "), ")"); + begin_scope(); + flush_phi(block.self, target_block); + end_scope(); + } + else + { + SmallVector conditions; + conditions.reserve(literals.size()); + for (auto &case_label : literals) + conditions.push_back(join(to_enclosed_expression(block.condition), + " == ", to_case_label(case_label, type.width, unsigned_case))); + statement("if (", merge(conditions, " || "), ")"); + begin_scope(); + flush_phi(block.self, target_block); + end_scope(); + } + + // Mark the block so that we don't flush Phi from header to case label. + get(target_block).ignore_phi_from_block = block.self; + } + } + + // If there is only one default block, and no cases, this is a case where SPIRV-opt decided to emulate + // non-structured exits with the help of a switch block. + // This is buggy on FXC, so just emit the logical equivalent of a do { } while(false), which is more idiomatic. + bool block_like_switch = cases.empty(); + + // If this is true, the switch is completely meaningless, and we should just avoid it. + bool collapsed_switch = block_like_switch && block.default_block == block.next_block; + + if (!collapsed_switch) + { + if (block_like_switch || is_legacy()) + { + // ESSL 1.0 is not guaranteed to support do/while. + if (is_legacy_es()) + { + uint32_t counter = statement_count; + statement("for (int spvDummy", counter, " = 0; spvDummy", counter, " < 1; spvDummy", counter, + "++)"); + } + else + statement("do"); + } + else + { + emit_block_hints(block); + statement("switch (", to_unpacked_expression(block.condition), ")"); + } + begin_scope(); + } + + for (size_t i = 0; i < num_blocks; i++) + { + uint32_t target_block = block_declaration_order[i]; + auto &literals = case_constructs[target_block]; + + if (literals.empty()) + { + // Default case. + if (!block_like_switch) + { + if (is_legacy()) + statement("else"); + else + statement("default:"); + } + } + else + { + if (is_legacy()) + { + statement((i ? "else " : ""), "if (", to_legacy_case_label(block.condition, literals, label_suffix), + ")"); + } + else + { + for (auto &case_literal : literals) + { + // The case label value must be sign-extended properly in SPIR-V, so we can assume 32-bit values here. + statement("case ", to_case_label(case_literal, type.width, unsigned_case), label_suffix, ":"); + } + } + } + + auto &case_block = get(target_block); + if (backend.support_case_fallthrough && i + 1 < num_blocks && + execution_is_direct_branch(case_block, get(block_declaration_order[i + 1]))) + { + // We will fall through here, so just terminate the block chain early. + // We still need to deal with Phi potentially. + // No need for a stack-like thing here since we only do fall-through when there is a + // single trivial branch to fall-through target.. + current_emitting_switch_fallthrough = true; + } + else + current_emitting_switch_fallthrough = false; + + if (!block_like_switch) + begin_scope(); + branch(block.self, target_block); + if (!block_like_switch) + end_scope(); + + current_emitting_switch_fallthrough = false; + } + + // Might still have to flush phi variables if we branch from loop header directly to merge target. + // This is supposed to emit all cases where we branch from header to merge block directly. + // There are two main scenarios where cannot rely on default fallthrough. + // - There is an explicit default: label already. + // In this case, literals_to_merge need to form their own "default" case, so that we avoid executing that block. + // - Header -> Merge requires flushing PHI. In this case, we need to collect all cases and flush PHI there. + bool header_merge_requires_phi = flush_phi_required(block.self, block.next_block); + bool need_fallthrough_block = block.default_block == block.next_block || !literals_to_merge.empty(); + if (!collapsed_switch && ((header_merge_requires_phi && need_fallthrough_block) || !literals_to_merge.empty())) + { + for (auto &case_literal : literals_to_merge) + statement("case ", to_case_label(case_literal, type.width, unsigned_case), label_suffix, ":"); + + if (block.default_block == block.next_block) + { + if (is_legacy()) + statement("else"); + else + statement("default:"); + } + + begin_scope(); + flush_phi(block.self, block.next_block); + statement("break;"); + end_scope(); + } + + if (!collapsed_switch) + { + if ((block_like_switch || is_legacy()) && !is_legacy_es()) + end_scope_decl("while(false)"); + else + end_scope(); + } + else + flush_phi(block.self, block.next_block); + + if (block.need_ladder_break) + { + statement("if (_", block.self, "_ladder_break)"); + begin_scope(); + statement("break;"); + end_scope(); + } + + current_emitting_switch_stack.pop_back(); + break; + } + + case SPIRBlock::Return: + { + for (auto &line : current_function->fixup_hooks_out) + line(); + + if (processing_entry_point) + emit_fixup(); + + auto &cfg = get_cfg_for_current_function(); + + if (block.return_value) + { + auto &type = expression_type(block.return_value); + if (!type.array.empty() && !backend.can_return_array) + { + // If we cannot return arrays, we will have a special out argument we can write to instead. + // The backend is responsible for setting this up, and redirection the return values as appropriate. + if (ir.ids[block.return_value].get_type() != TypeUndef) + { + emit_array_copy("spvReturnValue", 0, block.return_value, StorageClassFunction, + get_expression_effective_storage_class(block.return_value)); + } + + if (!cfg.node_terminates_control_flow_in_sub_graph(current_function->entry_block, block.self) || + block.loop_dominator != BlockID(SPIRBlock::NoDominator)) + { + statement("return;"); + } + } + else + { + // OpReturnValue can return Undef, so don't emit anything for this case. + if (ir.ids[block.return_value].get_type() != TypeUndef) + statement("return ", to_unpacked_expression(block.return_value), ";"); + } + } + else if (!cfg.node_terminates_control_flow_in_sub_graph(current_function->entry_block, block.self) || + block.loop_dominator != BlockID(SPIRBlock::NoDominator)) + { + // If this block is the very final block and not called from control flow, + // we do not need an explicit return which looks out of place. Just end the function here. + // In the very weird case of for(;;) { return; } executing return is unconditional, + // but we actually need a return here ... + statement("return;"); + } + break; + } + + // If the Kill is terminating a block with a (probably synthetic) return value, emit a return value statement. + case SPIRBlock::Kill: + statement(backend.discard_literal, ";"); + if (block.return_value) + statement("return ", to_unpacked_expression(block.return_value), ";"); + break; + + case SPIRBlock::Unreachable: + { + // If the entry point ends with unreachable and has a return value, insert a return + // statement to avoid potential compiler errors from non-void functions without a return value. + if (block.return_value) + { + statement("return ", to_unpacked_expression(block.return_value), ";"); + break; + } + + // Avoid emitting false fallthrough, which can happen for + // if (cond) break; else discard; inside a case label. + // Discard is not always implementable as a terminator. + + auto &cfg = get_cfg_for_current_function(); + bool inner_dominator_is_switch = false; + ID id = block.self; + + while (id) + { + auto &iter_block = get(id); + if (iter_block.terminator == SPIRBlock::MultiSelect || + iter_block.merge == SPIRBlock::MergeLoop) + { + ID next_block = iter_block.merge == SPIRBlock::MergeLoop ? + iter_block.merge_block : iter_block.next_block; + bool outside_construct = next_block && cfg.find_common_dominator(next_block, block.self) == next_block; + if (!outside_construct) + { + inner_dominator_is_switch = iter_block.terminator == SPIRBlock::MultiSelect; + break; + } + } + + if (cfg.get_preceding_edges(id).empty()) + break; + + id = cfg.get_immediate_dominator(id); + } + + if (inner_dominator_is_switch) + statement("break; // unreachable workaround"); + + emit_next_block = false; + break; + } + + case SPIRBlock::IgnoreIntersection: + statement("ignoreIntersectionEXT;"); + break; + + case SPIRBlock::TerminateRay: + statement("terminateRayEXT;"); + break; + + case SPIRBlock::EmitMeshTasks: + emit_mesh_tasks(block); + break; + + default: + SPIRV_CROSS_THROW("Unimplemented block terminator."); + } + + BlockID trailing_block_id = 0; + + if (block.next_block && emit_next_block) + { + // If we hit this case, we're dealing with an unconditional branch, which means we will output + // that block after this. If we had selection merge, we already flushed phi variables. + if (block.merge != SPIRBlock::MergeSelection) + { + flush_phi(block.self, block.next_block); + + // For a direct branch, need to remember to invalidate expressions in the next linear block instead. + get(block.next_block).invalidate_expressions.clear(); + std::swap(get(block.next_block).invalidate_expressions, block.invalidate_expressions); + } + + // For switch fallthrough cases, we terminate the chain here, but we still need to handle Phi. + if (!current_emitting_switch_fallthrough) + { + // For merge selects we might have ignored the fact that a merge target + // could have been a break; or continue; + // We will need to deal with it here. + if (is_loop_break(block.next_block)) + { + // Cannot check for just break, because switch statements will also use break. + assert(block.merge == SPIRBlock::MergeSelection); + statement("break;"); + } + else if (is_continue(block.next_block)) + { + assert(block.merge == SPIRBlock::MergeSelection); + branch_to_continue(block.self, block.next_block); + } + else if (BlockID(block.self) != block.next_block) + { + // Recursing here is quite scary since it's quite easy to stack overflow if + // the SPIR-V is constructed a particular way. + // We have to simulate the tail call ourselves. + if (block.merge != SPIRBlock::MergeLoop) + trailing_block_id = block.next_block; + else + emit_block_chain(get(block.next_block)); + } + } + } + + if (block.merge == SPIRBlock::MergeLoop) + { + if (continue_type == SPIRBlock::DoWhileLoop) + { + // Make sure that we run the continue block to get the expressions set, but this + // should become an empty string. + // We have no fallbacks if we cannot forward everything to temporaries ... + const auto &continue_block = get(block.continue_block); + bool positive_test = execution_is_noop(get(continue_block.true_block), + get(continue_block.loop_dominator)); + + uint32_t current_count = statement_count; + auto statements = emit_continue_block(block.continue_block, positive_test, !positive_test); + if (statement_count != current_count) + { + // The DoWhile block has side effects, force ComplexLoop pattern next pass. + get(block.continue_block).complex_continue = true; + force_recompile(); + } + + // Might have to invert the do-while test here. + auto condition = to_expression(continue_block.condition); + if (!positive_test) + condition = join("!", enclose_expression(condition)); + + end_scope_decl(join("while (", condition, ")")); + } + else + end_scope(); + + loop_level_saver.release(); + + // We cannot break out of two loops at once, so don't check for break; here. + // Using block.self as the "from" block isn't quite right, but it has the same scope + // and dominance structure, so it's fine. + if (is_continue(block.merge_block)) + branch_to_continue(block.self, block.merge_block); + else + trailing_block_id = block.merge_block; + } + + return trailing_block_id; +} + +void CompilerGLSL::emit_block_chain_cleanup(SPIRBlock &block) +{ + // Forget about control dependent expressions now. + block.invalidate_expressions.clear(); + + // After we return, we must be out of scope, so if we somehow have to re-emit this block, + // re-declare variables if necessary. + // We only need one array here for rearm_dominated_variables, + // since it should be impossible for the same block to be remitted in the same chain twice. + assert(block.rearm_dominated_variables.size() == block.dominated_variables.size()); + for (size_t i = 0; i < block.dominated_variables.size(); i++) + { + uint32_t var = block.dominated_variables[i]; + get(var).deferred_declaration = block.rearm_dominated_variables[i]; + } + block.rearm_dominated_variables.clear(); + + // Just like for deferred declaration, we need to forget about loop variable enable + // if our block chain is reinstantiated later. + for (auto &var_id : block.loop_variables) + get(var_id).loop_variable_enable = false; +} + +void CompilerGLSL::begin_scope() +{ + statement("{"); + indent++; +} + +void CompilerGLSL::end_scope() +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("}"); +} + +void CompilerGLSL::end_scope(const string &trailer) +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("}", trailer); +} + +void CompilerGLSL::end_scope_decl() +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("};"); +} + +void CompilerGLSL::end_scope_decl(const string &decl) +{ + if (!indent) + SPIRV_CROSS_THROW("Popping empty indent stack."); + indent--; + statement("} ", decl, ";"); +} + +void CompilerGLSL::check_function_call_constraints(const uint32_t *args, uint32_t length) +{ + // If our variable is remapped, and we rely on type-remapping information as + // well, then we cannot pass the variable as a function parameter. + // Fixing this is non-trivial without stamping out variants of the same function, + // so for now warn about this and suggest workarounds instead. + for (uint32_t i = 0; i < length; i++) + { + auto *var = maybe_get(args[i]); + if (!var || !var->remapped_variable) + continue; + + auto &type = get(var->basetype); + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData) + { + SPIRV_CROSS_THROW("Tried passing a remapped subpassInput variable to a function. " + "This will not work correctly because type-remapping information is lost. " + "To workaround, please consider not passing the subpass input as a function parameter, " + "or use in/out variables instead which do not need type remapping information."); + } + } +} + +const Instruction *CompilerGLSL::get_next_instruction_in_block(const Instruction &instr) +{ + // FIXME: This is kind of hacky. There should be a cleaner way. + auto offset = uint32_t(&instr - current_emitting_block->ops.data()); + if ((offset + 1) < current_emitting_block->ops.size()) + return ¤t_emitting_block->ops[offset + 1]; + else + return nullptr; +} + +uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics) +{ + return semantics & (MemorySemanticsAtomicCounterMemoryMask | MemorySemanticsImageMemoryMask | + MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask | + MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask); +} + +bool CompilerGLSL::emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, StorageClass, StorageClass) +{ + string lhs; + if (expr) + lhs = expr; + else + lhs = to_expression(lhs_id); + + statement(lhs, " = ", to_expression(rhs_id), ";"); + return true; +} + +bool CompilerGLSL::unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id) +{ + if (!backend.force_gl_in_out_block) + return false; + // This path is only relevant for GL backends. + + auto *var = maybe_get(target_id); + if (!var || var->storage != StorageClassOutput) + return false; + + if (!is_builtin_variable(*var) || BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInSampleMask) + return false; + + auto &type = expression_type(source_id); + string array_expr; + if (type.array_size_literal.back()) + { + array_expr = convert_to_string(type.array.back()); + if (type.array.back() == 0) + SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array."); + } + else + array_expr = to_expression(type.array.back()); + + SPIRType target_type { OpTypeInt }; + target_type.basetype = SPIRType::Int; + + statement("for (int i = 0; i < int(", array_expr, "); i++)"); + begin_scope(); + statement(to_expression(target_id), "[i] = ", + bitcast_expression(target_type, type.basetype, join(to_expression(source_id), "[i]")), + ";"); + end_scope(); + + return true; +} + +void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr) +{ + if (!backend.force_gl_in_out_block) + return; + // This path is only relevant for GL backends. + + auto *var = maybe_get(source_id); + if (!var) + return; + + if (var->storage != StorageClassInput && var->storage != StorageClassOutput) + return; + + auto &type = get_variable_data_type(*var); + if (type.array.empty()) + return; + + auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn)); + bool is_builtin = is_builtin_variable(*var) && + (builtin == BuiltInPointSize || + builtin == BuiltInPosition || + builtin == BuiltInSampleMask); + bool is_tess = is_tessellation_shader(); + bool is_patch = has_decoration(var->self, DecorationPatch); + bool is_sample_mask = is_builtin && builtin == BuiltInSampleMask; + + // Tessellation input arrays are special in that they are unsized, so we cannot directly copy from it. + // We must unroll the array load. + // For builtins, we couldn't catch this case normally, + // because this is resolved in the OpAccessChain in most cases. + // If we load the entire array, we have no choice but to unroll here. + if (!is_patch && (is_builtin || is_tess)) + { + auto new_expr = join("_", target_id, "_unrolled"); + statement(variable_decl(type, new_expr, target_id), ";"); + string array_expr; + if (type.array_size_literal.back()) + { + array_expr = convert_to_string(type.array.back()); + if (type.array.back() == 0) + SPIRV_CROSS_THROW("Cannot unroll an array copy from unsized array."); + } + else + array_expr = to_expression(type.array.back()); + + // The array size might be a specialization constant, so use a for-loop instead. + statement("for (int i = 0; i < int(", array_expr, "); i++)"); + begin_scope(); + if (is_builtin && !is_sample_mask) + statement(new_expr, "[i] = gl_in[i].", expr, ";"); + else if (is_sample_mask) + { + SPIRType target_type { OpTypeInt }; + target_type.basetype = SPIRType::Int; + statement(new_expr, "[i] = ", bitcast_expression(target_type, type.basetype, join(expr, "[i]")), ";"); + } + else + statement(new_expr, "[i] = ", expr, "[i];"); + end_scope(); + + expr = std::move(new_expr); + } +} + +void CompilerGLSL::cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) +{ + // We will handle array cases elsewhere. + if (!expr_type.array.empty()) + return; + + auto *var = maybe_get_backing_variable(source_id); + if (var) + source_id = var->self; + + // Only interested in standalone builtin variables. + if (!has_decoration(source_id, DecorationBuiltIn)) + { + // Except for int attributes in legacy GLSL, which are cast from float. + if (is_legacy() && expr_type.basetype == SPIRType::Int && var && var->storage == StorageClassInput) + expr = join(type_to_glsl(expr_type), "(", expr, ")"); + 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: + case BuiltInBaseVertex: + case BuiltInBaseInstance: + case BuiltInDrawIndex: + case BuiltInFragStencilRefEXT: + case BuiltInInstanceCustomIndexNV: + case BuiltInSampleMask: + case BuiltInPrimitiveShadingRateKHR: + case BuiltInShadingRateKHR: + expected_type = SPIRType::Int; + break; + + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInWorkgroupId: + case BuiltInLocalInvocationIndex: + case BuiltInWorkgroupSize: + case BuiltInNumWorkgroups: + case BuiltInIncomingRayFlagsNV: + case BuiltInLaunchIdNV: + case BuiltInLaunchSizeNV: + case BuiltInPrimitiveTriangleIndicesEXT: + case BuiltInPrimitiveLineIndicesEXT: + case BuiltInPrimitivePointIndicesEXT: + expected_type = SPIRType::UInt; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + expr = bitcast_expression(expr_type, expected_type, expr); +} + +SPIRType::BaseType CompilerGLSL::get_builtin_basetype(BuiltIn builtin, SPIRType::BaseType default_type) +{ + // TODO: Fill in for more builtins. + switch (builtin) + { + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + case BuiltInFragStencilRefEXT: + case BuiltInSampleMask: + case BuiltInPrimitiveShadingRateKHR: + case BuiltInShadingRateKHR: + return SPIRType::Int; + + default: + return default_type; + } +} + +void CompilerGLSL::cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) +{ + auto *var = maybe_get_backing_variable(target_id); + if (var) + target_id = var->self; + + // 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 = get_builtin_basetype(builtin, expr_type.basetype); + + if (expected_type != expr_type.basetype) + { + auto type = expr_type; + type.basetype = expected_type; + expr = bitcast_expression(type, expr_type.basetype, expr); + } +} + +void CompilerGLSL::convert_non_uniform_expression(string &expr, uint32_t ptr_id) +{ + if (*backend.nonuniform_qualifier == '\0') + return; + + auto *var = maybe_get_backing_variable(ptr_id); + auto *buffer_pointer = maybe_get_backing_buffer_pointer(ptr_id); + if (!var && !buffer_pointer) + return; + + if (!buffer_pointer && + var->storage != StorageClassUniformConstant && + var->storage != StorageClassStorageBuffer && + var->storage != StorageClassUniform) + return; + + auto &backing_type = get(var ? var->basetype : buffer_pointer->expression_type); + + bool descriptor_heap = false; + if (var) + { + auto builtin = BuiltIn(get_decoration(var->self, DecorationBuiltIn)); + descriptor_heap = builtin == BuiltInResourceHeapEXT || builtin == BuiltInSamplerHeapEXT; + } + else if (buffer_pointer) + descriptor_heap = true; + + if (!descriptor_heap && backing_type.array.empty()) + return; + + // If we get here, we know we're accessing an arrayed resource which + // might require nonuniform qualifier. + + auto start_array_index = expr.find_first_of('['); + + if (start_array_index == string::npos) + return; + + // We've opened a bracket, track expressions until we can close the bracket. + // This must be our resource index. + size_t end_array_index = string::npos; + unsigned bracket_count = 1; + for (size_t index = start_array_index + 1; index < expr.size(); index++) + { + if (expr[index] == ']') + { + if (--bracket_count == 0) + { + end_array_index = index; + break; + } + } + else if (expr[index] == '[') + bracket_count++; + } + + assert(bracket_count == 0); + + // Doesn't really make sense to declare a non-arrayed image with nonuniformEXT, but there's + // nothing we can do here to express that. + if (start_array_index == string::npos || end_array_index == string::npos || end_array_index < start_array_index) + return; + + start_array_index++; + + expr = join(expr.substr(0, start_array_index), backend.nonuniform_qualifier, "(", + expr.substr(start_array_index, end_array_index - start_array_index), ")", + expr.substr(end_array_index, string::npos)); +} + +void CompilerGLSL::emit_block_hints(const SPIRBlock &block) +{ + if ((options.es && options.version < 310) || (!options.es && options.version < 140)) + return; + + switch (block.hint) + { + case SPIRBlock::HintFlatten: + require_extension_internal("GL_EXT_control_flow_attributes"); + statement("SPIRV_CROSS_FLATTEN"); + break; + case SPIRBlock::HintDontFlatten: + require_extension_internal("GL_EXT_control_flow_attributes"); + statement("SPIRV_CROSS_BRANCH"); + break; + case SPIRBlock::HintUnroll: + require_extension_internal("GL_EXT_control_flow_attributes"); + statement("SPIRV_CROSS_UNROLL"); + break; + case SPIRBlock::HintDontUnroll: + require_extension_internal("GL_EXT_control_flow_attributes"); + statement("SPIRV_CROSS_LOOP"); + break; + default: + break; + } +} + +void CompilerGLSL::preserve_alias_on_reset(uint32_t id) +{ + preserved_aliases[id] = get_name(id); +} + +void CompilerGLSL::reset_name_caches() +{ + for (auto &preserved : preserved_aliases) + set_name(preserved.first, preserved.second); + + preserved_aliases.clear(); + resource_names.clear(); + block_input_names.clear(); + block_output_names.clear(); + block_ubo_names.clear(); + block_ssbo_names.clear(); + block_names.clear(); + function_overloads.clear(); +} + +void CompilerGLSL::fixup_anonymous_struct_names(std::unordered_set &visited, const SPIRType &type) +{ + if (visited.count(type.self)) + return; + visited.insert(type.self); + + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + auto &mbr_type = get(type.member_types[i]); + + if (mbr_type.basetype == SPIRType::Struct) + { + // If there are multiple aliases, the output might be somewhat unpredictable, + // but the only real alternative in that case is to do nothing, which isn't any better. + // This check should be fine in practice. + if (get_name(mbr_type.self).empty() && !get_member_name(type.self, i).empty()) + { + auto anon_name = join("anon_", get_member_name(type.self, i)); + ParsedIR::sanitize_underscores(anon_name); + set_name(mbr_type.self, anon_name); + } + + fixup_anonymous_struct_names(visited, mbr_type); + } + } +} + +void CompilerGLSL::fixup_anonymous_struct_names() +{ + // HLSL codegen can often end up emitting anonymous structs inside blocks, which + // breaks GL linking since all names must match ... + // Try to emit sensible code, so attempt to find such structs and emit anon_$member. + + // Breaks exponential explosion with weird type trees. + std::unordered_set visited; + + ir.for_each_typed_id([&](uint32_t, SPIRType &type) { + if (type.basetype == SPIRType::Struct && + (has_decoration(type.self, DecorationBlock) || + has_decoration(type.self, DecorationBufferBlock))) + { + fixup_anonymous_struct_names(visited, type); + } + }); +} + +void CompilerGLSL::fixup_type_alias() +{ + // Due to how some backends work, the "master" type of type_alias must be a block-like type if it exists. + ir.for_each_typed_id([&](uint32_t self, SPIRType &type) { + if (!type.type_alias) + return; + + if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock)) + { + // Top-level block types should never alias anything else. + type.type_alias = 0; + } + else if (type_is_block_like(type) && type.self == ID(self)) + { + // A block-like type is any type which contains Offset decoration, but not top-level blocks, + // i.e. blocks which are placed inside buffers. + // Become the master. + ir.for_each_typed_id([&](uint32_t other_id, SPIRType &other_type) { + if (other_id == self) + return; + + if (other_type.type_alias == type.type_alias) + other_type.type_alias = self; + }); + + this->get(type.type_alias).type_alias = self; + type.type_alias = 0; + } + }); +} + +void CompilerGLSL::reorder_type_alias() +{ + // Reorder declaration of types so that the master of the type alias is always emitted first. + // We need this in case a type B depends on type A (A must come before in the vector), but A is an alias of a type Abuffer, which + // means declaration of A doesn't happen (yet), and order would be B, ABuffer and not ABuffer, B. Fix this up here. + auto loop_lock = ir.create_loop_hard_lock(); + + auto &type_ids = ir.ids_for_type[TypeType]; + for (auto alias_itr = begin(type_ids); alias_itr != end(type_ids); ++alias_itr) + { + auto &type = get(*alias_itr); + if (type.type_alias != TypeID(0) && + !has_extended_decoration(type.type_alias, SPIRVCrossDecorationBufferBlockRepacked)) + { + // We will skip declaring this type, so make sure the type_alias type comes before. + auto master_itr = find(begin(type_ids), end(type_ids), ID(type.type_alias)); + assert(master_itr != end(type_ids)); + + if (alias_itr < master_itr) + { + // Must also swap the type order for the constant-type joined array. + auto &joined_types = ir.ids_for_constant_undef_or_type; + auto alt_alias_itr = find(begin(joined_types), end(joined_types), *alias_itr); + auto alt_master_itr = find(begin(joined_types), end(joined_types), *master_itr); + assert(alt_alias_itr != end(joined_types)); + assert(alt_master_itr != end(joined_types)); + + swap(*alias_itr, *master_itr); + swap(*alt_alias_itr, *alt_master_itr); + } + } + } +} + +void CompilerGLSL::emit_line_directive(uint32_t file_id, uint32_t line_literal) +{ + // If we are redirecting statements, ignore the line directive. + // Common case here is continue blocks. + if (redirect_statement) + return; + + // If we're emitting code in a sensitive context such as condition blocks in for loops, don't emit + // any line directives, because it's not possible. + if (block_debug_directives) + return; + + if (options.emit_line_directives) + { + require_extension_internal("GL_GOOGLE_cpp_style_line_directive"); + statement_no_indent("#line ", line_literal, " \"", get(file_id).str, "\""); + } +} + +void CompilerGLSL::emit_non_semantic_shader_debug_info(uint32_t, uint32_t result_id, uint32_t eop, + const uint32_t *args, uint32_t) +{ + if (!options.emit_line_directives) + return; + + switch (eop) + { + case SPIRExtension::DebugLine: + { + // We're missing line end and columns here, but I don't think we can emit those in any meaningful way. + emit_line_directive(args[0], get(args[1]).scalar()); + break; + } + + case SPIRExtension::DebugSource: + { + // Forward the string declaration here. We ignore the optional text operand. + auto &str = get(args[0]).str; + set(result_id, str); + break; + } + + default: + break; + } +} + +void CompilerGLSL::emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id, + SmallVector chain) +{ + // Fully unroll all member/array indices one by one. + + auto &lhs_type = get(lhs_type_id); + auto &rhs_type = get(rhs_type_id); + + if (!lhs_type.array.empty()) + { + // Could use a loop here to support specialization constants, but it gets rather complicated with nested array types, + // and this is a rather obscure opcode anyways, keep it simple unless we are forced to. + uint32_t array_size = to_array_size_literal(lhs_type); + chain.push_back(0); + + for (uint32_t i = 0; i < array_size; i++) + { + chain.back() = i; + emit_copy_logical_type(lhs_id, lhs_type.parent_type, rhs_id, rhs_type.parent_type, chain); + } + } + else if (lhs_type.basetype == SPIRType::Struct) + { + chain.push_back(0); + uint32_t member_count = uint32_t(lhs_type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + chain.back() = i; + emit_copy_logical_type(lhs_id, lhs_type.member_types[i], rhs_id, rhs_type.member_types[i], chain); + } + } + else + { + // Need to handle unpack/packing fixups since this can differ wildly between the logical types, + // particularly in MSL. + // To deal with this, we emit access chains and go through emit_store_statement + // to deal with all the special cases we can encounter. + + AccessChainMeta lhs_meta, rhs_meta; + auto lhs = access_chain_internal(lhs_id, chain.data(), uint32_t(chain.size()), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &lhs_meta, nullptr); + auto rhs = access_chain_internal(rhs_id, chain.data(), uint32_t(chain.size()), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT, &rhs_meta, nullptr); + + uint32_t id = ir.increase_bound_by(2); + lhs_id = id; + rhs_id = id + 1; + + { + auto &lhs_expr = set(lhs_id, std::move(lhs), lhs_type_id, true); + lhs_expr.need_transpose = lhs_meta.need_transpose; + + if (lhs_meta.storage_is_packed) + set_extended_decoration(lhs_id, SPIRVCrossDecorationPhysicalTypePacked); + if (lhs_meta.storage_physical_type != 0) + set_extended_decoration(lhs_id, SPIRVCrossDecorationPhysicalTypeID, lhs_meta.storage_physical_type); + + forwarded_temporaries.insert(lhs_id); + suppressed_usage_tracking.insert(lhs_id); + } + + { + auto &rhs_expr = set(rhs_id, std::move(rhs), rhs_type_id, true); + rhs_expr.need_transpose = rhs_meta.need_transpose; + + if (rhs_meta.storage_is_packed) + set_extended_decoration(rhs_id, SPIRVCrossDecorationPhysicalTypePacked); + if (rhs_meta.storage_physical_type != 0) + set_extended_decoration(rhs_id, SPIRVCrossDecorationPhysicalTypeID, rhs_meta.storage_physical_type); + + forwarded_temporaries.insert(rhs_id); + suppressed_usage_tracking.insert(rhs_id); + } + + emit_store_statement(lhs_id, rhs_id); + } +} + +bool CompilerGLSL::subpass_input_is_framebuffer_fetch(uint32_t id) const +{ + if (!has_decoration(id, DecorationInputAttachmentIndex)) + return false; + + uint32_t input_attachment_index = get_decoration(id, DecorationInputAttachmentIndex); + for (auto &remap : subpass_to_framebuffer_fetch_attachment) + if (remap.first == input_attachment_index) + return true; + + return false; +} + +const SPIRVariable *CompilerGLSL::find_subpass_input_by_attachment_index(uint32_t index) const +{ + const SPIRVariable *ret = nullptr; + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (has_decoration(var.self, DecorationInputAttachmentIndex) && + get_decoration(var.self, DecorationInputAttachmentIndex) == index) + { + ret = &var; + } + }); + return ret; +} + +const SPIRVariable *CompilerGLSL::find_color_output_by_location(uint32_t location) const +{ + const SPIRVariable *ret = nullptr; + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + if (var.storage == StorageClassOutput && get_decoration(var.self, DecorationLocation) == location) + ret = &var; + }); + return ret; +} + +void CompilerGLSL::emit_inout_fragment_outputs_copy_to_subpass_inputs() +{ + for (auto &remap : subpass_to_framebuffer_fetch_attachment) + { + auto *subpass_var = find_subpass_input_by_attachment_index(remap.first); + auto *output_var = find_color_output_by_location(remap.second); + if (!subpass_var) + continue; + if (!output_var) + SPIRV_CROSS_THROW("Need to declare the corresponding fragment output variable to be able " + "to read from it."); + if (is_array(get(output_var->basetype))) + SPIRV_CROSS_THROW("Cannot use GL_EXT_shader_framebuffer_fetch with arrays of color outputs."); + + auto &func = get(get_entry_point().self); + func.fixup_hooks_in.push_back([=]() { + if (is_legacy()) + { + statement(to_expression(subpass_var->self), " = ", "gl_LastFragData[", + get_decoration(output_var->self, DecorationLocation), "];"); + } + else + { + uint32_t num_rt_components = this->get(output_var->basetype).vecsize; + statement(to_expression(subpass_var->self), vector_swizzle(num_rt_components, 0), " = ", + to_expression(output_var->self), ";"); + } + }); + } +} + +bool CompilerGLSL::variable_is_depth_or_compare(VariableID id) const +{ + return is_depth_image(get(get(id).basetype), id); +} + +const char *CompilerGLSL::ShaderSubgroupSupportHelper::get_extension_name(Candidate c) +{ + static const char *const retval[CandidateCount] = { "GL_KHR_shader_subgroup_ballot", + "GL_KHR_shader_subgroup_basic", + "GL_KHR_shader_subgroup_vote", + "GL_KHR_shader_subgroup_arithmetic", + "GL_NV_gpu_shader_5", + "GL_NV_shader_thread_group", + "GL_NV_shader_thread_shuffle", + "GL_ARB_shader_ballot", + "GL_ARB_shader_group_vote", + "GL_AMD_gcn_shader" }; + return retval[c]; +} + +SmallVector CompilerGLSL::ShaderSubgroupSupportHelper::get_extra_required_extension_names(Candidate c) +{ + switch (c) + { + case ARB_shader_ballot: + return { "GL_ARB_shader_int64" }; + case AMD_gcn_shader: + return { "GL_AMD_gpu_shader_int64", "GL_NV_gpu_shader5" }; + default: + return {}; + } +} + +const char *CompilerGLSL::ShaderSubgroupSupportHelper::get_extra_required_extension_predicate(Candidate c) +{ + switch (c) + { + case ARB_shader_ballot: + return "defined(GL_ARB_shader_int64)"; + case AMD_gcn_shader: + return "(defined(GL_AMD_gpu_shader_int64) || defined(GL_NV_gpu_shader5))"; + default: + return ""; + } +} + +CompilerGLSL::ShaderSubgroupSupportHelper::FeatureVector CompilerGLSL::ShaderSubgroupSupportHelper:: + get_feature_dependencies(Feature feature) +{ + switch (feature) + { + case SubgroupAllEqualT: + return { SubgroupBroadcast_First, SubgroupAll_Any_AllEqualBool }; + case SubgroupElect: + return { SubgroupBallotFindLSB_MSB, SubgroupBallot, SubgroupInvocationID }; + case SubgroupInverseBallot_InclBitCount_ExclBitCout: + return { SubgroupMask }; + case SubgroupBallotBitCount: + return { SubgroupBallot }; + case SubgroupArithmeticIAddReduce: + case SubgroupArithmeticIAddInclusiveScan: + case SubgroupArithmeticFAddReduce: + case SubgroupArithmeticFAddInclusiveScan: + case SubgroupArithmeticIMulReduce: + case SubgroupArithmeticIMulInclusiveScan: + case SubgroupArithmeticFMulReduce: + case SubgroupArithmeticFMulInclusiveScan: + return { SubgroupSize, SubgroupBallot, SubgroupBallotBitCount, SubgroupMask, SubgroupBallotBitExtract }; + case SubgroupArithmeticIAddExclusiveScan: + case SubgroupArithmeticFAddExclusiveScan: + case SubgroupArithmeticIMulExclusiveScan: + case SubgroupArithmeticFMulExclusiveScan: + return { SubgroupSize, SubgroupBallot, SubgroupBallotBitCount, + SubgroupMask, SubgroupElect, SubgroupBallotBitExtract }; + default: + return {}; + } +} + +CompilerGLSL::ShaderSubgroupSupportHelper::FeatureMask CompilerGLSL::ShaderSubgroupSupportHelper:: + get_feature_dependency_mask(Feature feature) +{ + return build_mask(get_feature_dependencies(feature)); +} + +bool CompilerGLSL::ShaderSubgroupSupportHelper::can_feature_be_implemented_without_extensions(Feature feature) +{ + static const bool retval[FeatureCount] = { + false, false, false, false, false, false, + true, // SubgroupBalloFindLSB_MSB + false, false, false, false, + true, // SubgroupMemBarrier - replaced with workgroup memory barriers + false, false, true, false, + false, false, false, false, false, false, // iadd, fadd + false, false, false, false, false, false, // imul , fmul + }; + + return retval[feature]; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::Candidate CompilerGLSL::ShaderSubgroupSupportHelper:: + get_KHR_extension_for_feature(Feature feature) +{ + static const Candidate extensions[FeatureCount] = { + KHR_shader_subgroup_ballot, KHR_shader_subgroup_basic, KHR_shader_subgroup_basic, KHR_shader_subgroup_basic, + KHR_shader_subgroup_basic, KHR_shader_subgroup_ballot, KHR_shader_subgroup_ballot, KHR_shader_subgroup_vote, + KHR_shader_subgroup_vote, KHR_shader_subgroup_basic, KHR_shader_subgroup_basic, KHR_shader_subgroup_basic, + KHR_shader_subgroup_ballot, KHR_shader_subgroup_ballot, KHR_shader_subgroup_ballot, KHR_shader_subgroup_ballot, + KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, + KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, + KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, + KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, KHR_shader_subgroup_arithmetic, + }; + + return extensions[feature]; +} + +void CompilerGLSL::ShaderSubgroupSupportHelper::request_feature(Feature feature) +{ + feature_mask |= (FeatureMask(1) << feature) | get_feature_dependency_mask(feature); +} + +bool CompilerGLSL::ShaderSubgroupSupportHelper::is_feature_requested(Feature feature) const +{ + return (feature_mask & (1u << feature)) != 0; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::Result CompilerGLSL::ShaderSubgroupSupportHelper::resolve() const +{ + Result res; + + for (uint32_t i = 0u; i < FeatureCount; ++i) + { + if (feature_mask & (1u << i)) + { + auto feature = static_cast(i); + std::unordered_set unique_candidates; + + auto candidates = get_candidates_for_feature(feature); + unique_candidates.insert(candidates.begin(), candidates.end()); + + auto deps = get_feature_dependencies(feature); + for (Feature d : deps) + { + candidates = get_candidates_for_feature(d); + if (!candidates.empty()) + unique_candidates.insert(candidates.begin(), candidates.end()); + } + + for (uint32_t c : unique_candidates) + ++res.weights[static_cast(c)]; + } + } + + return res; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::CandidateVector CompilerGLSL::ShaderSubgroupSupportHelper:: + get_candidates_for_feature(Feature ft, const Result &r) +{ + auto c = get_candidates_for_feature(ft); + auto cmp = [&r](Candidate a, Candidate b) { + if (r.weights[a] == r.weights[b]) + return a < b; // Prefer candidates with lower enum value + return r.weights[a] > r.weights[b]; + }; + std::sort(c.begin(), c.end(), cmp); + return c; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::CandidateVector CompilerGLSL::ShaderSubgroupSupportHelper:: + get_candidates_for_feature(Feature feature) +{ + switch (feature) + { + case SubgroupMask: + return { KHR_shader_subgroup_ballot, NV_shader_thread_group, ARB_shader_ballot }; + case SubgroupSize: + return { KHR_shader_subgroup_basic, NV_shader_thread_group, AMD_gcn_shader, ARB_shader_ballot }; + case SubgroupInvocationID: + return { KHR_shader_subgroup_basic, NV_shader_thread_group, ARB_shader_ballot }; + case SubgroupID: + return { KHR_shader_subgroup_basic, NV_shader_thread_group }; + case NumSubgroups: + return { KHR_shader_subgroup_basic, NV_shader_thread_group }; + case SubgroupBroadcast_First: + return { KHR_shader_subgroup_ballot, NV_shader_thread_shuffle, ARB_shader_ballot }; + case SubgroupBallotFindLSB_MSB: + return { KHR_shader_subgroup_ballot, NV_shader_thread_group }; + case SubgroupAll_Any_AllEqualBool: + return { KHR_shader_subgroup_vote, NV_gpu_shader_5, ARB_shader_group_vote, AMD_gcn_shader }; + case SubgroupAllEqualT: + return {}; // depends on other features only + case SubgroupElect: + return {}; // depends on other features only + case SubgroupBallot: + return { KHR_shader_subgroup_ballot, NV_shader_thread_group, ARB_shader_ballot }; + case SubgroupBarrier: + return { KHR_shader_subgroup_basic, NV_shader_thread_group, ARB_shader_ballot, AMD_gcn_shader }; + case SubgroupMemBarrier: + return { KHR_shader_subgroup_basic }; + case SubgroupInverseBallot_InclBitCount_ExclBitCout: + return {}; + case SubgroupBallotBitExtract: + return { NV_shader_thread_group }; + case SubgroupBallotBitCount: + return {}; + case SubgroupArithmeticIAddReduce: + case SubgroupArithmeticIAddExclusiveScan: + case SubgroupArithmeticIAddInclusiveScan: + case SubgroupArithmeticFAddReduce: + case SubgroupArithmeticFAddExclusiveScan: + case SubgroupArithmeticFAddInclusiveScan: + case SubgroupArithmeticIMulReduce: + case SubgroupArithmeticIMulExclusiveScan: + case SubgroupArithmeticIMulInclusiveScan: + case SubgroupArithmeticFMulReduce: + case SubgroupArithmeticFMulExclusiveScan: + case SubgroupArithmeticFMulInclusiveScan: + return { KHR_shader_subgroup_arithmetic, NV_shader_thread_shuffle }; + default: + return {}; + } +} + +CompilerGLSL::ShaderSubgroupSupportHelper::FeatureMask CompilerGLSL::ShaderSubgroupSupportHelper::build_mask( + const SmallVector &features) +{ + FeatureMask mask = 0; + for (Feature f : features) + mask |= FeatureMask(1) << f; + return mask; +} + +CompilerGLSL::ShaderSubgroupSupportHelper::Result::Result() +{ + for (auto &weight : weights) + weight = 0; + + // Make sure KHR_shader_subgroup extensions are always prefered. + const uint32_t big_num = FeatureCount; + weights[KHR_shader_subgroup_ballot] = big_num; + weights[KHR_shader_subgroup_basic] = big_num; + weights[KHR_shader_subgroup_vote] = big_num; + weights[KHR_shader_subgroup_arithmetic] = big_num; +} + +void CompilerGLSL::request_workaround_wrapper_overload(TypeID id) +{ + // Must be ordered to maintain deterministic output, so vector is appropriate. + if (find(begin(workaround_ubo_load_overload_types), end(workaround_ubo_load_overload_types), id) == + end(workaround_ubo_load_overload_types)) + { + force_recompile(); + workaround_ubo_load_overload_types.push_back(id); + } +} + +void CompilerGLSL::rewrite_load_for_wrapped_row_major(std::string &expr, TypeID loaded_type, ID ptr) +{ + // Loading row-major matrices from UBOs on older AMD Windows OpenGL drivers is problematic. + // To load these types correctly, we must first wrap them in a dummy function which only purpose is to + // ensure row_major decoration is actually respected. + auto *var = maybe_get_backing_variable(ptr); + if (!var) + return; + + auto &backing_type = get(var->basetype); + bool is_ubo = backing_type.basetype == SPIRType::Struct && backing_type.storage == StorageClassUniform && + has_decoration(backing_type.self, DecorationBlock); + if (!is_ubo) + return; + + auto *type = &get(loaded_type); + bool rewrite = false; + bool relaxed = options.es; + + if (is_matrix(*type)) + { + // To avoid adding a lot of unnecessary meta tracking to forward the row_major state, + // we will simply look at the base struct itself. It is exceptionally rare to mix and match row-major/col-major state. + // If there is any row-major action going on, we apply the workaround. + // It is harmless to apply the workaround to column-major matrices, so this is still a valid solution. + // If an access chain occurred, the workaround is not required, so loading vectors or scalars don't need workaround. + type = &backing_type; + } + else + { + // If we're loading a composite, we don't have overloads like these. + relaxed = false; + } + + if (type->basetype == SPIRType::Struct) + { + // If we're loading a struct where any member is a row-major matrix, apply the workaround. + for (uint32_t i = 0; i < uint32_t(type->member_types.size()); i++) + { + auto decorations = combined_decoration_for_member(*type, i); + if (decorations.get(DecorationRowMajor)) + rewrite = true; + + // Since we decide on a per-struct basis, only use mediump wrapper if all candidates are mediump. + if (!decorations.get(DecorationRelaxedPrecision)) + relaxed = false; + } + } + + if (rewrite) + { + request_workaround_wrapper_overload(loaded_type); + expr = join("spvWorkaroundRowMajor", (relaxed ? "MP" : ""), "(", expr, ")"); + } +} + +void CompilerGLSL::mask_stage_output_by_location(uint32_t location, uint32_t component) +{ + masked_output_locations.insert({ location, component }); +} + +void CompilerGLSL::mask_stage_output_by_builtin(BuiltIn builtin) +{ + masked_output_builtins.insert(builtin); +} + +bool CompilerGLSL::is_stage_output_variable_masked(const SPIRVariable &var) const +{ + auto &type = get(var.basetype); + bool is_block = has_decoration(type.self, DecorationBlock); + // Blocks by themselves are never masked. Must be masked per-member. + if (is_block) + return false; + + bool is_builtin = has_decoration(var.self, DecorationBuiltIn); + + if (is_builtin) + { + return is_stage_output_builtin_masked(BuiltIn(get_decoration(var.self, DecorationBuiltIn))); + } + else + { + if (!has_decoration(var.self, DecorationLocation)) + return false; + + return is_stage_output_location_masked( + get_decoration(var.self, DecorationLocation), + get_decoration(var.self, DecorationComponent)); + } +} + +bool CompilerGLSL::is_stage_output_block_member_masked(const SPIRVariable &var, uint32_t index, bool strip_array) const +{ + auto &type = get(var.basetype); + bool is_block = has_decoration(type.self, DecorationBlock); + if (!is_block) + return false; + + BuiltIn builtin = BuiltInMax; + if (is_member_builtin(type, index, &builtin)) + { + return is_stage_output_builtin_masked(builtin); + } + else + { + uint32_t location = get_declared_member_location(var, index, strip_array); + uint32_t component = get_member_decoration(type.self, index, DecorationComponent); + return is_stage_output_location_masked(location, component); + } +} + +bool CompilerGLSL::is_per_primitive_variable(const SPIRVariable &var) const +{ + if (has_decoration(var.self, DecorationPerPrimitiveEXT)) + return true; + + auto &type = get(var.basetype); + if (!has_decoration(type.self, DecorationBlock)) + return false; + + for (uint32_t i = 0, n = uint32_t(type.member_types.size()); i < n; i++) + if (!has_member_decoration(type.self, i, DecorationPerPrimitiveEXT)) + return false; + + return true; +} + +bool CompilerGLSL::is_stage_output_location_masked(uint32_t location, uint32_t component) const +{ + return masked_output_locations.count({ location, component }) != 0; +} + +bool CompilerGLSL::is_stage_output_builtin_masked(BuiltIn builtin) const +{ + return masked_output_builtins.count(builtin) != 0; +} + +uint32_t CompilerGLSL::get_declared_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const +{ + auto &block_type = get(var.basetype); + if (has_member_decoration(block_type.self, mbr_idx, DecorationLocation)) + return get_member_decoration(block_type.self, mbr_idx, DecorationLocation); + else + return get_accumulated_member_location(var, mbr_idx, strip_array); +} + +uint32_t CompilerGLSL::get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const +{ + auto &type = strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + uint32_t location = get_decoration(var.self, DecorationLocation); + + for (uint32_t i = 0; i < mbr_idx; i++) + { + auto &mbr_type = get(type.member_types[i]); + + // Start counting from any place we have a new location decoration. + if (has_member_decoration(type.self, mbr_idx, DecorationLocation)) + location = get_member_decoration(type.self, mbr_idx, DecorationLocation); + + uint32_t location_count = type_to_location_count(mbr_type); + location += location_count; + } + + return location; +} + +StorageClass CompilerGLSL::get_expression_effective_storage_class(uint32_t ptr) +{ + auto *var = maybe_get_backing_variable(ptr); + + // If the expression has been lowered to a temporary, we need to use the Generic storage class. + // We're looking for the effective storage class of a given expression. + // An access chain or forwarded OpLoads from such access chains + // will generally have the storage class of the underlying variable, but if the load was not forwarded + // we have lost any address space qualifiers. + bool forced_temporary = ir.ids[ptr].get_type() == TypeExpression && !get(ptr).access_chain && + (forced_temporaries.count(ptr) != 0 || forwarded_temporaries.count(ptr) == 0); + + if (var && !forced_temporary) + { + if (variable_decl_is_remapped_storage(*var, StorageClassWorkgroup)) + return StorageClassWorkgroup; + if (variable_decl_is_remapped_storage(*var, StorageClassStorageBuffer)) + return StorageClassStorageBuffer; + + // Normalize SSBOs to StorageBuffer here. + if (var->storage == StorageClassUniform && + has_decoration(get(var->basetype).self, DecorationBufferBlock)) + return StorageClassStorageBuffer; + else + return var->storage; + } + else + return expression_type(ptr).storage; +} + +uint32_t CompilerGLSL::type_to_location_count(const SPIRType &type) const +{ + uint32_t count; + if (type.basetype == SPIRType::Struct) + { + uint32_t mbr_count = uint32_t(type.member_types.size()); + count = 0; + for (uint32_t i = 0; i < mbr_count; i++) + count += type_to_location_count(get(type.member_types[i])); + } + else + { + count = type.columns > 1 ? type.columns : 1; + } + + uint32_t dim_count = uint32_t(type.array.size()); + for (uint32_t i = 0; i < dim_count; i++) + count *= to_array_size_literal(type, i); + + return count; +} + +std::string CompilerGLSL::format_float(float value) const +{ + if (float_formatter) + return float_formatter->format_float(value); + + // default behavior + return convert_to_string(value, current_locale_radix_character); +} + +std::string CompilerGLSL::format_double(double value) const +{ + if (float_formatter) + return float_formatter->format_double(value); + + // default behavior + return convert_to_string(value, current_locale_radix_character); +} + +std::string CompilerGLSL::to_pretty_expression_if_int_constant( + uint32_t id, + const GlslConstantNameMapping *mapping_start, const GlslConstantNameMapping *mapping_end, + bool register_expression_read) +{ + auto *c = maybe_get(id); + if (c && !c->specialization) + { + auto value = c->scalar(); + auto pretty_name = std::find_if(mapping_start, mapping_end, + [value](const GlslConstantNameMapping &mapping) { return mapping.value == value; }); + if (pretty_name != mapping_end) + return pretty_name->alias; + } + return join("int(", to_expression(id, register_expression_read), ")"); +} + +uint32_t CompilerGLSL::get_fp_fast_math_flags_for_op(uint32_t result_type, uint32_t id) const +{ + uint32_t fp_flags = ~0; + + if (!type_is_floating_point(get(result_type))) + return fp_flags; + + auto &ep = get_entry_point(); + + // Per-operation flag supersedes all defaults. + if (id != 0 && has_decoration(id, DecorationFPFastMathMode)) + return get_decoration(id, DecorationFPFastMathMode); + + // Handle float_controls1 execution modes. + uint32_t width = get(result_type).width; + + bool szinp = false; + + switch (width) + { + case 8: + szinp = ep.signed_zero_inf_nan_preserve_8; + break; + + case 16: + szinp = ep.signed_zero_inf_nan_preserve_16; + break; + + case 32: + szinp = ep.signed_zero_inf_nan_preserve_32; + break; + + case 64: + szinp = ep.signed_zero_inf_nan_preserve_64; + break; + + default: + break; + } + + if (szinp) + fp_flags &= ~(FPFastMathModeNSZMask | FPFastMathModeNotInfMask | FPFastMathModeNotNaNMask); + + // Legacy NoContraction deals with any kind of transform to the expression. + if (id != 0 && has_decoration(id, DecorationNoContraction)) + fp_flags &= ~(FPFastMathModeAllowContractMask | FPFastMathModeAllowTransformMask | FPFastMathModeAllowReassocMask); + + // Handle float_controls2 execution modes. + bool found_default = false; + for (auto &fp_pair : ep.fp_fast_math_defaults) + { + if (get(fp_pair.first).width == width && fp_pair.second) + { + fp_flags &= get(fp_pair.second).scalar(); + found_default = true; + } + } + + // From SPV_KHR_float_controls2: + // "This definition implies that, if the entry point set any FPFastMathDefault execution mode + // then any type for which a default is not set uses no fast math flags + // (although this can still be overridden on a per-operation basis). + // Modules must not mix setting fast math modes explicitly using this extension and relying on older API defaults." + if (!found_default && !ep.fp_fast_math_defaults.empty()) + fp_flags = 0; + + return fp_flags; +} + +bool CompilerGLSL::has_legacy_nocontract(uint32_t result_type, uint32_t id) const +{ + const auto fp_flags = FPFastMathModeAllowContractMask | + FPFastMathModeAllowTransformMask | + FPFastMathModeAllowReassocMask; + return (get_fp_fast_math_flags_for_op(result_type, id) & fp_flags) != fp_flags; +} + +void CompilerGLSL::remap_descriptor_heap(ResourceType type, uint32_t desc_set, uint32_t binding, Dim dim) +{ + for (auto &mapping : descriptor_heap_mappings) + { + if (mapping.type == type) + { + mapping.desc_set = desc_set; + mapping.binding = binding; + mapping.dim = dim; + return; + } + } + + descriptor_heap_mappings.push_back({ type, desc_set, binding, dim }); +} + +bool CompilerGLSL::is_descriptor_non_uniform(uint32_t id) const +{ + if (has_decoration(id, DecorationNonUniform)) + return true; + + // Only infer nonuniform for descriptors. + auto &type = expression_type(id); + + if (is_pointer(type)) + { + if (type.storage != StorageClassUniform && type.storage != StorageClassStorageBuffer && + type.storage != StorageClassUniformConstant && type.storage != StorageClassImage) + return false; + } + else if (!type_is_opaque_value(type)) + { + return false; + } + + if (descriptor_heap_mappings.empty()) + return false; + + if (has_decoration(id, DecorationUniform)) + return false; + + if (std::find(ir.declared_capabilities.begin(), ir.declared_capabilities.end(), + CapabilityDescriptorHeapEXT) == ir.declared_capabilities.end()) + return false; + + // Definitely not. + if (maybe_get(id) || maybe_get(id)) + return false; + + // DescriptorHeapEXT requires that nonuniformEXT is implied, + // but if we're remapping to legacy set/binding model, glslang will not emit the cap in cross compiled source, + // so we have to enforce it. We don't have compiler-infra to deduce subgroup uniformity statically, + // so just slap it on everything. Compilers generally figure this stuff out. + return true; +} + +std::string CompilerGLSL::to_descriptor_heap_layout(const SPIRType &type, StorageClass storage) const +{ + auto resource = ResourceTypeUnknown; + Dim dim = DimMax; + + switch (type.basetype) + { + case SPIRType::Sampler: + resource = ResourceTypeSeparateSamplers; + break; + + case SPIRType::Image: + dim = type.image.dim == DimBuffer ? DimBuffer : Dim2D; + resource = type.image.sampled == 2 ? ResourceTypeStorageImage : ResourceTypeSeparateImage; + break; + + case SPIRType::SampledImage: + resource = ResourceTypeSampledImage; + break; + + case SPIRType::AccelerationStructure: + resource = ResourceTypeAccelerationStructure; + break; + + case SPIRType::AtomicCounter: + resource = ResourceTypeAtomicCounter; + break; + + case SPIRType::Struct: + { + bool ssbo = storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock); + resource = ssbo ? ResourceTypeStorageBuffer : ResourceTypeUniformBuffer; + break; + } + + default: + break; + } + + for (auto &mapping : descriptor_heap_mappings) + { + if (mapping.type == resource) + { + bool has_match = false; + + if (type.basetype == SPIRType::Image) + { + if (dim == DimBuffer && (mapping.dim == DimMax || mapping.dim == DimBuffer)) + has_match = true; + if (dim != DimBuffer && mapping.dim != DimBuffer) + has_match = true; + } + else + { + has_match = true; + } + + if (has_match) + return join("set = ", mapping.desc_set, ", binding = ", mapping.binding); + } + } + + // Fallback to unknown mapping. + for (auto &mapping : descriptor_heap_mappings) + if (mapping.type == ResourceTypeUnknown) + return join("set = ", mapping.desc_set, ", binding = ", mapping.binding); + + return "descriptor_heap"; +} + diff --git a/thirdparty/SPIRV-Cross/spirv_glsl.hpp b/thirdparty/SPIRV-Cross/spirv_glsl.hpp new file mode 100644 index 000000000..0b7590577 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_glsl.hpp @@ -0,0 +1,1132 @@ +/* + * Copyright 2015-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_GLSL_HPP +#define SPIRV_CROSS_GLSL_HPP + +#include "GLSL.std.450.h" +#include "spirv_cross.hpp" +#include +#include +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +struct GlslConstantNameMapping; + +enum PlsFormat +{ + PlsNone = 0, + + PlsR11FG11FB10F, + PlsR32F, + PlsRG16F, + PlsRGB10A2, + PlsRGBA8, + PlsRG16, + + PlsRGBA8I, + PlsRG16I, + + PlsRGB10A2UI, + PlsRGBA8UI, + PlsRG16UI, + PlsR32UI +}; + +struct PlsRemap +{ + uint32_t id; + PlsFormat format; +}; + +enum AccessChainFlagBits +{ + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT = 1 << 0, + ACCESS_CHAIN_CHAIN_ONLY_BIT = 1 << 1, + ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2, + ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3, + ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4, + ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5, + ACCESS_CHAIN_FORCE_COMPOSITE_BIT = 1 << 6, + ACCESS_CHAIN_PTR_CHAIN_POINTER_ARITH_BIT = 1 << 7, + ACCESS_CHAIN_PTR_CHAIN_CAST_TO_SCALAR_BIT = 1 << 8 +}; +typedef uint32_t AccessChainFlags; + +class CompilerGLSL : public Compiler +{ +public: + struct Options + { + // The shading language version. Corresponds to #version $VALUE. + uint32_t version = 450; + + // Emit the OpenGL ES shading language instead of desktop OpenGL. + bool es = false; + + // Debug option to always emit temporary variables for all expressions. + bool force_temporary = false; + // Debug option, can be increased in an attempt to workaround SPIRV-Cross bugs temporarily. + // If this limit has to be increased, it points to an implementation bug. + // In certain scenarios, the maximum number of debug iterations may increase beyond this limit + // as long as we can prove we're making certain kinds of forward progress. + uint32_t force_recompile_max_debug_iterations = 3; + + // If true, Vulkan GLSL features are used instead of GL-compatible features. + // Mostly useful for debugging SPIR-V files. + bool vulkan_semantics = false; + + // If true, gl_PerVertex is explicitly redeclared in vertex, geometry and tessellation shaders. + // The members of gl_PerVertex is determined by which built-ins are declared by the shader. + // This option is ignored in ES versions, as redeclaration in ES is not required, and it depends on a different extension + // (EXT_shader_io_blocks) which makes things a bit more fuzzy. + bool separate_shader_objects = false; + + // Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays, + // e.g. float foo[a * b * c]. + // This function does not change the actual SPIRType of any object. + // Only the generated code, including declarations of interface variables are changed to be single array dimension. + bool flatten_multidimensional_arrays = false; + + // For older desktop GLSL targets than version 420, the + // GL_ARB_shading_language_420pack extensions is used to be able to support + // layout(binding) on UBOs and samplers. + // If disabled on older targets, binding decorations will be stripped. + bool enable_420pack_extension = true; + + // In non-Vulkan GLSL, emit push constant blocks as UBOs rather than plain uniforms. + bool emit_push_constant_as_uniform_buffer = false; + + // Always emit uniform blocks as plain uniforms, regardless of the GLSL version, even when UBOs are supported. + // Does not apply to shader storage or push constant blocks. + bool emit_uniform_buffer_as_plain_uniforms = false; + + // Emit OpLine directives if present in the module. + // May not correspond exactly to original source, but should be a good approximation. + bool emit_line_directives = false; + + // In cases where readonly/writeonly decoration are not used at all, + // we try to deduce which qualifier(s) we should actually used, since actually emitting + // read-write decoration is very rare, and older glslang/HLSL compilers tend to just emit readwrite as a matter of fact. + // The default (true) is to enable automatic deduction for these cases, but if you trust the decorations set + // by the SPIR-V, it's recommended to set this to false. + bool enable_storage_image_qualifier_deduction = true; + + // On some targets (WebGPU), uninitialized variables are banned. + // If this is enabled, all variables (temporaries, Private, Function) + // which would otherwise be uninitialized will now be initialized to 0 instead. + bool force_zero_initialized_variables = false; + + // In GLSL, force use of I/O block flattening, similar to + // what happens on legacy GLSL targets for blocks and structs. + bool force_flattened_io_blocks = false; + + // For opcodes where we have to perform explicit additional nan checks, very ugly code is generated. + // If we opt-in, ignore these requirements. + // In opcodes like NClamp/NMin/NMax and FP compare, ignore NaN behavior. + // Use FClamp/FMin/FMax semantics for clamps and lets implementation choose ordered or unordered + // compares. + bool relax_nan_checks = false; + + // Loading row-major matrices from UBOs on older AMD Windows OpenGL drivers is problematic. + // To load these types correctly, we must generate a wrapper. them in a dummy function which only purpose is to + // ensure row_major decoration is actually respected. + // This workaround may cause significant performance degeneration on some Android devices. + bool enable_row_major_load_workaround = true; + + // If non-zero, controls layout(num_views = N) in; in GL_OVR_multiview2. + uint32_t ovr_multiview_view_count = 0; + + // Emit the entry point name in SPIR-V rather than "main". + bool use_entry_point_name = false; + + enum Precision + { + DontCare, + Lowp, + Mediump, + Highp + }; + + struct VertexOptions + { + // "Vertex-like shader" here is any shader stage that can write BuiltInPosition. + + // GLSL: In vertex-like shaders, rewrite [0, w] depth (Vulkan/D3D style) to [-w, w] depth (GL style). + // MSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. + // HLSL: In vertex-like shaders, rewrite [-w, w] depth (GL style) to [0, w] depth. + bool fixup_clipspace = false; + + // In vertex-like shaders, inverts gl_Position.y or equivalent. + bool flip_vert_y = false; + + // GLSL only, for HLSL version of this option, see CompilerHLSL. + // 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 FragmentOptions + { + // Add precision mediump float in ES targets when emitting GLES source. + // Add precision highp int in ES targets when emitting GLES source. + Precision default_float_precision = Mediump; + Precision default_int_precision = Highp; + } fragment; + }; + + void remap_pixel_local_storage(std::vector inputs, std::vector outputs) + { + pls_inputs = std::move(inputs); + pls_outputs = std::move(outputs); + remap_pls_variables(); + } + + // Redirect a subpassInput reading from input_attachment_index to instead load its value from + // the color attachment at location = color_location. Requires ESSL. + // If coherent, uses GL_EXT_shader_framebuffer_fetch, if not, uses noncoherent variant. + void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location, bool coherent); + + explicit CompilerGLSL(std::vector spirv_) + : Compiler(std::move(spirv_)) + { + init(); + } + + CompilerGLSL(const uint32_t *ir_, size_t word_count) + : Compiler(ir_, word_count) + { + init(); + } + + explicit CompilerGLSL(const ParsedIR &ir_) + : Compiler(ir_) + { + init(); + } + + explicit CompilerGLSL(ParsedIR &&ir_) + : Compiler(std::move(ir_)) + { + init(); + } + + const Options &get_common_options() const + { + return options; + } + + void set_common_options(const Options &opts) + { + options = opts; + } + + std::string compile() override; + + // Returns the current string held in the conversion buffer. Useful for + // capturing what has been converted so far when compile() throws an error. + std::string get_partial_source(); + + // Adds a line to be added right after #version in GLSL backend. + // This is useful for enabling custom extensions which are outside the scope of SPIRV-Cross. + // This can be combined with variable remapping. + // A new-line will be added. + // + // While add_header_line() is a more generic way of adding arbitrary text to the header + // of a GLSL file, require_extension() should be used when adding extensions since it will + // avoid creating collisions with SPIRV-Cross generated extensions. + // + // Code added via add_header_line() is typically backend-specific. + void add_header_line(const std::string &str); + + // Adds an extension which is required to run this shader, e.g. + // require_extension("GL_KHR_my_extension"); + void require_extension(const std::string &ext); + + // Returns the list of required extensions. After compilation this will contains any other + // extensions that the compiler used automatically, in addition to the user specified ones. + const SmallVector &get_required_extensions() const; + + // Legacy GLSL compatibility method. + // Takes a uniform or push constant variable and flattens it into a (i|u)vec4 array[N]; array instead. + // For this to work, all types in the block must be the same basic type, e.g. mixing vec2 and vec4 is fine, but + // mixing int and float is not. + // The name of the uniform array will be the same as the interface block name. + void flatten_buffer_block(VariableID id); + + // After compilation, query if a variable ID was used as a depth resource. + // This is meaningful for MSL since descriptor types depend on this knowledge. + // Cases which return true: + // - Images which are declared with depth = 1 image type. + // - Samplers which are statically used at least once with Dref opcodes. + // - Images which are statically used at least once with Dref opcodes. + bool variable_is_depth_or_compare(VariableID id) const; + + // If a shader output is active in this stage, but inactive in a subsequent stage, + // this can be signalled here. This can be used to work around certain cross-stage matching problems + // which plagues MSL and HLSL in certain scenarios. + // An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private + // variable. + // This option is only meaningful for MSL and HLSL, since GLSL matches by location directly. + // Masking builtins only takes effect if the builtin in question is part of the stage output interface. + void mask_stage_output_by_location(uint32_t location, uint32_t component); + void mask_stage_output_by_builtin(BuiltIn builtin); + + // Allow to control how to format float literals in the output. + // Set to "nullptr" to use the default "convert_to_string" function. + // This handle is not owned by SPIRV-Cross and must remain valid until compile() has been called. + void set_float_formatter(FloatFormatter *formatter) + { + float_formatter = formatter; + } + + // Returns the macro name corresponding to constant id + std::string constant_value_macro_name(uint32_t id) const; + + // Rather than using layout(descriptor_heap), emit layout(set, binding). + // This intended to be compatible with descriptor buffers, legacy descriptor indexing, + // or when the heap descriptors require unusual kinds of mapping in the Vulkan API + // which is not expressible by GLSL directly. + // + // ResourceTypeUnknown can be used as a default catch-all mapping. + // dim can be used to disambiguate between texel buffers and images since they are both image types, + // but use different descriptor types in the Vulkan API. + // No distinction is made between 1D/2D/3D/Cube textures. + // The default argument of DimMax maps to both texel buffers and images. + // dim is ignored for ResourceTypeUnknown. + void remap_descriptor_heap(ResourceType type, uint32_t desc_set, uint32_t binding, Dim dim = DimMax); + +protected: + struct ShaderSubgroupSupportHelper + { + // lower enum value = greater priority + enum Candidate + { + KHR_shader_subgroup_ballot, + KHR_shader_subgroup_basic, + KHR_shader_subgroup_vote, + KHR_shader_subgroup_arithmetic, + NV_gpu_shader_5, + NV_shader_thread_group, + NV_shader_thread_shuffle, + ARB_shader_ballot, + ARB_shader_group_vote, + AMD_gcn_shader, + + CandidateCount + }; + + static const char *get_extension_name(Candidate c); + static SmallVector get_extra_required_extension_names(Candidate c); + static const char *get_extra_required_extension_predicate(Candidate c); + + enum Feature + { + SubgroupMask = 0, + SubgroupSize = 1, + SubgroupInvocationID = 2, + SubgroupID = 3, + NumSubgroups = 4, + SubgroupBroadcast_First = 5, + SubgroupBallotFindLSB_MSB = 6, + SubgroupAll_Any_AllEqualBool = 7, + SubgroupAllEqualT = 8, + SubgroupElect = 9, + SubgroupBarrier = 10, + SubgroupMemBarrier = 11, + SubgroupBallot = 12, + SubgroupInverseBallot_InclBitCount_ExclBitCout = 13, + SubgroupBallotBitExtract = 14, + SubgroupBallotBitCount = 15, + SubgroupArithmeticIAddReduce = 16, + SubgroupArithmeticIAddExclusiveScan = 17, + SubgroupArithmeticIAddInclusiveScan = 18, + SubgroupArithmeticFAddReduce = 19, + SubgroupArithmeticFAddExclusiveScan = 20, + SubgroupArithmeticFAddInclusiveScan = 21, + SubgroupArithmeticIMulReduce = 22, + SubgroupArithmeticIMulExclusiveScan = 23, + SubgroupArithmeticIMulInclusiveScan = 24, + SubgroupArithmeticFMulReduce = 25, + SubgroupArithmeticFMulExclusiveScan = 26, + SubgroupArithmeticFMulInclusiveScan = 27, + FeatureCount + }; + + using FeatureMask = uint32_t; + static_assert(sizeof(FeatureMask) * 8u >= FeatureCount, "Mask type needs more bits."); + + using CandidateVector = SmallVector; + using FeatureVector = SmallVector; + + static FeatureVector get_feature_dependencies(Feature feature); + static FeatureMask get_feature_dependency_mask(Feature feature); + static bool can_feature_be_implemented_without_extensions(Feature feature); + static Candidate get_KHR_extension_for_feature(Feature feature); + + struct Result + { + Result(); + uint32_t weights[CandidateCount]; + }; + + void request_feature(Feature feature); + bool is_feature_requested(Feature feature) const; + Result resolve() const; + + static CandidateVector get_candidates_for_feature(Feature ft, const Result &r); + + private: + static CandidateVector get_candidates_for_feature(Feature ft); + static FeatureMask build_mask(const SmallVector &features); + FeatureMask feature_mask = 0; + }; + + // TODO remove this function when all subgroup ops are supported (or make it always return true) + static bool is_supported_subgroup_op_in_opengl(Op op, const uint32_t *ops); + + void reset(uint32_t iteration_count); + void emit_function(SPIRFunction &func, const Bitset &return_flags); + + bool has_extension(const std::string &ext) const; + void require_extension_internal(const std::string &ext); + + // Virtualize methods which need to be overridden by subclass targets like C++ and such. + virtual void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags); + + SPIRBlock *current_emitting_block = nullptr; + SmallVector current_emitting_switch_stack; + bool current_emitting_switch_fallthrough = false; + + virtual void emit_instruction(const Instruction &instr); + struct TemporaryCopy + { + uint32_t dst_id; + uint32_t src_id; + }; + TemporaryCopy handle_instruction_precision(const Instruction &instr); + void emit_block_instructions(SPIRBlock &block); + void emit_block_instructions_with_masked_debug(SPIRBlock &block); + + // For relax_nan_checks. + GLSLstd450 get_remapped_glsl_op(GLSLstd450 std450_op) const; + Op get_remapped_spirv_op(Op op) const; + + virtual void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count); + virtual void emit_spv_amd_shader_ballot_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_shader_explicit_vertex_parameter_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count); + void emit_non_semantic_shader_debug_info(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count); + virtual void emit_header(); + void emit_line_directive(uint32_t file_id, uint32_t line_literal); + void build_workgroup_size(SmallVector &arguments, const SpecializationConstant &x, + const SpecializationConstant &y, const SpecializationConstant &z); + + void request_subgroup_feature(ShaderSubgroupSupportHelper::Feature feature); + + virtual void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id); + virtual void emit_texture_op(const Instruction &i, bool sparse); + virtual std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions); + virtual void emit_subgroup_op(const Instruction &i); + virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0); + virtual std::string builtin_to_glsl(BuiltIn builtin, StorageClass storage); + virtual void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = "", uint32_t base_offset = 0); + virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0, bool member = false); + std::string constant_expression(const SPIRConstant &c, + bool inside_block_like_struct_scope = false, + bool inside_struct_scope = false); + virtual std::string constant_op_expression(const SPIRConstantOp &cop); + virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector); + virtual void emit_fixup(); + virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0); + virtual bool variable_decl_is_remapped_storage(const SPIRVariable &var, StorageClass storage) const; + virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id); + virtual void emit_workgroup_initialization(const SPIRVariable &var); + + struct TextureFunctionBaseArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionBaseArguments() = default; + VariableID img = 0; + const SPIRType *imgtype = nullptr; + bool is_fetch = false, is_gather = false, is_proj = false; + }; + + struct TextureFunctionNameArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionNameArguments() = default; + TextureFunctionBaseArguments base; + bool has_array_offsets = false, has_offset = false, has_grad = false; + bool has_dref = false, is_sparse_feedback = false, has_min_lod = false; + uint32_t lod = 0; + }; + virtual std::string to_function_name(const TextureFunctionNameArguments &args); + + struct TextureFunctionArguments + { + // GCC 4.8 workarounds, it doesn't understand '{}' constructor here, use explicit default constructor. + TextureFunctionArguments() = default; + TextureFunctionBaseArguments base; + uint32_t coord = 0, coord_components = 0, dref = 0; + uint32_t grad_x = 0, grad_y = 0, lod = 0, offset = 0; + uint32_t bias = 0, component = 0, sample = 0, sparse_texel = 0, min_lod = 0; + bool nonuniform_expression = false, has_array_offsets = false; + }; + virtual std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward); + + void emit_sparse_feedback_temporaries(uint32_t result_type_id, uint32_t id, uint32_t &feedback_id, + uint32_t &texel_id); + uint32_t get_sparse_feedback_texel_id(uint32_t id) const; + virtual void emit_buffer_block(const SPIRVariable &type); + virtual void emit_push_constant_block(const SPIRVariable &var); + virtual void emit_uniform(const SPIRVariable &var); + virtual std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool packed_type, bool row_major); + + virtual bool builtin_translates_to_nonarray(BuiltIn builtin) const; + + virtual bool is_user_type_structured(uint32_t id) const; + + void emit_copy_logical_type(uint32_t lhs_id, uint32_t lhs_type_id, uint32_t rhs_id, uint32_t rhs_type_id, + SmallVector chain); + + StringStream<> buffer; + + template + inline void statement_inner(T &&t) + { + buffer << std::forward(t); + statement_count++; + } + + template + inline void statement_inner(T &&t, Ts &&... ts) + { + buffer << std::forward(t); + statement_count++; + statement_inner(std::forward(ts)...); + } + + template + inline void statement(Ts &&... ts) + { + if (is_forcing_recompilation()) + { + // Do not bother emitting code while force_recompile is active. + // We will compile again. + statement_count++; + return; + } + + if (redirect_statement) + { + redirect_statement->push_back(join(std::forward(ts)...)); + statement_count++; + } + else + { + for (uint32_t i = 0; i < indent; i++) + buffer << " "; + statement_inner(std::forward(ts)...); + buffer << '\n'; + } + } + + template + inline void statement_no_indent(Ts &&... ts) + { + auto old_indent = indent; + indent = 0; + statement(std::forward(ts)...); + indent = old_indent; + } + + // Used for implementing continue blocks where + // we want to obtain a list of statements we can merge + // on a single line separated by comma. + SmallVector *redirect_statement = nullptr; + const SPIRBlock *current_continue_block = nullptr; + bool block_temporary_hoisting = false; + bool block_debug_directives = false; + + void begin_scope(); + void end_scope(); + void end_scope(const std::string &trailer); + void end_scope_decl(); + void end_scope_decl(const std::string &decl); + + Options options; + + // Allow Metal to use the array template to make arrays a value type + virtual std::string type_to_array_glsl(const SPIRType &type, uint32_t variable_id); + std::string to_array_size(const SPIRType &type, uint32_t index); + uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const; + uint32_t to_array_size_literal(const SPIRType &type) const; + virtual std::string variable_decl(const SPIRVariable &variable); // Threadgroup arrays can't have a wrapper type + std::string variable_decl_function_local(SPIRVariable &variable); + + void add_local_variable_name(uint32_t id); + void add_resource_name(uint32_t id); + void add_member_name(SPIRType &type, uint32_t name); + void add_function_overload(const SPIRFunction &func); + + virtual bool is_non_native_row_major_matrix(uint32_t id); + virtual bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index); + bool member_is_remapped_physical_type(const SPIRType &type, uint32_t index) const; + bool member_is_packed_physical_type(const SPIRType &type, uint32_t index) const; + virtual std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, + uint32_t physical_type_id, bool is_packed, + bool relaxed = false); + + std::unordered_set local_variable_names; + std::unordered_set resource_names; + std::unordered_set block_input_names; + std::unordered_set block_output_names; + std::unordered_set block_ubo_names; + std::unordered_set block_ssbo_names; + std::unordered_set block_names; // A union of all block_*_names. + std::unordered_map> function_overloads; + std::unordered_map preserved_aliases; + void preserve_alias_on_reset(uint32_t id); + void reset_name_caches(); + + bool processing_entry_point = false; + + // Can be overriden by subclass backends for trivial things which + // shouldn't need polymorphism. + struct BackendVariations + { + std::string discard_literal = "discard"; + std::string demote_literal = "demote"; + std::string null_pointer_literal = ""; + bool float_literal_suffix = false; + bool double_literal_suffix = true; + bool uint32_t_literal_suffix = true; + bool long_long_literal_suffix = false; + const char *basic_int_type = "int"; + const char *basic_uint_type = "uint"; + const char *basic_int8_type = "int8_t"; + const char *basic_uint8_type = "uint8_t"; + const char *basic_int16_type = "int16_t"; + const char *basic_uint16_type = "uint16_t"; + const char *int16_t_literal_suffix = "s"; + const char *uint16_t_literal_suffix = "us"; + const char *nonuniform_qualifier = "nonuniformEXT"; + const char *boolean_mix_function = "mix"; + const char *printf_function = "debugPrintfEXT"; + std::string constant_null_initializer = ""; + SPIRType::BaseType boolean_in_struct_remapped_type = SPIRType::Boolean; + bool swizzle_is_function = false; + bool shared_is_implied = false; + bool unsized_array_supported = true; + bool explicit_struct_type = false; + bool use_initializer_list = false; + bool use_typed_initializer_list = false; + bool requires_matching_array_initializer = false; + bool can_declare_struct_inline = true; + bool can_declare_arrays_inline = true; + bool native_row_major_matrix = true; + bool use_constructor_splatting = true; + bool allow_precision_qualifiers = false; + bool can_swizzle_scalar = false; + bool force_gl_in_out_block = false; + bool force_merged_mesh_block = false; + bool can_return_array = true; + bool allow_truncated_access_chain = false; + bool supports_extensions = false; + bool supports_empty_struct = false; + bool array_is_value_type = true; + bool array_is_value_type_in_buffer_blocks = true; + bool comparison_image_samples_scalar = false; + bool native_pointers = false; + bool support_small_type_sampling_result = false; + bool support_case_fallthrough = true; + bool use_array_constructor = false; + bool needs_row_major_load_workaround = false; + bool support_pointer_to_pointer = false; + bool support_precise_qualifier = false; + bool support_64bit_switch = false; + bool workgroup_size_is_hidden = false; + bool requires_relaxed_precision_analysis = false; + bool implicit_c_integer_promotion_rules = false; + bool supports_spec_constant_array_size = true; + bool requires_phi_undef_zero_init = false; + } backend; + + void emit_struct(SPIRType &type); + void emit_resources(); + void emit_extension_workarounds(ExecutionModel model); + void emit_subgroup_arithmetic_workaround(const std::string &func, Op op, GroupOperation group_op); + void emit_polyfills(uint32_t polyfills, bool relaxed); + void emit_buffer_block_native(const SPIRVariable *var, const DescriptorHeapMeta *heap_meta = nullptr); + std::string to_buffer_pointer_name_prefix(uint32_t ptr_id) const; + static std::string heap_meta_to_prefix(const DescriptorHeapMeta &meta); + void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration); + void emit_buffer_block_legacy(const SPIRVariable &var); + void emit_buffer_block_flattened(const SPIRVariable &type); + void fixup_implicit_builtin_block_names(ExecutionModel model); + void emit_declared_builtin_block(StorageClass storage, ExecutionModel model); + bool should_force_emit_builtin_block(StorageClass storage); + void emit_push_constant_block_vulkan(const SPIRVariable &var); + void emit_push_constant_block_glsl(const SPIRVariable &var); + void emit_interface_block(const SPIRVariable &type); + void emit_flattened_io_block(const SPIRVariable &var, const char *qual); + void emit_flattened_io_block_struct(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices); + void emit_flattened_io_block_member(const std::string &basename, const SPIRType &type, const char *qual, + const SmallVector &indices); + void emit_block_chain(SPIRBlock &block); + BlockID emit_block_chain_inner(SPIRBlock &block); + void emit_block_chain_cleanup(SPIRBlock &block); + void emit_hoisted_temporaries(SmallVector> &temporaries); + int get_constant_mapping_to_workgroup_component(const SPIRConstant &constant) const; + void emit_constant(const SPIRConstant &constant); + void emit_specialization_constant_op(const SPIRConstantOp &constant); + std::string emit_continue_block(uint32_t continue_block, bool follow_true_block, bool follow_false_block); + bool attempt_emit_loop_header(SPIRBlock &block, SPIRBlock::Method method); + + void branch(BlockID from, BlockID to); + void branch_to_continue(BlockID from, BlockID to); + void branch(BlockID from, uint32_t cond, BlockID true_block, BlockID false_block); + void flush_phi(BlockID from, BlockID to); + void flush_variable_declaration(uint32_t id); + void flush_undeclared_variables(SPIRBlock &block); + void emit_variable_temporary_copies(const SPIRVariable &var); + + bool should_dereference(uint32_t id); + bool should_dereference_caller_param(uint32_t id); + bool should_forward(uint32_t id) const; + bool should_suppress_usage_tracking(uint32_t id) const; + void emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, uint32_t right, uint32_t lerp); + void emit_nminmax_op(uint32_t result_type, uint32_t id, uint32_t op0, uint32_t op1, GLSLstd450 op); + void emit_emulated_ahyper_op(uint32_t result_type, uint32_t result_id, uint32_t op0, GLSLstd450 op); + bool to_trivial_mix_op(const SPIRType &type, std::string &op, uint32_t left, uint32_t right, uint32_t lerp); + void emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + uint32_t op3, const char *op); + void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + const char *op); + void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op); + + void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op, + SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type); + void emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + SPIRType::BaseType input_type, bool skip_cast_if_equal_type); + void emit_binary_func_op_cast_clustered(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op, SPIRType::BaseType input_type); + void emit_trinary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + const char *op, SPIRType::BaseType input_type); + void emit_trinary_func_op_bitextract(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + uint32_t op2, const char *op, SPIRType::BaseType expected_result_type, + SPIRType::BaseType input_type0, SPIRType::BaseType input_type1, + SPIRType::BaseType input_type2); + void emit_bitfield_insert_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, + uint32_t op3, const char *op, SPIRType::BaseType offset_count_type); + + void emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); + void emit_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op); + void emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_unrolled_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + bool negate, SPIRType::BaseType expected_type); + void emit_binary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op, + SPIRType::BaseType input_type, bool skip_cast_if_equal_type, bool implicit_integer_promotion); + + 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); + + virtual bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0); + + 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); + void emit_unary_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op); + virtual void emit_mesh_tasks(SPIRBlock &block); + bool expression_is_forwarded(uint32_t id) const; + bool expression_suppresses_usage_tracking(uint32_t id) const; + bool expression_read_implies_multiple_reads(uint32_t id) const; + SPIRExpression &emit_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs, + bool suppress_usage_tracking = false); + void emit_transposed_op(uint32_t result_type, uint32_t result_id, const std::string &rhs, bool forward_rhs); + + void access_chain_internal_append_index(std::string &expr, uint32_t base, const SPIRType *type, + AccessChainFlags flags, bool &access_chain_is_arrayed, uint32_t index); + + std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags, + AccessChainMeta *meta, const SPIRType *untyped_data_type); + + // Only meaningful on backends with physical pointer support ala MSL. + // Relevant for PtrAccessChain / BDA. + virtual uint32_t get_physical_type_id_stride(TypeID type_id) const; + + StorageClass get_expression_effective_storage_class(uint32_t ptr); + virtual bool access_chain_needs_stage_io_builtin_translation(uint32_t base); + + virtual bool check_physical_type_cast(std::string &expr, const SPIRType *type, uint32_t physical_type); + virtual bool prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, + StorageClass storage, bool &is_packed); + + std::string access_chain(uint32_t base, const uint32_t *indices, uint32_t count, const SPIRType &target_type, + AccessChainMeta *meta = nullptr, bool ptr_chain = false, + const SPIRType *untyped_data_type = nullptr); + + std::string flattened_access_chain(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + uint32_t array_stride, bool need_transpose); + std::string flattened_access_chain_struct(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset); + std::string flattened_access_chain_matrix(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + bool need_transpose); + std::string flattened_access_chain_vector(uint32_t base, const uint32_t *indices, uint32_t count, + const SPIRType &target_type, uint32_t offset, uint32_t matrix_stride, + bool need_transpose); + std::pair flattened_access_chain_offset(const SPIRType &basetype, const uint32_t *indices, + uint32_t count, uint32_t offset, + uint32_t word_stride, bool *need_transpose = nullptr, + uint32_t *matrix_stride = nullptr, + uint32_t *array_stride = nullptr, + bool ptr_chain = false); + + const char *index_to_swizzle(uint32_t index); + std::string remap_swizzle(const SPIRType &result_type, uint32_t input_components, const std::string &expr); + std::string declare_temporary(uint32_t type, uint32_t id); + bool can_declare_inline_temporary(uint32_t id) const; + void emit_uninitialized_temporary(uint32_t type, uint32_t id); + SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id); + virtual void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist); + std::string to_non_uniform_aware_expression(uint32_t id); + std::string to_atomic_ptr_expression(uint32_t id); + std::string to_pretty_expression_if_int_constant( + uint32_t id, + const GlslConstantNameMapping *mapping_start, const GlslConstantNameMapping *mapping_end, + bool register_expression_read = true); + std::string to_expression(uint32_t id, bool register_expression_read = true); + std::string to_composite_constructor_expression(const SPIRType &parent_type, uint32_t id, bool block_like_type); + std::string to_rerolled_array_expression(const SPIRType &parent_type, const std::string &expr, const SPIRType &type); + std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true); + std::string to_unpacked_expression(uint32_t id, bool register_expression_read = true); + std::string to_unpacked_row_major_matrix_expression(uint32_t id); + std::string to_enclosed_unpacked_expression(uint32_t id, bool register_expression_read = true); + std::string to_dereferenced_expression(uint32_t id, bool register_expression_read = true); + std::string to_pointer_expression(uint32_t id, bool register_expression_read = true); + std::string to_enclosed_pointer_expression(uint32_t id, bool register_expression_read = true); + std::string to_extract_component_expression(uint32_t id, uint32_t index); + std::string to_extract_constant_composite_expression(uint32_t result_type, const SPIRConstant &c, + const uint32_t *chain, uint32_t length); + static bool needs_enclose_expression(const std::string &expr); + std::string enclose_expression(const std::string &expr); + std::string dereference_expression(const SPIRType &expression_type, const std::string &expr); + std::string address_of_expression(const std::string &expr); + void strip_enclosed_expression(std::string &expr); + std::string to_member_name(const SPIRType &type, uint32_t index); + virtual std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain_is_resolved); + std::string to_multi_member_reference(const SPIRType &type, const SmallVector &indices); + std::string type_to_glsl_constructor(const SPIRType &type); + std::string argument_decl(const SPIRFunction::Parameter &arg); + virtual std::string to_qualifiers_glsl(uint32_t id); + void fixup_io_block_patch_primitive_qualifiers(const SPIRVariable &var); + void emit_output_variable_initializer(const SPIRVariable &var); + std::string to_precision_qualifiers_glsl(uint32_t id); + virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var); + std::string flags_to_qualifiers_glsl(const SPIRType &type, uint32_t id, const Bitset &flags); + const char *format_to_glsl(ImageFormat format); + virtual std::string layout_for_member(const SPIRType &type, uint32_t index); + virtual std::string to_interpolation_qualifiers(const Bitset &flags); + std::string layout_for_variable(const SPIRVariable &variable); + std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id); + virtual bool skip_argument(uint32_t id) const; + virtual bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, + StorageClass lhs_storage, StorageClass rhs_storage); + virtual void emit_block_hints(const SPIRBlock &block); + virtual std::string to_initializer_expression(const SPIRVariable &var); + virtual std::string to_zero_initialized_expression(uint32_t type_id); + bool type_can_zero_initialize(const SPIRType &type) const; + + bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, + uint32_t *failed_index = nullptr, uint32_t start_offset = 0, + uint32_t end_offset = ~(0u)); + std::string buffer_to_packing_standard(const SPIRType &type, + bool support_std430_without_scalar_layout, + bool support_enhanced_layouts); + + uint32_t type_to_packed_base_size(const SPIRType &type, BufferPackingStandard packing); + uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing); + uint32_t type_to_location_count(const SPIRType &type) const; + + std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg); + virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type); + + std::string bitcast_expression(SPIRType::BaseType target_type, uint32_t arg); + std::string bitcast_expression(const SPIRType &target_type, SPIRType::BaseType expr_type, const std::string &expr); + + std::string build_composite_combiner(uint32_t result_type, const uint32_t *elems, uint32_t length); + bool remove_duplicate_swizzle(std::string &op); + bool remove_unity_swizzle(uint32_t base, std::string &op); + + // Can modify flags to remote readonly/writeonly if image type + // and force recompile. + bool check_atomic_image(uint32_t id); + + virtual void replace_illegal_names(); + void replace_illegal_names(const std::unordered_set &keywords); + virtual void emit_entry_point_declarations(); + + void replace_fragment_output(SPIRVariable &var); + void replace_fragment_outputs(); + std::string legacy_tex_op(const std::string &op, const SPIRType &imgtype, uint32_t id); + + void forward_relaxed_precision(uint32_t dst_id, const uint32_t *args, uint32_t length); + void analyze_precision_requirements(uint32_t type_id, uint32_t dst_id, uint32_t *args, uint32_t length); + Options::Precision analyze_expression_precision(const uint32_t *args, uint32_t length) const; + + uint32_t indent = 0; + + std::unordered_set emitted_functions; + + // Ensure that we declare phi-variable copies even if the original declaration isn't deferred + std::unordered_set flushed_phi_variables; + + std::unordered_set flattened_buffer_blocks; + std::unordered_map flattened_structs; + + ShaderSubgroupSupportHelper shader_subgroup_supporter; + + std::string load_flattened_struct(const std::string &basename, const SPIRType &type); + std::string to_flattened_struct_member(const std::string &basename, const SPIRType &type, uint32_t index); + void store_flattened_struct(uint32_t lhs_id, uint32_t value); + void store_flattened_struct(const std::string &basename, uint32_t rhs, const SPIRType &type, + const SmallVector &indices); + std::string to_flattened_access_chain_expression(uint32_t id); + + // Usage tracking. If a temporary is used more than once, use the temporary instead to + // avoid AST explosion when SPIRV is generated with pure SSA and doesn't write stuff to variables. + std::unordered_map expression_usage_counts; + void track_expression_read(uint32_t id); + + SmallVector forced_extensions; + SmallVector header_lines; + + // Used when expressions emit extra opcodes with their own unique IDs, + // and we need to reuse the IDs across recompilation loops. + // Currently used by NMin/Max/Clamp implementations. + std::unordered_map extra_sub_expressions; + + SmallVector workaround_ubo_load_overload_types; + void request_workaround_wrapper_overload(TypeID id); + void rewrite_load_for_wrapped_row_major(std::string &expr, TypeID loaded_type, ID ptr); + + uint32_t statement_count = 0; + + inline bool is_legacy() const + { + return (options.es && options.version < 300) || (!options.es && options.version < 130); + } + + inline bool is_legacy_es() const + { + return options.es && options.version < 300; + } + + inline bool is_legacy_desktop() const + { + return !options.es && options.version < 130; + } + + enum Polyfill : uint32_t + { + PolyfillTranspose2x2 = 1 << 0, + PolyfillTranspose3x3 = 1 << 1, + PolyfillTranspose4x4 = 1 << 2, + PolyfillDeterminant2x2 = 1 << 3, + PolyfillDeterminant3x3 = 1 << 4, + PolyfillDeterminant4x4 = 1 << 5, + PolyfillMatrixInverse2x2 = 1 << 6, + PolyfillMatrixInverse3x3 = 1 << 7, + PolyfillMatrixInverse4x4 = 1 << 8, + PolyfillNMin16 = 1 << 9, + PolyfillNMin32 = 1 << 10, + PolyfillNMin64 = 1 << 11, + PolyfillNMax16 = 1 << 12, + PolyfillNMax32 = 1 << 13, + PolyfillNMax64 = 1 << 14, + PolyfillNClamp16 = 1 << 15, + PolyfillNClamp32 = 1 << 16, + PolyfillNClamp64 = 1 << 17, + }; + + uint32_t required_polyfills = 0; + uint32_t required_polyfills_relaxed = 0; + void require_polyfill(Polyfill polyfill, bool relaxed); + + bool ray_tracing_is_khr = false; + bool barycentric_is_nv = false; + void ray_tracing_khr_fixup_locations(); + + bool args_will_forward(uint32_t id, const uint32_t *args, uint32_t num_args, bool pure); + void register_call_out_argument(uint32_t id); + void register_impure_function_call(); + void register_control_dependent_expression(uint32_t expr); + + // GL_EXT_shader_pixel_local_storage support. + std::vector pls_inputs; + std::vector pls_outputs; + std::string pls_decl(const PlsRemap &variable); + const char *to_pls_qualifiers_glsl(const SPIRVariable &variable); + void emit_pls(); + void remap_pls_variables(); + + // GL_EXT_shader_framebuffer_fetch support. + std::vector> subpass_to_framebuffer_fetch_attachment; + std::vector> inout_color_attachments; + bool location_is_framebuffer_fetch(uint32_t location) const; + bool location_is_non_coherent_framebuffer_fetch(uint32_t location) const; + bool subpass_input_is_framebuffer_fetch(uint32_t id) const; + void emit_inout_fragment_outputs_copy_to_subpass_inputs(); + const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const; + const SPIRVariable *find_color_output_by_location(uint32_t location) const; + + // A variant which takes two sets of name. The secondary is only used to verify there are no collisions, + // but the set is not updated when we have found a new name. + // Used primarily when adding block interface names. + void add_variable(std::unordered_set &variables_primary, + const std::unordered_set &variables_secondary, std::string &name); + + void check_function_call_constraints(const uint32_t *args, uint32_t length); + void handle_invalid_expression(uint32_t id); + void force_temporary_and_recompile(uint32_t id); + void find_static_extensions(); + + uint32_t consume_temporary_in_precision_context(uint32_t type_id, uint32_t id, Options::Precision precision); + std::unordered_map temporary_to_mirror_precision_alias; + std::unordered_set composite_insert_overwritten; + std::unordered_set block_composite_insert_overwrite; + + std::string emit_for_loop_initializers(const SPIRBlock &block); + void emit_while_loop_initializers(const SPIRBlock &block); + std::string undef_loop_variable_initializer_suffix(const SPIRVariable &var); + bool for_loop_initializers_are_same_type(const SPIRBlock &block); + bool optimize_read_modify_write(const SPIRType &type, const std::string &lhs, const std::string &rhs); + void fixup_image_load_store_access(); + + bool type_is_empty(const SPIRType &type); + + bool can_use_io_location(StorageClass storage, bool block); + const Instruction *get_next_instruction_in_block(const Instruction &instr); + static uint32_t mask_relevant_memory_semantics(uint32_t semantics); + + std::string convert_floate4m3_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_floate5m2_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_half_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_float_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + std::string convert_double_to_string(const SPIRConstant &value, uint32_t col, uint32_t row); + + std::string convert_separate_image_to_expression(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 casts on load and store to make this work. + virtual SPIRType::BaseType get_builtin_basetype(BuiltIn builtin, SPIRType::BaseType default_type); + virtual void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type); + virtual void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type); + void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr); + bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id); + void convert_non_uniform_expression(std::string &expr, uint32_t ptr_id); + + void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id); + void disallow_forwarding_in_expression_chain(const SPIRExpression &expr); + + bool expression_is_constant_null(uint32_t id) const; + bool expression_is_non_value_type_array(uint32_t value_type_id, uint32_t ptr); + virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression); + + uint32_t get_integer_width_for_instruction(const Instruction &instr) const; + uint32_t get_integer_width_for_glsl_instruction(GLSLstd450 op, const uint32_t *arguments, uint32_t length) const; + + bool variable_is_lut(const SPIRVariable &var) const; + + char current_locale_radix_character = '.'; + + void fixup_type_alias(); + void reorder_type_alias(); + void fixup_anonymous_struct_names(); + void fixup_anonymous_struct_names(std::unordered_set &visited, const SPIRType &type); + + static const char *vector_swizzle(int vecsize, int index); + + bool is_stage_output_location_masked(uint32_t location, uint32_t component) const; + bool is_stage_output_builtin_masked(BuiltIn builtin) const; + bool is_stage_output_variable_masked(const SPIRVariable &var) const; + bool is_stage_output_block_member_masked(const SPIRVariable &var, uint32_t index, bool strip_array) const; + bool is_per_primitive_variable(const SPIRVariable &var) const; + uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const; + uint32_t get_declared_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const; + std::unordered_set masked_output_locations; + std::unordered_set masked_output_builtins; + + FloatFormatter *float_formatter = nullptr; + std::string format_float(float value) const; + std::string format_double(double value) const; + + uint32_t get_fp_fast_math_flags_for_op(uint32_t result_type, uint32_t id) const; + bool has_legacy_nocontract(uint32_t result_type, uint32_t id) const; + + struct DescriptorHeapMapping + { + ResourceType type; + uint32_t desc_set; + uint32_t binding; + Dim dim; + }; + SmallVector descriptor_heap_mappings; + bool is_descriptor_non_uniform(uint32_t id) const; + std::string to_descriptor_heap_layout(const SPIRType &type, StorageClass storage = StorageClassUniformConstant) const; + +private: + void init(); + + SmallVector get_composite_constant_ids(ConstantID const_id); + void fill_composite_constant(SPIRConstant &constant, TypeID type_id, const SmallVector &initializers); + void set_composite_constant(ConstantID const_id, TypeID type_id, const SmallVector &initializers); + TypeID get_composite_member_type(TypeID type_id, uint32_t member_idx); + std::unordered_map> const_composite_insert_ids; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_hlsl.cpp b/thirdparty/SPIRV-Cross/spirv_hlsl.cpp new file mode 100644 index 000000000..60ec93b5f --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_hlsl.cpp @@ -0,0 +1,7320 @@ +/* + * Copyright 2016-2021 Robert Konrad + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + * + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_hlsl.hpp" +#include "GLSL.std.450.h" +#include +#include + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +enum class ImageFormatNormalizedState +{ + None = 0, + Unorm = 1, + Snorm = 2 +}; + +static ImageFormatNormalizedState image_format_to_normalized_state(ImageFormat fmt) +{ + switch (fmt) + { + case ImageFormatR8: + case ImageFormatR16: + case ImageFormatRg8: + case ImageFormatRg16: + case ImageFormatRgba8: + case ImageFormatRgba16: + case ImageFormatRgb10A2: + return ImageFormatNormalizedState::Unorm; + + case ImageFormatR8Snorm: + case ImageFormatR16Snorm: + case ImageFormatRg8Snorm: + case ImageFormatRg16Snorm: + case ImageFormatRgba8Snorm: + case ImageFormatRgba16Snorm: + return ImageFormatNormalizedState::Snorm; + + default: + break; + } + + return ImageFormatNormalizedState::None; +} + +static unsigned image_format_to_components(ImageFormat fmt) +{ + switch (fmt) + { + case ImageFormatR8: + case ImageFormatR16: + case ImageFormatR8Snorm: + case ImageFormatR16Snorm: + case ImageFormatR16f: + case ImageFormatR32f: + case ImageFormatR8i: + case ImageFormatR16i: + case ImageFormatR32i: + case ImageFormatR8ui: + case ImageFormatR16ui: + case ImageFormatR32ui: + return 1; + + case ImageFormatRg8: + case ImageFormatRg16: + case ImageFormatRg8Snorm: + case ImageFormatRg16Snorm: + case ImageFormatRg16f: + case ImageFormatRg32f: + case ImageFormatRg8i: + case ImageFormatRg16i: + case ImageFormatRg32i: + case ImageFormatRg8ui: + case ImageFormatRg16ui: + case ImageFormatRg32ui: + return 2; + + case ImageFormatR11fG11fB10f: + return 3; + + case ImageFormatRgba8: + case ImageFormatRgba16: + case ImageFormatRgb10A2: + case ImageFormatRgba8Snorm: + case ImageFormatRgba16Snorm: + case ImageFormatRgba16f: + case ImageFormatRgba32f: + case ImageFormatRgba8i: + case ImageFormatRgba16i: + case ImageFormatRgba32i: + case ImageFormatRgba8ui: + case ImageFormatRgba16ui: + case ImageFormatRgba32ui: + case ImageFormatRgb10a2ui: + return 4; + + case ImageFormatUnknown: + return 4; // Assume 4. + + default: + SPIRV_CROSS_THROW("Unrecognized typed image format."); + } +} + +static string image_format_to_type(ImageFormat fmt, SPIRType::BaseType basetype) +{ + switch (fmt) + { + case ImageFormatR8: + case ImageFormatR16: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float"; + case ImageFormatRg8: + case ImageFormatRg16: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float2"; + case ImageFormatRgba8: + case ImageFormatRgba16: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float4"; + case ImageFormatRgb10A2: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "unorm float4"; + + case ImageFormatR8Snorm: + case ImageFormatR16Snorm: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "snorm float"; + case ImageFormatRg8Snorm: + case ImageFormatRg16Snorm: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "snorm float2"; + case ImageFormatRgba8Snorm: + case ImageFormatRgba16Snorm: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "snorm float4"; + + case ImageFormatR16f: + case ImageFormatR32f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float"; + case ImageFormatRg16f: + case ImageFormatRg32f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float2"; + case ImageFormatRgba16f: + case ImageFormatRgba32f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float4"; + + case ImageFormatR11fG11fB10f: + if (basetype != SPIRType::Float) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "float3"; + + case ImageFormatR8i: + case ImageFormatR16i: + case ImageFormatR32i: + if (basetype != SPIRType::Int) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "int"; + case ImageFormatRg8i: + case ImageFormatRg16i: + case ImageFormatRg32i: + if (basetype != SPIRType::Int) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "int2"; + case ImageFormatRgba8i: + case ImageFormatRgba16i: + case ImageFormatRgba32i: + if (basetype != SPIRType::Int) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "int4"; + + case ImageFormatR8ui: + case ImageFormatR16ui: + case ImageFormatR32ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint"; + case ImageFormatRg8ui: + case ImageFormatRg16ui: + case ImageFormatRg32ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint2"; + case ImageFormatRgba8ui: + case ImageFormatRgba16ui: + case ImageFormatRgba32ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint4"; + case ImageFormatRgb10a2ui: + if (basetype != SPIRType::UInt) + SPIRV_CROSS_THROW("Mismatch in image type and base type of image."); + return "uint4"; + + case ImageFormatUnknown: + switch (basetype) + { + case SPIRType::Float: + return "float4"; + case SPIRType::Int: + return "int4"; + case SPIRType::UInt: + return "uint4"; + default: + SPIRV_CROSS_THROW("Unsupported base type for image."); + } + + default: + SPIRV_CROSS_THROW("Unrecognized typed image format."); + } +} + +string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type, uint32_t id) +{ + auto &imagetype = get(type.image.type); + const char *dim = nullptr; + bool typed_load = false; + uint32_t components = 4; + + bool force_image_srv = hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(id, DecorationNonWritable); + + switch (type.image.dim) + { + case Dim1D: + typed_load = type.image.sampled == 2; + dim = "1D"; + break; + case Dim2D: + typed_load = type.image.sampled == 2; + dim = "2D"; + break; + case Dim3D: + typed_load = type.image.sampled == 2; + dim = "3D"; + break; + case DimCube: + if (type.image.sampled == 2) + SPIRV_CROSS_THROW("RWTextureCube does not exist in HLSL."); + dim = "Cube"; + break; + case DimRect: + SPIRV_CROSS_THROW("Rectangle texture support is not yet implemented for HLSL."); // TODO + case DimBuffer: + if (type.image.sampled == 1) + return join("Buffer<", type_to_glsl(imagetype), components, ">"); + else if (type.image.sampled == 2) + { + if (interlocked_resources.count(id)) + return join("RasterizerOrderedBuffer<", image_format_to_type(type.image.format, imagetype.basetype), + ">"); + + typed_load = !force_image_srv && type.image.sampled == 2; + + const char *rw = force_image_srv ? "" : "RW"; + return join(rw, "Buffer<", + typed_load ? image_format_to_type(type.image.format, imagetype.basetype) : + join(type_to_glsl(imagetype), components), + ">"); + } + else + SPIRV_CROSS_THROW("Sampler buffers must be either sampled or unsampled. Cannot deduce in runtime."); + case DimSubpassData: + dim = "2D"; + typed_load = false; + break; + default: + SPIRV_CROSS_THROW("Invalid dimension."); + } + const char *arrayed = type.image.arrayed ? "Array" : ""; + const char *ms = type.image.ms ? "MS" : ""; + const char *rw = typed_load && !force_image_srv ? "RW" : ""; + + if (force_image_srv) + typed_load = false; + + if (typed_load && interlocked_resources.count(id)) + rw = "RasterizerOrdered"; + + return join(rw, "Texture", dim, ms, arrayed, "<", + typed_load ? image_format_to_type(type.image.format, imagetype.basetype) : + join(type_to_glsl(imagetype), components), + ">"); +} + +string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type, uint32_t /*id*/) +{ + auto &imagetype = get(type.image.type); + string res; + + switch (imagetype.basetype) + { + case SPIRType::Int: + res = "i"; + break; + case SPIRType::UInt: + res = "u"; + break; + default: + break; + } + + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData) + return res + "subpassInput" + (type.image.ms ? "MS" : ""); + + // If we're emulating subpassInput with samplers, force sampler2D + // so we don't have to specify format. + if (type.basetype == SPIRType::Image && type.image.dim != DimSubpassData) + { + // Sampler buffers are always declared as samplerBuffer even though they might be separate images in the SPIR-V. + if (type.image.dim == DimBuffer && type.image.sampled == 1) + res += "sampler"; + else + res += type.image.sampled == 2 ? "image" : "texture"; + } + else + res += "sampler"; + + switch (type.image.dim) + { + case Dim1D: + res += "1D"; + break; + case Dim2D: + res += "2D"; + break; + case Dim3D: + res += "3D"; + break; + case DimCube: + res += "CUBE"; + break; + + case DimBuffer: + res += "Buffer"; + break; + + case DimSubpassData: + res += "2D"; + break; + default: + SPIRV_CROSS_THROW("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported."); + } + + if (type.image.ms) + res += "MS"; + if (type.image.arrayed) + res += "Array"; + + return res; +} + +string CompilerHLSL::image_type_hlsl(const SPIRType &type, uint32_t id) +{ + if (hlsl_options.shader_model <= 30) + return image_type_hlsl_legacy(type, id); + else + return image_type_hlsl_modern(type, id); +} + +// The optional id parameter indicates the object whose type we are trying +// to find the description for. It is optional. Most type descriptions do not +// depend on a specific object's use of that type. +string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id) +{ + // Ignore the pointer type since GLSL doesn't have pointers. + + switch (type.basetype) + { + case SPIRType::Struct: + // Need OpName lookup here to get a "sensible" name for a struct. + if (backend.explicit_struct_type) + return join("struct ", to_name(type.self)); + else + return to_name(type.self); + + case SPIRType::Image: + case SPIRType::SampledImage: + return image_type_hlsl(type, id); + + case SPIRType::Sampler: + return comparison_ids.count(id) ? "SamplerComparisonState" : "SamplerState"; + + case SPIRType::Void: + return "void"; + + default: + break; + } + + if (type.vecsize == 1 && type.columns == 1) // Scalar builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return "bool"; + case SPIRType::Int: + return backend.basic_int_type; + case SPIRType::UInt: + return backend.basic_uint_type; + case SPIRType::AtomicCounter: + return "atomic_uint"; + case SPIRType::Half: + if (hlsl_options.enable_16bit_types) + return "half"; + else + return "min16float"; + case SPIRType::Short: + if (hlsl_options.enable_16bit_types) + return "int16_t"; + else + return "min16int"; + case SPIRType::UShort: + if (hlsl_options.enable_16bit_types) + return "uint16_t"; + else + return "min16uint"; + case SPIRType::Float: + return "float"; + case SPIRType::Double: + return "double"; + case SPIRType::Int64: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0."); + return "int64_t"; + case SPIRType::UInt64: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("64-bit integers only supported in SM 6.0."); + return "uint64_t"; + case SPIRType::AccelerationStructure: + return "RaytracingAccelerationStructure"; + case SPIRType::RayQuery: + return "RayQuery"; + default: + return "???"; + } + } + else if (type.vecsize > 1 && type.columns == 1) // Vector builtin + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bool", type.vecsize); + case SPIRType::Int: + return join("int", type.vecsize); + case SPIRType::UInt: + return join("uint", type.vecsize); + case SPIRType::Half: + return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.vecsize); + case SPIRType::Short: + return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.vecsize); + case SPIRType::UShort: + return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.vecsize); + case SPIRType::Float: + return join("float", type.vecsize); + case SPIRType::Double: + return join("double", type.vecsize); + case SPIRType::Int64: + return join("int64_t", type.vecsize); + case SPIRType::UInt64: + return join("uint64_t", type.vecsize); + default: + return "???"; + } + } + else + { + switch (type.basetype) + { + case SPIRType::Boolean: + return join("bool", type.columns, "x", type.vecsize); + case SPIRType::Int: + return join("int", type.columns, "x", type.vecsize); + case SPIRType::UInt: + return join("uint", type.columns, "x", type.vecsize); + case SPIRType::Half: + return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.columns, "x", type.vecsize); + case SPIRType::Short: + return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.columns, "x", type.vecsize); + case SPIRType::UShort: + return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.columns, "x", type.vecsize); + case SPIRType::Float: + return join("float", type.columns, "x", type.vecsize); + case SPIRType::Double: + return join("double", type.columns, "x", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } +} + +void CompilerHLSL::emit_header() +{ + for (auto &header : header_lines) + statement(header); + + if (header_lines.size() > 0) + { + statement(""); + } +} + +void CompilerHLSL::emit_interface_block_globally(const SPIRVariable &var) +{ + add_resource_name(var.self); + + // The global copies of I/O variables should not contain interpolation qualifiers. + // These are emitted inside the interface structs. + auto &flags = ir.meta[var.self].decoration.decoration_flags; + auto old_flags = flags; + flags.reset(); + statement("static ", variable_decl(var), ";"); + flags = old_flags; +} + +const char *CompilerHLSL::to_storage_qualifiers_glsl(const SPIRVariable &var) +{ + // Input and output variables are handled specially in HLSL backend. + // The variables are declared as global, private variables, and do not need any qualifiers. + if (var.storage == StorageClassUniformConstant || var.storage == StorageClassUniform || + var.storage == StorageClassPushConstant) + { + return "uniform "; + } + + return ""; +} + +void CompilerHLSL::emit_builtin_outputs_in_struct() +{ + auto &execution = get_entry_point(); + + bool legacy = hlsl_options.shader_model <= 30; + active_output_builtins.for_each_bit([&](uint32_t i) { + const char *type = nullptr; + const char *semantic = nullptr; + auto builtin = static_cast(i); + switch (builtin) + { + case BuiltInPosition: + type = is_position_invariant() && backend.support_precise_qualifier ? "precise float4" : "float4"; + semantic = legacy ? "POSITION" : "SV_Position"; + break; + + case BuiltInSampleMask: + if (hlsl_options.shader_model < 41 || execution.model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Sample Mask output is only supported in PS 4.1 or higher."); + type = "uint"; + semantic = "SV_Coverage"; + break; + + case BuiltInFragDepth: + type = "float"; + if (legacy) + { + semantic = "DEPTH"; + } + else + { + if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthGreater)) + semantic = "SV_DepthGreaterEqual"; + else if (hlsl_options.shader_model >= 50 && execution.flags.get(ExecutionModeDepthLess)) + semantic = "SV_DepthLessEqual"; + else + semantic = "SV_Depth"; + } + break; + + case BuiltInClipDistance: + { + static const char *types[] = { "float", "float2", "float3", "float4" }; + + // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors. + if (execution.model == ExecutionModelMeshEXT) + { + if (clip_distance_count > 4) + SPIRV_CROSS_THROW("Clip distance count > 4 not supported for mesh shaders."); + + if (clip_distance_count == 1) + { + // Avoids having to hack up access_chain code. Makes it trivially indexable. + statement("float gl_ClipDistance[1] : SV_ClipDistance;"); + } + else + { + // Replace array with vector directly, avoids any weird fixup path. + statement(types[clip_distance_count - 1], " gl_ClipDistance : SV_ClipDistance;"); + } + } + else + { + for (uint32_t clip = 0; clip < clip_distance_count; clip += 4) + { + uint32_t to_declare = clip_distance_count - clip; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = clip / 4; + + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index, + " : SV_ClipDistance", semantic_index, ";"); + } + } + break; + } + + case BuiltInCullDistance: + { + static const char *types[] = { "float", "float2", "float3", "float4" }; + + // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors. + if (execution.model == ExecutionModelMeshEXT) + { + if (cull_distance_count > 4) + SPIRV_CROSS_THROW("Cull distance count > 4 not supported for mesh shaders."); + + if (cull_distance_count == 1) + { + // Avoids having to hack up access_chain code. Makes it trivially indexable. + statement("float gl_CullDistance[1] : SV_CullDistance;"); + } + else + { + // Replace array with vector directly, avoids any weird fixup path. + statement(types[cull_distance_count - 1], " gl_CullDistance : SV_CullDistance;"); + } + } + else + { + for (uint32_t cull = 0; cull < cull_distance_count; cull += 4) + { + uint32_t to_declare = cull_distance_count - cull; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = cull / 4; + + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassOutput), semantic_index, + " : SV_CullDistance", semantic_index, ";"); + } + } + break; + } + + case BuiltInPointSize: + // If point_size_compat is enabled, just ignore PointSize. + // PointSize does not exist in HLSL, but some code bases might want to be able to use these shaders, + // even if it means working around the missing feature. + if (legacy) + { + type = "float"; + semantic = "PSIZE"; + } + else if (!hlsl_options.point_size_compat) + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + break; + + case BuiltInLayer: + case BuiltInPrimitiveId: + case BuiltInViewportIndex: + case BuiltInPrimitiveShadingRateKHR: + case BuiltInCullPrimitiveEXT: + // per-primitive attributes handled separatly + break; + + case BuiltInPrimitivePointIndicesEXT: + case BuiltInPrimitiveLineIndicesEXT: + case BuiltInPrimitiveTriangleIndicesEXT: + // meshlet local-index buffer handled separatly + break; + + default: + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + } + + if (type && semantic) + statement(type, " ", builtin_to_glsl(builtin, StorageClassOutput), " : ", semantic, ";"); + }); +} + +void CompilerHLSL::emit_builtin_primitive_outputs_in_struct() +{ + active_output_builtins.for_each_bit([&](uint32_t i) { + const char *type = nullptr; + const char *semantic = nullptr; + auto builtin = static_cast(i); + switch (builtin) + { + case BuiltInLayer: + { + if (hlsl_options.shader_model < 50) + SPIRV_CROSS_THROW("Render target array index output is only supported in SM 5.0 or higher."); + type = "uint"; + semantic = "SV_RenderTargetArrayIndex"; + break; + } + + case BuiltInPrimitiveId: + type = "uint"; + semantic = "SV_PrimitiveID"; + break; + + case BuiltInViewportIndex: + type = "uint"; + semantic = "SV_ViewportArrayIndex"; + break; + + case BuiltInPrimitiveShadingRateKHR: + type = "uint"; + semantic = "SV_ShadingRate"; + break; + + case BuiltInCullPrimitiveEXT: + type = "bool"; + semantic = "SV_CullPrimitive"; + break; + + default: + break; + } + + if (type && semantic) + statement(type, " ", builtin_to_glsl(builtin, StorageClassOutput), " : ", semantic, ";"); + }); +} + +void CompilerHLSL::emit_builtin_inputs_in_struct() +{ + bool legacy = hlsl_options.shader_model <= 30; + active_input_builtins.for_each_bit([&](uint32_t i) { + const char *type = nullptr; + const char *semantic = nullptr; + auto builtin = static_cast(i); + switch (builtin) + { + case BuiltInPosition: + type = "float4"; + semantic = legacy ? "POSITION" : "SV_Position"; + break; + case BuiltInFragCoord: + type = "float4"; + semantic = legacy ? "VPOS" : "SV_Position"; + break; + + case BuiltInVertexId: + case BuiltInVertexIndex: + if (legacy) + SPIRV_CROSS_THROW("Vertex index not supported in SM 3.0 or lower."); + type = "uint"; + semantic = "SV_VertexID"; + break; + + case BuiltInPrimitiveId: + // For geometry shaders, PrimitiveId is a direct function parameter + // (SV_PrimitiveID), not part of the input struct. + if (get_entry_point().model != ExecutionModelGeometry) + { + type = "uint"; + semantic = "SV_PrimitiveID"; + } + break; + + case BuiltInInvocationId: + if (get_entry_point().model == ExecutionModelGeometry) + { + type = "uint"; + semantic = "SV_GSInstanceID"; + } + else if (get_entry_point().model != ExecutionModelTessellationControl) + { + // For tesc, InvocationId is a direct function parameter (SV_OutputControlPointID), + // not part of the input struct. + SPIRV_CROSS_THROW("InvocationId is only supported in geometry and tessellation control shaders."); + } + break; + + case BuiltInInstanceId: + case BuiltInInstanceIndex: + if (legacy) + SPIRV_CROSS_THROW("Instance index not supported in SM 3.0 or lower."); + type = "uint"; + semantic = "SV_InstanceID"; + break; + + case BuiltInSampleId: + if (legacy) + SPIRV_CROSS_THROW("Sample ID not supported in SM 3.0 or lower."); + type = "uint"; + semantic = "SV_SampleIndex"; + break; + + case BuiltInSampleMask: + if (hlsl_options.shader_model < 50 || get_entry_point().model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Sample Mask input is only supported in PS 5.0 or higher."); + type = "uint"; + semantic = "SV_Coverage"; + break; + + case BuiltInGlobalInvocationId: + type = "uint3"; + semantic = "SV_DispatchThreadID"; + break; + + case BuiltInLocalInvocationId: + type = "uint3"; + semantic = "SV_GroupThreadID"; + break; + + case BuiltInLocalInvocationIndex: + type = "uint"; + semantic = "SV_GroupIndex"; + break; + + case BuiltInWorkgroupId: + type = "uint3"; + semantic = "SV_GroupID"; + break; + + case BuiltInFrontFacing: + type = "bool"; + semantic = "SV_IsFrontFace"; + break; + + case BuiltInViewIndex: + if (hlsl_options.shader_model < 61 || (get_entry_point().model != ExecutionModelVertex && get_entry_point().model != ExecutionModelFragment)) + SPIRV_CROSS_THROW("View Index input is only supported in VS and PS 6.1 or higher."); + type = "uint"; + semantic = "SV_ViewID"; + break; + + case BuiltInNumWorkgroups: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + case BuiltInSubgroupEqMask: + case BuiltInSubgroupLtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupGeMask: + // Handled specially. + break; + + case BuiltInBaseVertex: + if (hlsl_options.shader_model >= 68) + { + type = "uint"; + semantic = "SV_StartVertexLocation"; + } + break; + + case BuiltInBaseInstance: + if (hlsl_options.shader_model >= 68) + { + type = "uint"; + semantic = "SV_StartInstanceLocation"; + } + break; + + case BuiltInHelperInvocation: + if (hlsl_options.shader_model < 50 || get_entry_point().model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Helper Invocation input is only supported in PS 5.0 or higher."); + break; + + case BuiltInClipDistance: + // HLSL is a bit weird here, use SV_ClipDistance0, SV_ClipDistance1 and so on with vectors. + for (uint32_t clip = 0; clip < clip_distance_count; clip += 4) + { + uint32_t to_declare = clip_distance_count - clip; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = clip / 4; + + static const char *types[] = { "float", "float2", "float3", "float4" }; + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index, + " : SV_ClipDistance", semantic_index, ";"); + } + break; + + case BuiltInCullDistance: + // HLSL is a bit weird here, use SV_CullDistance0, SV_CullDistance1 and so on with vectors. + for (uint32_t cull = 0; cull < cull_distance_count; cull += 4) + { + uint32_t to_declare = cull_distance_count - cull; + if (to_declare > 4) + to_declare = 4; + + uint32_t semantic_index = cull / 4; + + static const char *types[] = { "float", "float2", "float3", "float4" }; + statement(types[to_declare - 1], " ", builtin_to_glsl(builtin, StorageClassInput), semantic_index, + " : SV_CullDistance", semantic_index, ";"); + } + break; + + case BuiltInPointCoord: + // PointCoord is not supported, but provide a way to just ignore that, similar to PointSize. + if (hlsl_options.point_coord_compat) + break; + else + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + + case BuiltInLayer: + if (hlsl_options.shader_model < 50 || get_entry_point().model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Render target array index input is only supported in PS 5.0 or higher."); + type = "uint"; + semantic = "SV_RenderTargetArrayIndex"; + break; + + case BuiltInBaryCoordKHR: + case BuiltInBaryCoordNoPerspKHR: + if (hlsl_options.shader_model < 61) + SPIRV_CROSS_THROW("SM 6.1 is required for barycentrics."); + type = builtin == BuiltInBaryCoordNoPerspKHR ? "noperspective float3" : "float3"; + if (active_input_builtins.get(BuiltInBaryCoordKHR) && active_input_builtins.get(BuiltInBaryCoordNoPerspKHR)) + semantic = builtin == BuiltInBaryCoordKHR ? "SV_Barycentrics0" : "SV_Barycentrics1"; + else + semantic = "SV_Barycentrics"; + break; + + default: + SPIRV_CROSS_THROW("Unsupported builtin in HLSL."); + } + + if (type && semantic) + statement(type, " ", builtin_to_glsl(builtin, StorageClassInput), " : ", semantic, ";"); + }); +} + +uint32_t CompilerHLSL::type_to_consumed_locations(const SPIRType &type) const +{ + // TODO: Need to verify correctness. + uint32_t elements = 0; + + if (type.basetype == SPIRType::Struct) + { + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + elements += type_to_consumed_locations(get(type.member_types[i])); + } + else + { + uint32_t array_multiplier = 1; + for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) + { + if (type.array_size_literal[i]) + array_multiplier *= type.array[i]; + else + array_multiplier *= evaluate_constant_u32(type.array[i]); + } + elements += array_multiplier * type.columns; + } + return elements; +} + +string CompilerHLSL::to_interpolation_qualifiers(const Bitset &flags) +{ + string res; + //if (flags & (1ull << DecorationSmooth)) + // res += "linear "; + if (flags.get(DecorationFlat) || flags.get(DecorationPerVertexKHR)) + res += "nointerpolation "; + if (flags.get(DecorationNoPerspective)) + res += "noperspective "; + if (flags.get(DecorationCentroid)) + res += "centroid "; + if (flags.get(DecorationPatch)) + res += "patch "; // Seems to be different in actual HLSL. + if (flags.get(DecorationSample)) + res += "sample "; + if (flags.get(DecorationInvariant) && backend.support_precise_qualifier) + res += "precise "; // Not supported? + + return res; +} + +std::string CompilerHLSL::to_semantic(uint32_t location, ExecutionModel em, StorageClass sc) +{ + if (em == ExecutionModelVertex && sc == StorageClassInput) + { + // We have a vertex attribute - we should look at remapping it if the user provided + // vertex attribute hints. + for (auto &attribute : remap_vertex_attributes) + if (attribute.location == location) + return attribute.semantic; + } + + // Not a vertex attribute, or no remap_vertex_attributes entry. + return join("TEXCOORD", location); +} + +std::string CompilerHLSL::to_initializer_expression(const SPIRVariable &var) +{ + // We cannot emit static const initializer for block constants for practical reasons, + // so just inline the initializer. + // FIXME: There is a theoretical problem here if someone tries to composite extract + // into this initializer since we don't declare it properly, but that is somewhat non-sensical. + auto &type = get(var.basetype); + bool is_block = has_decoration(type.self, DecorationBlock); + auto *c = maybe_get(var.initializer); + if (is_block && c) + return constant_expression(*c); + else + return CompilerGLSL::to_initializer_expression(var); +} + +void CompilerHLSL::emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index, + uint32_t location, + std::unordered_set &active_locations) +{ + auto &execution = get_entry_point(); + auto type = get(var.basetype); + + std::string semantic; + if (hlsl_options.user_semantic && has_member_decoration(var.self, member_index, DecorationUserSemantic)) + semantic = get_member_decoration_string(var.self, member_index, DecorationUserSemantic); + else + semantic = to_semantic(location, execution.model, var.storage); + + auto mbr_name = join(to_name(type.self), "_", to_member_name(type, member_index)); + auto &mbr_type = get(type.member_types[member_index]); + + Bitset member_decorations = get_member_decoration_bitset(type.self, member_index); + if (has_decoration(var.self, DecorationPerVertexKHR)) + member_decorations.set(DecorationPerVertexKHR); + + statement(to_interpolation_qualifiers(member_decorations), + type_to_glsl(mbr_type), + " ", mbr_name, type_to_array_glsl(mbr_type, var.self), + " : ", semantic, ";"); + + // Structs and arrays should consume more locations. + uint32_t consumed_locations = type_to_consumed_locations(mbr_type); + for (uint32_t i = 0; i < consumed_locations; i++) + active_locations.insert(location + i); +} + +void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set &active_locations) +{ + auto &execution = get_entry_point(); + auto type = get(var.basetype); + + string binding; + bool use_location_number = true; + bool need_matrix_unroll = false; + bool legacy = hlsl_options.shader_model <= 30; + if (execution.model == ExecutionModelFragment && var.storage == StorageClassOutput) + { + // Dual-source blending is achieved in HLSL by emitting to SV_Target0 and 1. + uint32_t index = get_decoration(var.self, DecorationIndex); + uint32_t location = get_decoration(var.self, DecorationLocation); + + if (index != 0 && location != 0) + SPIRV_CROSS_THROW("Dual-source blending is only supported on MRT #0 in HLSL."); + + binding = join(legacy ? "COLOR" : "SV_Target", location + index); + use_location_number = false; + if (legacy) // COLOR must be a four-component vector on legacy shader model targets (HLSL ERR_COLOR_4COMP) + type.vecsize = 4; + } + else if (var.storage == StorageClassInput && execution.model == ExecutionModelVertex) + { + need_matrix_unroll = true; + if (legacy) // Inputs must be floating-point in legacy targets. + type.basetype = SPIRType::Float; + } + + const auto get_vacant_location = [&]() -> uint32_t { + for (uint32_t i = 0; i < 64; i++) + if (!active_locations.count(i)) + return i; + SPIRV_CROSS_THROW("All locations from 0 to 63 are exhausted."); + }; + + auto name = to_name(var.self); + if (use_location_number) + { + uint32_t location_number = UINT32_MAX; + + std::string semantic; + bool has_user_semantic = false; + + if (hlsl_options.user_semantic && has_decoration(var.self, DecorationUserSemantic)) + { + semantic = get_decoration_string(var.self, DecorationUserSemantic); + has_user_semantic = true; + } + else + { + // If an explicit location exists, use it with TEXCOORD[N] semantic. + // Otherwise, pick a vacant location. + if (has_decoration(var.self, DecorationLocation)) + location_number = get_decoration(var.self, DecorationLocation); + else + location_number = get_vacant_location(); + + // Allow semantic remap if specified. + semantic = to_semantic(location_number, execution.model, var.storage); + } + + if (need_matrix_unroll && type.columns > 1) + { + if (!type.array.empty()) + SPIRV_CROSS_THROW("Arrays of matrices used as input/output. This is not supported."); + + // Unroll matrices. + for (uint32_t i = 0; i < type.columns; i++) + { + SPIRType newtype = type; + newtype.columns = 1; + + string effective_semantic; + if (hlsl_options.flatten_matrix_vertex_input_semantics && !has_user_semantic) + effective_semantic = to_semantic(location_number, execution.model, var.storage); + else + effective_semantic = join(semantic, "_", i); + + statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)), + variable_decl(newtype, join(name, "_", i)), " : ", effective_semantic, ";"); + if (location_number != UINT32_MAX) + active_locations.insert(location_number++); + } + } + else + { + auto decl_type = type; + if (execution.model == ExecutionModelMeshEXT || + (execution.model == ExecutionModelGeometry && var.storage == StorageClassInput) || + has_decoration(var.self, DecorationPerVertexKHR)) + { + // The per-vertex/per-CP dimension is the outermost (last element in array vector). + decl_type.array.pop_back(); + decl_type.array_size_literal.pop_back(); + } + statement(to_interpolation_qualifiers(get_decoration_bitset(var.self)), variable_decl(decl_type, name), " : ", + semantic, ";"); + + if (location_number != UINT32_MAX) + { + // Structs and arrays should consume more locations. + uint32_t consumed_locations = type_to_consumed_locations(decl_type); + for (uint32_t i = 0; i < consumed_locations; i++) + active_locations.insert(location_number + i); + } + } + } + else + { + statement(variable_decl(type, name), " : ", binding, ";"); + } +} + +std::string CompilerHLSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) +{ + switch (builtin) + { + case BuiltInPosition: + // We want to avoid clash between input/output for geometry shader + return storage == StorageClass::StorageClassInput ? "gl_PositionIn" : "gl_Position"; + case BuiltInVertexId: + return "gl_VertexID"; + case BuiltInInstanceId: + return "gl_InstanceID"; + case BuiltInNumWorkgroups: + { + if (!num_workgroups_builtin) + SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. " + "Cannot emit code for this builtin."); + + auto &var = get(num_workgroups_builtin); + auto &type = get(var.basetype); + auto ret = join(to_name(num_workgroups_builtin), "_", get_member_name(type.self, 0)); + ParsedIR::sanitize_underscores(ret); + return ret; + } + case BuiltInPointCoord: + // Crude hack, but there is no real alternative. This path is only enabled if point_coord_compat is set. + return "float2(0.5f, 0.5f)"; + case BuiltInSubgroupLocalInvocationId: + return "WaveGetLaneIndex()"; + case BuiltInSubgroupSize: + return "WaveGetLaneCount()"; + case BuiltInHelperInvocation: + return "IsHelperLane()"; + + default: + return CompilerGLSL::builtin_to_glsl(builtin, storage); + } +} + +void CompilerHLSL::emit_builtin_variables() +{ + Bitset builtins = active_input_builtins; + builtins.merge_or(active_output_builtins); + + std::unordered_map builtin_to_initializer; + + // We need to declare sample mask with the same type that module declares it. + // Sample mask is somewhat special in that SPIR-V has an array, and we can copy that array, so we need to + // match sign. + SPIRType::BaseType sample_mask_in_basetype = SPIRType::Void; + SPIRType::BaseType sample_mask_out_basetype = SPIRType::Void; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (!is_builtin_variable(var)) + return; + + auto &type = this->get(var.basetype); + auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + + if (var.storage == StorageClassInput && builtin == BuiltInSampleMask) + sample_mask_in_basetype = type.basetype; + else if (var.storage == StorageClassOutput && builtin == BuiltInSampleMask) + sample_mask_out_basetype = type.basetype; + + if (var.initializer && var.storage == StorageClassOutput) + { + auto *c = this->maybe_get(var.initializer); + if (!c) + return; + + if (type.basetype == SPIRType::Struct) + { + uint32_t member_count = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + if (has_member_decoration(type.self, i, DecorationBuiltIn)) + { + builtin_to_initializer[get_member_decoration(type.self, i, DecorationBuiltIn)] = + c->subconstants[i]; + } + } + } + else if (has_decoration(var.self, DecorationBuiltIn)) + { + builtin_to_initializer[builtin] = var.initializer; + } + } + }); + + // Emit global variables for the interface variables which are statically used by the shader. + builtins.for_each_bit([&](uint32_t i) { + auto builtin = static_cast(i); + + string init_expr; + auto init_itr = builtin_to_initializer.find(builtin); + if (init_itr != builtin_to_initializer.end()) + init_expr = join(" = ", to_expression(init_itr->second)); + + if (get_execution_model() == ExecutionModelMeshEXT) + { + if (builtin == BuiltInPosition || builtin == BuiltInPointSize || builtin == BuiltInClipDistance || + builtin == BuiltInCullDistance || builtin == BuiltInLayer || builtin == BuiltInPrimitiveId || + builtin == BuiltInViewportIndex || builtin == BuiltInCullPrimitiveEXT || + builtin == BuiltInPrimitiveShadingRateKHR || builtin == BuiltInPrimitivePointIndicesEXT || + builtin == BuiltInPrimitiveLineIndicesEXT || builtin == BuiltInPrimitiveTriangleIndicesEXT) + { + return; + } + } + + // If we need to emit 2 separate variables (for both input & output), we'll update this value + bool has_separate_input_output = false; + for (int variable_index = 0; variable_index < (has_separate_input_output ? 2 : 1); variable_index++) + { + uint32_t array_size = 0; + StorageClass storage = active_input_builtins.get(i) && variable_index == 0 + ? StorageClassInput + : StorageClassOutput; + const char *type = nullptr; + switch (builtin) + { + case BuiltInFragCoord: + type = "float4"; + break; + + case BuiltInPosition: + type = "float4"; + if (storage == StorageClass::StorageClassInput && + (get_execution_model() == ExecutionModelGeometry || + get_execution_model() == ExecutionModelTessellationControl)) + array_size = input_vertices_from_execution_mode(get_entry_point()); + break; + + case BuiltInFragDepth: + type = "float"; + break; + + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInInstanceIndex: + type = "int"; + if (hlsl_options.support_nonzero_base_vertex_base_instance || hlsl_options.shader_model >= 68) + base_vertex_info.used = true; + break; + + case BuiltInBaseVertex: + case BuiltInBaseInstance: + type = "int"; + base_vertex_info.used = true; + break; + + case BuiltInInstanceId: + case BuiltInSampleId: + type = "int"; + break; + + case BuiltInPointSize: + if (hlsl_options.point_size_compat || hlsl_options.shader_model <= 30) + { + // Just emit the global variable, it will be ignored. + type = "float"; + break; + } + else + SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin))); + + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInWorkgroupId: + type = "uint3"; + break; + + case BuiltInLocalInvocationIndex: + type = "uint"; + break; + + case BuiltInFrontFacing: + type = "bool"; + break; + + case BuiltInNumWorkgroups: + case BuiltInPointCoord: + // Handled specially. + break; + + case BuiltInSubgroupLocalInvocationId: + case BuiltInSubgroupSize: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops."); + break; + + case BuiltInSubgroupEqMask: + case BuiltInSubgroupLtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupGeMask: + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("Need SM 6.0 for Wave ops."); + type = "uint4"; + break; + + case BuiltInHelperInvocation: + if (hlsl_options.shader_model < 50) + SPIRV_CROSS_THROW("Need SM 5.0 for Helper Invocation."); + break; + + case BuiltInClipDistance: + array_size = clip_distance_count; + type = "float"; + break; + + case BuiltInCullDistance: + array_size = cull_distance_count; + type = "float"; + break; + + case BuiltInSampleMask: + if (storage == StorageClass::StorageClassInput) + type = sample_mask_in_basetype == SPIRType::UInt ? "uint" : "int"; + else + type = sample_mask_out_basetype == SPIRType::UInt ? "uint" : "int"; + array_size = 1; + break; + + case BuiltInPrimitiveId: + case BuiltInViewIndex: + case BuiltInLayer: + type = "uint"; + break; + + case BuiltInViewportIndex: + case BuiltInPrimitiveShadingRateKHR: + case BuiltInPrimitiveLineIndicesEXT: + case BuiltInCullPrimitiveEXT: + type = "uint"; + break; + + case BuiltInBaryCoordKHR: + case BuiltInBaryCoordNoPerspKHR: + if (hlsl_options.shader_model < 61) + SPIRV_CROSS_THROW("Need SM 6.1 for barycentrics."); + type = "float3"; + break; + + default: + SPIRV_CROSS_THROW(join("Unsupported builtin in HLSL: ", unsigned(builtin))); + } + + if (type) + { + auto builtin_name = builtin_to_glsl(builtin, storage); + if (array_size) + statement("static ", type, " ", builtin_name, "[", array_size, "]", init_expr, ";"); + else + statement("static ", type, " ", builtin_name, init_expr, ";"); + + if (storage == StorageClassInput && this->active_output_builtins.get(i)) + { + auto out_builtin_name = builtin_to_glsl(builtin, StorageClassOutput); + if (out_builtin_name != builtin_name) + { + // If built-in name differs, we need to output it again + // (we reevaluate type and array size in case they are different) + has_separate_input_output = true; + } + } + } + } + }); + + if (base_vertex_info.used && hlsl_options.shader_model < 68) + { + string binding_info; + if (base_vertex_info.explicit_binding) + { + binding_info = join(" : register(b", base_vertex_info.register_index); + if (base_vertex_info.register_space) + binding_info += join(", space", base_vertex_info.register_space); + binding_info += ")"; + } + statement("cbuffer SPIRV_Cross_VertexInfo", binding_info); + begin_scope(); + statement("int SPIRV_Cross_BaseVertex;"); + statement("int SPIRV_Cross_BaseInstance;"); + end_scope_decl(); + statement(""); + } +} + +void CompilerHLSL::set_hlsl_aux_buffer_binding(HLSLAuxBinding binding, uint32_t register_index, uint32_t register_space) +{ + if (binding == HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE) + { + base_vertex_info.explicit_binding = true; + base_vertex_info.register_space = register_space; + base_vertex_info.register_index = register_index; + } +} + +void CompilerHLSL::unset_hlsl_aux_buffer_binding(HLSLAuxBinding binding) +{ + if (binding == HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE) + base_vertex_info.explicit_binding = false; +} + +bool CompilerHLSL::is_hlsl_aux_buffer_binding_used(HLSLAuxBinding binding) const +{ + if (binding == HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE) + return base_vertex_info.used; + else + return false; +} + +void CompilerHLSL::emit_composite_constants() +{ + // HLSL cannot declare structs or arrays inline, so we must move them out to + // global constants directly. + bool emitted = false; + + ir.for_each_typed_id([&](uint32_t, SPIRConstant &c) { + if (c.specialization) + return; + + auto &type = this->get(c.constant_type); + + if (type.basetype == SPIRType::Struct && is_builtin_type(type)) + return; + + if (type.basetype == SPIRType::Struct || !type.array.empty()) + { + add_resource_name(c.self); + auto name = to_name(c.self); + statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";"); + emitted = true; + } + }); + + if (emitted) + statement(""); +} + +void CompilerHLSL::emit_specialization_constants_and_structs() +{ + bool emitted = false; + SpecializationConstant wg_x, wg_y, wg_z; + ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + std::unordered_set io_block_types; + ir.for_each_typed_id([&](uint32_t, const SPIRVariable &var) { + auto &type = this->get(var.basetype); + if ((var.storage == StorageClassInput || var.storage == StorageClassOutput) && + !var.remapped_variable && type.pointer && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self) && + has_decoration(type.self, DecorationBlock)) + { + io_block_types.insert(type.self); + } + }); + + auto loop_lock = ir.create_loop_hard_lock(); + for (auto &id_ : ir.ids_for_constant_undef_or_type) + { + auto &id = ir.ids[id_]; + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + if (c.self == workgroup_size_id) + { + statement("static const uint3 gl_WorkGroupSize = ", + constant_expression(get(workgroup_size_id)), ";"); + emitted = true; + } + else if (c.specialization) + { + auto &type = get(c.constant_type); + add_resource_name(c.self); + auto name = to_name(c.self); + + if (has_decoration(c.self, DecorationSpecId)) + { + // HLSL does not support specialization constants, so fallback to macros. + c.specialization_constant_macro_name = + constant_value_macro_name(get_decoration(c.self, DecorationSpecId)); + + statement("#ifndef ", c.specialization_constant_macro_name); + statement("#define ", c.specialization_constant_macro_name, " ", constant_expression(c)); + statement("#endif"); + statement("static const ", variable_decl(type, name), " = ", c.specialization_constant_macro_name, ";"); + } + else + statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";"); + + emitted = true; + } + } + else if (id.get_type() == TypeConstantOp) + { + auto &c = id.get(); + auto &type = get(c.basetype); + add_resource_name(c.self); + auto name = to_name(c.self); + statement("static const ", variable_decl(type, name), " = ", constant_op_expression(c), ";"); + emitted = true; + } + else if (id.get_type() == TypeType) + { + auto &type = id.get(); + bool is_non_io_block = has_decoration(type.self, DecorationBlock) && + io_block_types.count(type.self) == 0; + bool is_buffer_block = has_decoration(type.self, DecorationBufferBlock); + if (type.basetype == SPIRType::Struct && type.array.empty() && + !type.pointer && !is_non_io_block && !is_buffer_block) + { + if (emitted) + statement(""); + emitted = false; + + emit_struct(type); + } + } + else if (id.get_type() == TypeUndef) + { + auto &undef = id.get(); + auto &type = this->get(undef.basetype); + // OpUndef can be void for some reason ... + if (type.basetype == SPIRType::Void) + return; + + string initializer; + if (options.force_zero_initialized_variables && type_can_zero_initialize(type)) + initializer = join(" = ", to_zero_initialized_expression(undef.basetype)); + + statement("static ", variable_decl(type, to_name(undef.self), undef.self), initializer, ";"); + emitted = true; + } + } + + if (emitted) + statement(""); +} + +void CompilerHLSL::replace_illegal_names() +{ + static const unordered_set keywords = { + // Additional HLSL specific keywords. + // From https://docs.microsoft.com/en-US/windows/win32/direct3dhlsl/dx-graphics-hlsl-appendix-keywords + "AppendStructuredBuffer", "asm", "asm_fragment", + "BlendState", "bool", "break", "Buffer", "ByteAddressBuffer", + "case", "cbuffer", "centroid", "class", "column_major", "compile", + "compile_fragment", "CompileShader", "const", "continue", "ComputeShader", + "ConsumeStructuredBuffer", + "default", "DepthStencilState", "DepthStencilView", "discard", "do", + "double", "DomainShader", "dword", + "else", "export", "false", "float", "for", "fxgroup", + "GeometryShader", "groupshared", "half", "HullShader", + "indices", "if", "in", "inline", "inout", "InputPatch", "int", "interface", + "line", "lineadj", "linear", "LineStream", + "matrix", "min16float", "min10float", "min16int", "min16uint", + "namespace", "nointerpolation", "noperspective", "NULL", + "out", "OutputPatch", + "payload", "packoffset", "pass", "pixelfragment", "PixelShader", "point", + "PointStream", "precise", "RasterizerState", "RenderTargetView", + "return", "register", "row_major", "RWBuffer", "RWByteAddressBuffer", + "RWStructuredBuffer", "RWTexture1D", "RWTexture1DArray", "RWTexture2D", + "RWTexture2DArray", "RWTexture3D", "sample", "sampler", "SamplerState", + "SamplerComparisonState", "shared", "snorm", "stateblock", "stateblock_state", + "static", "string", "struct", "switch", "StructuredBuffer", "tbuffer", + "technique", "technique10", "technique11", "texture", "Texture1D", + "Texture1DArray", "Texture2D", "Texture2DArray", "Texture2DMS", "Texture2DMSArray", + "Texture3D", "TextureCube", "TextureCubeArray", "true", "typedef", "triangle", + "triangleadj", "TriangleStream", "uint", "uniform", "unorm", "unsigned", + "vector", "vertexfragment", "VertexShader", "vertices", "void", "volatile", "while", + "signed", + }; + + CompilerGLSL::replace_illegal_names(keywords); + CompilerGLSL::replace_illegal_names(); +} + +SPIRType::BaseType CompilerHLSL::get_builtin_basetype(BuiltIn builtin, SPIRType::BaseType default_type) +{ + switch (builtin) + { + case BuiltInSampleMask: + // We declare sample mask array with module type, so always use default_type here. + return default_type; + default: + return CompilerGLSL::get_builtin_basetype(builtin, default_type); + } +} + +void CompilerHLSL::emit_resources() +{ + auto &execution = get_entry_point(); + + replace_illegal_names(); + + switch (execution.model) + { + case ExecutionModelGeometry: + case ExecutionModelTessellationControl: + case ExecutionModelTessellationEvaluation: + case ExecutionModelMeshEXT: + fixup_implicit_builtin_block_names(execution.model); + break; + + default: + break; + } + + emit_specialization_constants_and_structs(); + emit_composite_constants(); + + bool emitted = false; + + // Output UBOs and SSBOs + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + bool is_block_storage = type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform; + bool has_block_flags = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock); + + if (var.storage != StorageClassFunction && type.pointer && is_block_storage && !is_hidden_variable(var) && + has_block_flags) + { + emit_buffer_block(var); + emitted = true; + } + }); + + // Output push constant blocks + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + if (var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant && + !is_hidden_variable(var)) + { + emit_push_constant_block(var); + emitted = true; + } + }); + + if (execution.model == ExecutionModelVertex && hlsl_options.shader_model <= 30 && + active_output_builtins.get(BuiltInPosition)) + { + statement("uniform float4 gl_HalfPixel;"); + emitted = true; + } + + bool skip_separate_image_sampler = !combined_image_samplers.empty() || hlsl_options.shader_model <= 30; + + // Output Uniform Constants (values, samplers, images, etc). + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + // If we're remapping separate samplers and images, only emit the combined samplers. + if (skip_separate_image_sampler) + { + // Sampler buffers are always used without a sampler, and they will also work in regular D3D. + bool sampler_buffer = type.basetype == SPIRType::Image && type.image.dim == DimBuffer; + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + if (!sampler_buffer && (separate_image || separate_sampler)) + return; + } + + if (var.storage != StorageClassFunction && !is_builtin_variable(var) && !var.remapped_variable && + type.pointer && (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter) && + !is_hidden_variable(var)) + { + emit_uniform(var); + emitted = true; + } + }); + + if (emitted) + statement(""); + emitted = false; + + // Emit builtin input and output variables here. + emit_builtin_variables(); + + if (execution.model != ExecutionModelMeshEXT) + { + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + + bool is_hidden = is_hidden_io_variable(var); + + if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer && + (var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self) && !is_hidden) + { + // Builtin variables are handled separately. + emit_interface_block_globally(var); + emitted = true; + } + }); + } + + if (emitted) + statement(""); + emitted = false; + + require_input = false; + require_output = false; + unordered_set active_inputs; + unordered_set active_outputs; + + struct IOVariable + { + const SPIRVariable *var; + uint32_t location; + uint32_t block_member_index; + bool block; + }; + + SmallVector input_variables; + SmallVector output_variables; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + + if (var.storage != StorageClassInput && var.storage != StorageClassOutput) + return; + + bool is_hidden = is_hidden_io_variable(var); + + if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self) && !is_hidden) + { + if (block) + { + for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++) + { + uint32_t location = get_declared_member_location(var, i, false); + if (var.storage == StorageClassInput) + input_variables.push_back({ &var, location, i, true }); + else + output_variables.push_back({ &var, location, i, true }); + } + } + else + { + uint32_t location = get_decoration(var.self, DecorationLocation); + if (var.storage == StorageClassInput) + input_variables.push_back({ &var, location, 0, false }); + else + output_variables.push_back({ &var, location, 0, false }); + } + } + }); + + const auto variable_compare = [&](const IOVariable &a, const IOVariable &b) -> bool { + // Sort input and output variables based on, from more robust to less robust: + // - Location + // - Variable has a location + // - Name comparison + // - Variable has a name + // - Fallback: ID + bool has_location_a = a.block || has_decoration(a.var->self, DecorationLocation); + bool has_location_b = b.block || has_decoration(b.var->self, DecorationLocation); + + if (has_location_a && has_location_b) + return a.location < b.location; + else if (has_location_a && !has_location_b) + return true; + else if (!has_location_a && has_location_b) + return false; + + const auto &name1 = to_name(a.var->self); + const auto &name2 = to_name(b.var->self); + + if (name1.empty() && name2.empty()) + return a.var->self < b.var->self; + else if (name1.empty()) + return true; + else if (name2.empty()) + return false; + + return name1.compare(name2) < 0; + }; + + auto input_builtins = active_input_builtins; + input_builtins.clear(BuiltInNumWorkgroups); + input_builtins.clear(BuiltInPointCoord); + input_builtins.clear(BuiltInSubgroupSize); + input_builtins.clear(BuiltInSubgroupLocalInvocationId); + input_builtins.clear(BuiltInSubgroupEqMask); + input_builtins.clear(BuiltInSubgroupLtMask); + input_builtins.clear(BuiltInSubgroupLeMask); + input_builtins.clear(BuiltInSubgroupGtMask); + input_builtins.clear(BuiltInSubgroupGeMask); + + if (!input_variables.empty() || !input_builtins.empty()) + { + require_input = true; + statement("struct SPIRV_Cross_Input"); + + begin_scope(); + sort(input_variables.begin(), input_variables.end(), variable_compare); + for (auto &var : input_variables) + { + if (var.block) + emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_inputs); + else + emit_interface_block_in_struct(*var.var, active_inputs); + } + emit_builtin_inputs_in_struct(); + end_scope_decl(); + statement(""); + } + + const bool is_mesh_shader = execution.model == ExecutionModelMeshEXT; + if (!output_variables.empty() || !active_output_builtins.empty()) + { + sort(output_variables.begin(), output_variables.end(), variable_compare); + require_output = !(is_mesh_shader || execution.model == ExecutionModelGeometry); + + statement(is_mesh_shader ? "struct gl_MeshPerVertexEXT" : "struct SPIRV_Cross_Output"); + begin_scope(); + for (auto &var : output_variables) + { + if (is_per_primitive_variable(*var.var)) + continue; + if (var.block && is_mesh_shader && var.block_member_index != 0) + continue; + if (var.block && !is_mesh_shader) + emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_outputs); + else + emit_interface_block_in_struct(*var.var, active_outputs); + } + emit_builtin_outputs_in_struct(); + if (!is_mesh_shader) + emit_builtin_primitive_outputs_in_struct(); + end_scope_decl(); + statement(""); + + if (is_mesh_shader) + { + statement("struct gl_MeshPerPrimitiveEXT"); + begin_scope(); + for (auto &var : output_variables) + { + if (!is_per_primitive_variable(*var.var)) + continue; + if (var.block && var.block_member_index != 0) + continue; + + emit_interface_block_in_struct(*var.var, active_outputs); + } + emit_builtin_primitive_outputs_in_struct(); + end_scope_decl(); + statement(""); + } + } + + // Global variables. + for (auto global : global_variables) + { + auto &var = get(global); + if (is_hidden_variable(var, true)) + continue; + + if (var.storage == StorageClassTaskPayloadWorkgroupEXT && is_mesh_shader) + continue; + + if (var.storage != StorageClassOutput) + { + if (!variable_is_lut(var)) + { + add_resource_name(var.self); + + const char *storage = nullptr; + switch (var.storage) + { + case StorageClassWorkgroup: + case StorageClassTaskPayloadWorkgroupEXT: + storage = "groupshared"; + break; + + default: + storage = "static"; + break; + } + + string initializer; + if (options.force_zero_initialized_variables && var.storage == StorageClassPrivate && + !var.initializer && !var.static_expression && type_can_zero_initialize(get_variable_data_type(var))) + { + initializer = join(" = ", to_zero_initialized_expression(get_variable_data_type_id(var))); + } + statement(storage, " ", variable_decl(var), initializer, ";"); + + emitted = true; + } + } + } + + if (emitted) + statement(""); + + if (requires_op_fmod) + { + static const char *types[] = { + "float", + "float2", + "float3", + "float4", + }; + + for (auto &type : types) + { + statement(type, " mod(", type, " x, ", type, " y)"); + begin_scope(); + statement("return x - y * floor(x / y);"); + end_scope(); + statement(""); + } + } + + emit_texture_size_variants(required_texture_size_variants.srv, "4", false, ""); + for (uint32_t norm = 0; norm < 3; norm++) + { + for (uint32_t comp = 0; comp < 4; comp++) + { + static const char *qualifiers[] = { "", "unorm ", "snorm " }; + static const char *vecsizes[] = { "", "2", "3", "4" }; + emit_texture_size_variants(required_texture_size_variants.uav[norm][comp], vecsizes[comp], true, + qualifiers[norm]); + } + } + + if (requires_fp16_packing) + { + // HLSL does not pack into a single word sadly :( + statement("uint spvPackHalf2x16(float2 value)"); + begin_scope(); + statement("uint2 Packed = f32tof16(value);"); + statement("return Packed.x | (Packed.y << 16);"); + end_scope(); + statement(""); + + statement("float2 spvUnpackHalf2x16(uint value)"); + begin_scope(); + statement("return f16tof32(uint2(value & 0xffff, value >> 16));"); + end_scope(); + statement(""); + } + + if (requires_uint2_packing) + { + statement("uint64_t spvPackUint2x32(uint2 value)"); + begin_scope(); + statement("return (uint64_t(value.y) << 32) | uint64_t(value.x);"); + end_scope(); + statement(""); + + statement("uint2 spvUnpackUint2x32(uint64_t value)"); + begin_scope(); + statement("uint2 Unpacked;"); + statement("Unpacked.x = uint(value & 0xffffffff);"); + statement("Unpacked.y = uint(value >> 32);"); + statement("return Unpacked;"); + end_scope(); + statement(""); + } + + if (requires_explicit_fp16_packing) + { + // HLSL does not pack into a single word sadly :( + statement("uint spvPackFloat2x16(min16float2 value)"); + begin_scope(); + statement("uint2 Packed = f32tof16(value);"); + statement("return Packed.x | (Packed.y << 16);"); + end_scope(); + statement(""); + + statement("min16float2 spvUnpackFloat2x16(uint value)"); + begin_scope(); + statement("return min16float2(f16tof32(uint2(value & 0xffff, value >> 16)));"); + end_scope(); + statement(""); + } + + // HLSL does not seem to have builtins for these operation, so roll them by hand ... + if (requires_unorm8_packing) + { + statement("uint spvPackUnorm4x8(float4 value)"); + begin_scope(); + statement("uint4 Packed = uint4(round(saturate(value) * 255.0));"); + statement("return Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24);"); + end_scope(); + statement(""); + + statement("float4 spvUnpackUnorm4x8(uint value)"); + begin_scope(); + statement("uint4 Packed = uint4(value & 0xff, (value >> 8) & 0xff, (value >> 16) & 0xff, value >> 24);"); + statement("return float4(Packed) / 255.0;"); + end_scope(); + statement(""); + } + + if (requires_snorm8_packing) + { + statement("uint spvPackSnorm4x8(float4 value)"); + begin_scope(); + statement("int4 Packed = int4(round(clamp(value, -1.0, 1.0) * 127.0)) & 0xff;"); + statement("return uint(Packed.x | (Packed.y << 8) | (Packed.z << 16) | (Packed.w << 24));"); + end_scope(); + statement(""); + + statement("float4 spvUnpackSnorm4x8(uint value)"); + begin_scope(); + statement("int SignedValue = int(value);"); + statement("int4 Packed = int4(SignedValue << 24, SignedValue << 16, SignedValue << 8, SignedValue) >> 24;"); + statement("return clamp(float4(Packed) / 127.0, -1.0, 1.0);"); + end_scope(); + statement(""); + } + + if (requires_unorm16_packing) + { + statement("uint spvPackUnorm2x16(float2 value)"); + begin_scope(); + statement("uint2 Packed = uint2(round(saturate(value) * 65535.0));"); + statement("return Packed.x | (Packed.y << 16);"); + end_scope(); + statement(""); + + statement("float2 spvUnpackUnorm2x16(uint value)"); + begin_scope(); + statement("uint2 Packed = uint2(value & 0xffff, value >> 16);"); + statement("return float2(Packed) / 65535.0;"); + end_scope(); + statement(""); + } + + if (requires_snorm16_packing) + { + statement("uint spvPackSnorm2x16(float2 value)"); + begin_scope(); + statement("int2 Packed = int2(round(clamp(value, -1.0, 1.0) * 32767.0)) & 0xffff;"); + statement("return uint(Packed.x | (Packed.y << 16));"); + end_scope(); + statement(""); + + statement("float2 spvUnpackSnorm2x16(uint value)"); + begin_scope(); + statement("int SignedValue = int(value);"); + statement("int2 Packed = int2(SignedValue << 16, SignedValue) >> 16;"); + statement("return clamp(float2(Packed) / 32767.0, -1.0, 1.0);"); + end_scope(); + statement(""); + } + + if (requires_bitfield_insert) + { + static const char *types[] = { "uint", "uint2", "uint3", "uint4" }; + for (auto &type : types) + { + statement(type, " spvBitfieldInsert(", type, " Base, ", type, " Insert, uint Offset, uint Count)"); + begin_scope(); + statement("uint Mask = Count == 32 ? 0xffffffff : (((1u << Count) - 1) << (Offset & 31));"); + statement("return (Base & ~Mask) | ((Insert << Offset) & Mask);"); + end_scope(); + statement(""); + } + } + + if (requires_bitfield_extract) + { + static const char *unsigned_types[] = { "uint", "uint2", "uint3", "uint4" }; + for (auto &type : unsigned_types) + { + statement(type, " spvBitfieldUExtract(", type, " Base, uint Offset, uint Count)"); + begin_scope(); + statement("uint Mask = Count == 32 ? 0xffffffff : ((1 << Count) - 1);"); + statement("return (Base >> Offset) & Mask;"); + end_scope(); + statement(""); + } + + // In this overload, we will have to do sign-extension, which we will emulate by shifting up and down. + static const char *signed_types[] = { "int", "int2", "int3", "int4" }; + for (auto &type : signed_types) + { + statement(type, " spvBitfieldSExtract(", type, " Base, int Offset, int Count)"); + begin_scope(); + statement("int Mask = Count == 32 ? -1 : ((1 << Count) - 1);"); + statement(type, " Masked = (Base >> Offset) & Mask;"); + statement("int ExtendShift = (32 - Count) & 31;"); + statement("return (Masked << ExtendShift) >> ExtendShift;"); + end_scope(); + statement(""); + } + } + + if (requires_inverse_2x2) + { + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement("float2x2 spvInverse(float2x2 m)"); + begin_scope(); + statement("float2x2 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = m[1][1];"); + statement("adj[0][1] = -m[0][1];"); + statement_no_indent(""); + statement("adj[1][0] = -m[1][0];"); + statement("adj[1][1] = m[0][0];"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + } + + if (requires_inverse_3x3) + { + statement("// Returns the determinant of a 2x2 matrix."); + statement("float spvDet2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement_no_indent(""); + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement("float3x3 spvInverse(float3x3 m)"); + begin_scope(); + statement("float3x3 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]);"); + statement("adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]);"); + statement("adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]);"); + statement_no_indent(""); + statement("adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]);"); + statement("adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]);"); + statement("adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]);"); + statement_no_indent(""); + statement("adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]);"); + statement("adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]);"); + statement("adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + } + + if (requires_inverse_4x4) + { + if (!requires_inverse_3x3) + { + statement("// Returns the determinant of a 2x2 matrix."); + statement("float spvDet2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement(""); + } + + statement("// Returns the determinant of a 3x3 matrix."); + statement("float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, " + "float c2, float c3)"); + begin_scope(); + statement("return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * " + "spvDet2x2(a2, a3, " + "b2, b3);"); + end_scope(); + statement_no_indent(""); + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement("float4x4 spvInverse(float4x4 m)"); + begin_scope(); + statement("float4x4 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement( + "adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement( + "adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement( + "adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement( + "adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement( + "adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement( + "adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement( + "adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement( + "adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement( + "adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement( + "adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement( + "adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement( + "adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], " + "m[2][3]);"); + statement_no_indent(""); + statement( + "adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement( + "adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement( + "adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement( + "adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], " + "m[2][2]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] " + "* m[3][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + } + + if (requires_scalar_reflect) + { + // FP16/FP64? No templates in HLSL. + statement("float spvReflect(float i, float n)"); + begin_scope(); + statement("return i - 2.0 * dot(n, i) * n;"); + end_scope(); + statement(""); + } + + if (requires_scalar_refract) + { + // FP16/FP64? No templates in HLSL. + statement("float spvRefract(float i, float n, float eta)"); + begin_scope(); + statement("float NoI = n * i;"); + statement("float NoI2 = NoI * NoI;"); + statement("float k = 1.0 - eta * eta * (1.0 - NoI2);"); + statement("if (k < 0.0)"); + begin_scope(); + statement("return 0.0;"); + end_scope(); + statement("else"); + begin_scope(); + statement("return eta * i - (eta * NoI + sqrt(k)) * n;"); + end_scope(); + end_scope(); + statement(""); + } + + if (requires_scalar_faceforward) + { + // FP16/FP64? No templates in HLSL. + statement("float spvFaceForward(float n, float i, float nref)"); + begin_scope(); + statement("return i * nref < 0.0 ? n : -n;"); + end_scope(); + statement(""); + } + + for (TypeID type_id : composite_selection_workaround_types) + { + // Need out variable since HLSL does not support returning arrays. + auto &type = get(type_id); + auto type_str = type_to_glsl(type); + auto type_arr_str = type_to_array_glsl(type, 0); + statement("void spvSelectComposite(out ", type_str, " out_value", type_arr_str, ", bool cond, ", + type_str, " true_val", type_arr_str, ", ", + type_str, " false_val", type_arr_str, ")"); + begin_scope(); + statement("if (cond)"); + begin_scope(); + statement("out_value = true_val;"); + end_scope(); + statement("else"); + begin_scope(); + statement("out_value = false_val;"); + end_scope(); + end_scope(); + statement(""); + } + + if (is_mesh_shader && options.vertex.flip_vert_y) + { + statement("float4 spvFlipVertY(float4 v)"); + begin_scope(); + statement("return float4(v.x, -v.y, v.z, v.w);"); + end_scope(); + statement(""); + statement("float spvFlipVertY(float v)"); + begin_scope(); + statement("return -v;"); + end_scope(); + statement(""); + } +} + +void CompilerHLSL::emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav, + const char *type_qualifier) +{ + if (variant_mask == 0) + return; + + static const char *types[QueryTypeCount] = { "float", "int", "uint" }; + static const char *dims[QueryDimCount] = { "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray", + "Texture3D", "Buffer", "TextureCube", "TextureCubeArray", + "Texture2DMS", "Texture2DMSArray" }; + + static const bool has_lod[QueryDimCount] = { true, true, true, true, true, false, true, true, false, false }; + + static const char *ret_types[QueryDimCount] = { + "uint", "uint2", "uint2", "uint3", "uint3", "uint", "uint2", "uint3", "uint2", "uint3", + }; + + static const uint32_t return_arguments[QueryDimCount] = { + 1, 2, 2, 3, 3, 1, 2, 3, 2, 3, + }; + + for (uint32_t index = 0; index < QueryDimCount; index++) + { + for (uint32_t type_index = 0; type_index < QueryTypeCount; type_index++) + { + uint32_t bit = 16 * type_index + index; + uint64_t mask = 1ull << bit; + + if ((variant_mask & mask) == 0) + continue; + + statement(ret_types[index], " spv", (uav ? "Image" : "Texture"), "Size(", (uav ? "RW" : ""), + dims[index], "<", type_qualifier, types[type_index], vecsize_qualifier, "> Tex, ", + (uav ? "" : "uint Level, "), "out uint Param)"); + begin_scope(); + statement(ret_types[index], " ret;"); + switch (return_arguments[index]) + { + case 1: + if (has_lod[index] && !uav) + statement("Tex.GetDimensions(Level, ret.x, Param);"); + else + { + statement("Tex.GetDimensions(ret.x);"); + statement("Param = 0u;"); + } + break; + case 2: + if (has_lod[index] && !uav) + statement("Tex.GetDimensions(Level, ret.x, ret.y, Param);"); + else if (!uav) + statement("Tex.GetDimensions(ret.x, ret.y, Param);"); + else + { + statement("Tex.GetDimensions(ret.x, ret.y);"); + statement("Param = 0u;"); + } + break; + case 3: + if (has_lod[index] && !uav) + statement("Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param);"); + else if (!uav) + statement("Tex.GetDimensions(ret.x, ret.y, ret.z, Param);"); + else + { + statement("Tex.GetDimensions(ret.x, ret.y, ret.z);"); + statement("Param = 0u;"); + } + break; + } + + statement("return ret;"); + end_scope(); + statement(""); + } + } +} + +void CompilerHLSL::analyze_meshlet_writes() +{ + uint32_t id_per_vertex = 0; + uint32_t id_per_primitive = 0; + bool need_per_primitive = false; + bool need_per_vertex = false; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + if (var.storage == StorageClassOutput && block && is_builtin_variable(var)) + { + auto flags = get_buffer_block_flags(var.self); + if (flags.get(DecorationPerPrimitiveEXT)) + id_per_primitive = var.self; + else + id_per_vertex = var.self; + } + else if (var.storage == StorageClassOutput) + { + Bitset flags; + if (block) + flags = get_buffer_block_flags(var.self); + else + flags = get_decoration_bitset(var.self); + + if (flags.get(DecorationPerPrimitiveEXT)) + need_per_primitive = true; + else + need_per_vertex = true; + } + }); + + // If we have per-primitive outputs, and no per-primitive builtins, + // empty version of gl_MeshPerPrimitiveEXT will be emitted. + // If we don't use block IO for vertex output, we'll also need to synthesize the PerVertex block. + + const auto generate_block = [&](const char *block_name, const char *instance_name, bool per_primitive) -> uint32_t { + auto &execution = get_entry_point(); + + uint32_t op_type = ir.increase_bound_by(4); + uint32_t op_arr = op_type + 1; + uint32_t op_ptr = op_type + 2; + uint32_t op_var = op_type + 3; + + auto &type = set(op_type, OpTypeStruct); + type.basetype = SPIRType::Struct; + set_name(op_type, block_name); + set_decoration(op_type, DecorationBlock); + if (per_primitive) + set_decoration(op_type, DecorationPerPrimitiveEXT); + + auto &arr = set(op_arr, type); + arr.op = OpTypeArray; + arr.parent_type = type.self; + arr.array.push_back(per_primitive ? execution.output_primitives : execution.output_vertices); + arr.array_size_literal.push_back(true); + + auto &ptr = set(op_ptr, arr); + ptr.parent_type = arr.self; + ptr.op = OpTypePointer; + ptr.pointer = true; + ptr.pointer_depth++; + ptr.storage = StorageClassOutput; + set_decoration(op_ptr, DecorationBlock); + set_name(op_ptr, block_name); + + auto &var = set(op_var, op_ptr, StorageClassOutput); + if (per_primitive) + set_decoration(op_var, DecorationPerPrimitiveEXT); + set_name(op_var, instance_name); + execution.interface_variables.push_back(var.self); + + return op_var; + }; + + if (id_per_vertex == 0 && need_per_vertex) + id_per_vertex = generate_block("gl_MeshPerVertexEXT", "gl_MeshVerticesEXT", false); + if (id_per_primitive == 0 && need_per_primitive) + id_per_primitive = generate_block("gl_MeshPerPrimitiveEXT", "gl_MeshPrimitivesEXT", true); + + unordered_set processed_func_ids; + analyze_meshlet_writes(ir.default_entry_point, id_per_vertex, id_per_primitive, processed_func_ids); +} + +void CompilerHLSL::analyze_meshlet_writes(uint32_t func_id, uint32_t id_per_vertex, uint32_t id_per_primitive, + std::unordered_set &processed_func_ids) +{ + // Avoid processing a function more than once + if (processed_func_ids.find(func_id) != processed_func_ids.end()) + return; + processed_func_ids.insert(func_id); + + auto &func = get(func_id); + // Recursively establish global args added to functions on which we depend. + for (auto& block : func.blocks) + { + auto &b = get(block); + for (auto &i : b.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpFunctionCall: + { + // Then recurse into the function itself to extract globals used internally in the function + uint32_t inner_func_id = ops[2]; + analyze_meshlet_writes(inner_func_id, id_per_vertex, id_per_primitive, processed_func_ids); + auto &inner_func = get(inner_func_id); + for (auto &iarg : inner_func.arguments) + { + if (!iarg.alias_global_variable) + continue; + + bool already_declared = false; + for (auto &arg : func.arguments) + { + if (arg.id == iarg.id) + { + already_declared = true; + break; + } + } + + if (!already_declared) + { + // basetype is effectively ignored here since we declare the argument + // with explicit types. Just pass down a valid type. + func.arguments.push_back({ expression_type_id(iarg.id), iarg.id, + iarg.read_count, iarg.write_count, true }); + } + } + break; + } + + case OpStore: + case OpLoad: + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + case OpInBoundsPtrAccessChain: + case OpArrayLength: + { + auto *var = maybe_get(ops[op == OpStore ? 0 : 2]); + if (var && (var->storage == StorageClassOutput || var->storage == StorageClassTaskPayloadWorkgroupEXT)) + { + bool already_declared = false; + auto builtin_type = BuiltIn(get_decoration(var->self, DecorationBuiltIn)); + + uint32_t var_id = var->self; + if (var->storage != StorageClassTaskPayloadWorkgroupEXT && + builtin_type != BuiltInPrimitivePointIndicesEXT && + builtin_type != BuiltInPrimitiveLineIndicesEXT && + builtin_type != BuiltInPrimitiveTriangleIndicesEXT) + { + var_id = is_per_primitive_variable(*var) ? id_per_primitive : id_per_vertex; + } + + for (auto &arg : func.arguments) + { + if (arg.id == var_id) + { + already_declared = true; + break; + } + } + + if (!already_declared) + { + // basetype is effectively ignored here since we declare the argument + // with explicit types. Just pass down a valid type. + uint32_t type_id = expression_type_id(var_id); + if (var->storage == StorageClassTaskPayloadWorkgroupEXT) + func.arguments.push_back({ type_id, var_id, 1u, 0u, true }); + else + func.arguments.push_back({ type_id, var_id, 1u, 1u, true }); + } + } + break; + } + + default: + break; + } + } + } +} + +string CompilerHLSL::layout_for_member(const SPIRType &type, uint32_t index) +{ + auto &flags = get_member_decoration_bitset(type.self, index); + + // HLSL can emit row_major or column_major decoration in any struct. + // Do not try to merge combined decorations for children like in GLSL. + + // Flip the convention. HLSL is a bit odd in that the memory layout is column major ... but the language API is "row-major". + // The way to deal with this is to multiply everything in inverse order, and reverse the memory layout. + if (flags.get(DecorationColMajor)) + return "row_major "; + else if (flags.get(DecorationRowMajor)) + return "column_major "; + + return ""; +} + +void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier, uint32_t base_offset) +{ + auto &membertype = get(member_type_id); + + Bitset memberflags; + auto &memb = ir.meta[type.self].members; + if (index < memb.size()) + memberflags = memb[index].decoration_flags; + + string packing_offset; + bool is_push_constant = type.storage == StorageClassPushConstant; + + if ((has_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset) || is_push_constant) && + has_member_decoration(type.self, index, DecorationOffset)) + { + uint32_t offset = memb[index].offset - base_offset; + if (offset & 3) + SPIRV_CROSS_THROW("Cannot pack on tighter bounds than 4 bytes in HLSL."); + + static const char *packing_swizzle[] = { "", ".y", ".z", ".w" }; + packing_offset = join(" : packoffset(c", offset / 16, packing_swizzle[(offset & 15) >> 2], ")"); + } + + statement(layout_for_member(type, index), qualifier, + variable_decl(membertype, to_member_name(type, index)), packing_offset, ";"); +} + +void CompilerHLSL::emit_rayquery_function(const char *commited, const char *candidate, const uint32_t *ops) +{ + flush_variable_declaration(ops[0]); + uint32_t is_commited = evaluate_constant_u32(ops[3]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), is_commited ? commited : candidate), false); +} + +void CompilerHLSL::emit_mesh_tasks(SPIRBlock &block) +{ + if (block.mesh.payload != 0) + { + statement("DispatchMesh(", to_unpacked_expression(block.mesh.groups[0]), ", ", to_unpacked_expression(block.mesh.groups[1]), ", ", + to_unpacked_expression(block.mesh.groups[2]), ", ", to_unpacked_expression(block.mesh.payload), ");"); + } + else + { + SPIRV_CROSS_THROW("Amplification shader in HLSL must have payload"); + } +} + +void CompilerHLSL::emit_geometry_stream_append() +{ + begin_scope(); + statement("SPIRV_Cross_Output stage_output;"); + + active_output_builtins.for_each_bit( + [&](uint32_t i) + { + if (i == BuiltInPointSize && hlsl_options.shader_model > 30) + return; + switch (static_cast(i)) + { + case BuiltInClipDistance: + for (uint32_t clip = 0; clip < clip_distance_count; clip++) + statement("stage_output.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3], " = gl_ClipDistance[", + clip, "];"); + break; + case BuiltInCullDistance: + for (uint32_t cull = 0; cull < cull_distance_count; cull++) + statement("stage_output.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3], " = gl_CullDistance[", + cull, "];"); + break; + case BuiltInSampleMask: + statement("stage_output.gl_SampleMask = gl_SampleMask[0];"); + break; + default: + { + auto builtin_expr = builtin_to_glsl(static_cast(i), StorageClassOutput); + statement("stage_output.", builtin_expr, " = ", builtin_expr, ";"); + } + break; + } + }); + + ir.for_each_typed_id( + [&](uint32_t, SPIRVariable &var) + { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + + if (var.storage != StorageClassOutput) + return; + + if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self)) + { + if (block) + { + auto type_name = to_name(type.self); + auto var_name = to_name(var.self); + for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++) + { + auto mbr_name = to_member_name(type, mbr_idx); + auto flat_name = join(type_name, "_", mbr_name); + statement("stage_output.", flat_name, " = ", var_name, ".", mbr_name, ";"); + } + } + else + { + auto name = to_name(var.self); + if (hlsl_options.shader_model <= 30 && get_entry_point().model == ExecutionModelFragment) + { + string output_filler; + for (uint32_t size = type.vecsize; size < 4; ++size) + output_filler += ", 0.0"; + statement("stage_output.", name, " = float4(", name, output_filler, ");"); + } + else + statement("stage_output.", name, " = ", name, ";"); + } + } + }); + + statement("geometry_stream.Append(stage_output);"); + end_scope(); +} + +void CompilerHLSL::emit_buffer_block(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + + bool is_uav = var.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock); + + if (flattened_buffer_blocks.count(var.self)) + { + emit_buffer_block_flattened(var); + } + else if (is_uav) + { + Bitset flags = ir.get_buffer_block_flags(var); + bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self); + bool is_coherent = flags.get(DecorationCoherent) && !is_readonly; + bool is_interlocked = interlocked_resources.count(var.self) > 0; + + auto to_structuredbuffer_subtype_name = [this](const SPIRType &parent_type) -> std::string + { + if (parent_type.basetype == SPIRType::Struct && parent_type.member_types.size() == 1) + { + // Use type of first struct member as a StructuredBuffer will have only one '._m0' field in SPIR-V + const auto &member0_type = this->get(parent_type.member_types.front()); + return this->type_to_glsl(member0_type); + } + else + { + // Otherwise, this StructuredBuffer only has a basic subtype, e.g. StructuredBuffer + return this->type_to_glsl(parent_type); + } + }; + + std::string type_name; + if (is_user_type_structured(var.self)) + type_name = join(is_readonly ? "" : is_interlocked ? "RasterizerOrdered" : "RW", "StructuredBuffer<", to_structuredbuffer_subtype_name(type), ">"); + else + type_name = is_readonly ? "ByteAddressBuffer" : is_interlocked ? "RasterizerOrderedByteAddressBuffer" : "RWByteAddressBuffer"; + + add_resource_name(var.self); + statement(is_coherent ? "globallycoherent " : "", type_name, " ", to_name(var.self), type_to_array_glsl(type, var.self), + to_resource_binding(var), ";"); + } + else + { + if (type.array.empty()) + { + // Flatten the top-level struct so we can use packoffset, + // this restriction is similar to GLSL where layout(offset) is not possible on sub-structs. + flattened_structs[var.self] = false; + + // Prefer the block name if possible. + auto buffer_name = to_name(type.self, false); + if (ir.meta[type.self].decoration.alias.empty() || + resource_names.find(buffer_name) != end(resource_names) || + block_names.find(buffer_name) != end(block_names)) + { + buffer_name = get_block_fallback_name(var.self); + } + + add_variable(block_names, resource_names, buffer_name); + + // If for some reason buffer_name is an illegal name, make a final fallback to a workaround name. + // This cannot conflict with anything else, so we're safe now. + if (buffer_name.empty()) + buffer_name = join("_", get(var.basetype).self, "_", var.self); + + uint32_t failed_index = 0; + if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, &failed_index)) + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + else + { + SPIRV_CROSS_THROW(join("cbuffer ID ", var.self, " (name: ", buffer_name, "), member index ", + failed_index, " (name: ", to_member_name(type, failed_index), + ") cannot be expressed with either HLSL packing layout or packoffset.")); + } + + block_names.insert(buffer_name); + + // Save for post-reflection later. + declared_block_names[var.self] = buffer_name; + + type.member_name_cache.clear(); + // var.self can be used as a backup name for the block name, + // so we need to make sure we don't disturb the name here on a recompile. + // It will need to be reset if we have to recompile. + preserve_alias_on_reset(var.self); + add_resource_name(var.self); + statement("cbuffer ", buffer_name, to_resource_binding(var)); + begin_scope(); + + uint32_t i = 0; + for (auto &member : type.member_types) + { + add_member_name(type, i); + auto backup_name = get_member_name(type.self, i); + auto member_name = to_member_name(type, i); + member_name = join(to_name(var.self), "_", member_name); + ParsedIR::sanitize_underscores(member_name); + set_member_name(type.self, i, member_name); + emit_struct_member(type, member, i, ""); + set_member_name(type.self, i, backup_name); + i++; + } + + end_scope_decl(); + statement(""); + } + else + { + if (hlsl_options.shader_model < 51) + SPIRV_CROSS_THROW( + "Need ConstantBuffer to use arrays of UBOs, but this is only supported in SM 5.1."); + + add_resource_name(type.self); + add_resource_name(var.self); + + // ConstantBuffer does not support packoffset, so it is unuseable unless everything aligns as we expect. + uint32_t failed_index = 0; + if (!buffer_is_packing_standard(type, BufferPackingHLSLCbuffer, &failed_index)) + { + SPIRV_CROSS_THROW(join("HLSL ConstantBuffer ID ", var.self, " (name: ", to_name(type.self), + "), member index ", failed_index, " (name: ", to_member_name(type, failed_index), + ") cannot be expressed with normal HLSL packing rules.")); + } + + emit_struct(get(type.self)); + statement("ConstantBuffer<", to_name(type.self), "> ", to_name(var.self), type_to_array_glsl(type, var.self), + to_resource_binding(var), ";"); + } + } +} + +void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var) +{ + if (flattened_buffer_blocks.count(var.self)) + { + emit_buffer_block_flattened(var); + } + else if (root_constants_layout.empty()) + { + emit_buffer_block(var); + } + else + { + for (const auto &layout : root_constants_layout) + { + auto &type = get(var.basetype); + + uint32_t failed_index = 0; + if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, &failed_index, layout.start, + layout.end)) + set_extended_decoration(type.self, SPIRVCrossDecorationExplicitOffset); + else + { + SPIRV_CROSS_THROW(join("Root constant cbuffer ID ", var.self, " (name: ", to_name(type.self), ")", + ", member index ", failed_index, " (name: ", to_member_name(type, failed_index), + ") cannot be expressed with either HLSL packing layout or packoffset.")); + } + + flattened_structs[var.self] = false; + type.member_name_cache.clear(); + add_resource_name(var.self); + auto &memb = ir.meta[type.self].members; + + statement("cbuffer SPIRV_CROSS_RootConstant_", to_name(var.self), + to_resource_register(HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT, 'b', layout.binding, layout.space)); + begin_scope(); + + // Index of the next field in the generated root constant constant buffer + auto constant_index = 0u; + + // Iterate over all member of the push constant and check which of the fields + // fit into the given root constant layout. + for (auto i = 0u; i < memb.size(); i++) + { + const auto offset = memb[i].offset; + if (layout.start <= offset && offset < layout.end) + { + const auto &member = type.member_types[i]; + + add_member_name(type, constant_index); + auto backup_name = get_member_name(type.self, i); + auto member_name = to_member_name(type, i); + member_name = join(to_name(var.self), "_", member_name); + ParsedIR::sanitize_underscores(member_name); + set_member_name(type.self, constant_index, member_name); + emit_struct_member(type, member, i, "", layout.start); + set_member_name(type.self, constant_index, backup_name); + + constant_index++; + } + } + + end_scope_decl(); + } + } +} + +string CompilerHLSL::to_sampler_expression(uint32_t id) +{ + auto expr = join("_", to_non_uniform_aware_expression(id)); + auto index = expr.find_first_of('['); + if (index == string::npos) + { + return expr + "_sampler"; + } + else + { + // We have an expression like _ident[array], so we cannot tack on _sampler, insert it inside the string instead. + return expr.insert(index, "_sampler"); + } +} + +void CompilerHLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) +{ + if (hlsl_options.shader_model >= 40 && combined_image_samplers.empty()) + { + set(result_id, result_type, image_id, samp_id); + } + else + { + // Make sure to suppress usage tracking. It is illegal to create temporaries of opaque types. + emit_op(result_type, result_id, to_combined_image_sampler(image_id, samp_id), true, true); + } +} + +string CompilerHLSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) +{ + string arg_str = CompilerGLSL::to_func_call_arg(arg, id); + + if (hlsl_options.shader_model <= 30) + return arg_str; + + // Manufacture automatic sampler arg if the arg is a SampledImage texture and we're in modern HLSL. + auto &type = expression_type(id); + + // We don't have to consider combined image samplers here via OpSampledImage because + // those variables cannot be passed as arguments to functions. + // Only global SampledImage variables may be used as arguments. + if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer) + arg_str += ", " + to_sampler_expression(id); + + return arg_str; +} + +string CompilerHLSL::get_inner_entry_point_name() const +{ + auto &execution = get_entry_point(); + + if (hlsl_options.use_entry_point_name) + { + auto name = join(execution.name, "_inner"); + ParsedIR::sanitize_underscores(name); + return name; + } + + if (execution.model == ExecutionModelVertex) + return "vert_main"; + else if (execution.model == ExecutionModelFragment) + return "frag_main"; + else if (execution.model == ExecutionModelGLCompute) + return "comp_main"; + else if (execution.model == ExecutionModelGeometry) + return "geom_main"; + else if (execution.model == ExecutionModelMeshEXT) + return "mesh_main"; + else if (execution.model == ExecutionModelTaskEXT) + return "task_main"; + else + SPIRV_CROSS_THROW("Unsupported execution model."); +} + +uint32_t CompilerHLSL::input_vertices_from_execution_mode(SPIREntryPoint &execution) const +{ + uint32_t input_vertices = 1; + + if (execution.flags.get(ExecutionModeInputLines)) + input_vertices = 2; + else if (execution.flags.get(ExecutionModeInputLinesAdjacency)) + input_vertices = 4; + else if (execution.flags.get(ExecutionModeInputTrianglesAdjacency)) + input_vertices = 6; + else if (execution.flags.get(ExecutionModeTriangles)) + input_vertices = 3; + else if (execution.flags.get(ExecutionModeInputPoints)) + input_vertices = 1; + else + SPIRV_CROSS_THROW("Unsupported execution model."); + return input_vertices; +} + +void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + // Avoid shadow declarations. + local_variable_names = resource_names; + + string decl; + + auto &type = get(func.return_type); + if (type.array.empty()) + { + decl += flags_to_qualifiers_glsl(type, 0, return_flags); + decl += type_to_glsl(type); + decl += " "; + } + else + { + // We cannot return arrays in HLSL, so "return" through an out variable. + decl = "void "; + } + + if (func.self == ir.default_entry_point) + { + decl += get_inner_entry_point_name(); + processing_entry_point = true; + } + else + decl += to_name(func.self); + + decl += "("; + SmallVector arglist; + + if (!type.array.empty()) + { + // Fake array returns by writing to an out array instead. + string out_argument; + out_argument += "out "; + out_argument += type_to_glsl(type); + out_argument += " "; + out_argument += "spvReturnValue"; + out_argument += type_to_array_glsl(type, 0); + arglist.push_back(std::move(out_argument)); + } + + for (auto &arg : func.arguments) + { + // Do not pass in separate images or samplers if we're remapping + // to combined image samplers. + if (skip_argument(arg.id)) + continue; + + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Flatten a combined sampler to two separate arguments in modern HLSL. + auto &arg_type = get(arg.type); + if (hlsl_options.shader_model > 30 && arg_type.basetype == SPIRType::SampledImage && + arg_type.image.dim != DimBuffer) + { + // Manufacture automatic sampler arg for SampledImage texture + arglist.push_back(join(is_depth_image(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ", + to_sampler_expression(arg.id), type_to_array_glsl(arg_type, arg.id))); + } + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + for (auto &arg : func.shadow_arguments) + { + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an implementation + // to use same name for variables. + // Since we want to make the GLSL debuggable and somewhat sane, use fallback names for variables which are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + auto *var = maybe_get(arg.id); + if (var) + var->parameter = &arg; + } + + if ((func.self == ir.default_entry_point || func.emits_geometry) && + get_entry_point().model == ExecutionModelGeometry) + { + auto &execution = get_entry_point(); + + uint32_t input_vertices = input_vertices_from_execution_mode(execution); + + const char *prim; + if (execution.flags.get(ExecutionModeInputLinesAdjacency)) + prim = "lineadj"; + else if (execution.flags.get(ExecutionModeInputLines)) + prim = "line"; + else if (execution.flags.get(ExecutionModeInputTrianglesAdjacency)) + prim = "triangleadj"; + else if (execution.flags.get(ExecutionModeTriangles)) + prim = "triangle"; + else + prim = "point"; + + const char *stream_type; + if (execution.flags.get(ExecutionModeOutputPoints)) + stream_type = "PointStream"; + else if (execution.flags.get(ExecutionModeOutputLineStrip)) + stream_type = "LineStream"; + else + stream_type = "TriangleStream"; + + if (func.self == ir.default_entry_point) + arglist.push_back(join(prim, " SPIRV_Cross_Input stage_input[", input_vertices, "]")); + arglist.push_back(join("inout ", stream_type, " ", "geometry_stream")); + } + + decl += merge(arglist); + decl += ")"; + statement(decl); +} + +void CompilerHLSL::emit_hlsl_entry_point() +{ + SmallVector arguments; + + if (require_input && get_entry_point().model != ExecutionModelGeometry) + arguments.push_back("SPIRV_Cross_Input stage_input"); + + auto &execution = get_entry_point(); + + uint32_t input_vertices = 1; + + switch (execution.model) + { + case ExecutionModelGeometry: + { + input_vertices = input_vertices_from_execution_mode(execution); + + string prim; + if (execution.flags.get(ExecutionModeInputLinesAdjacency)) + prim = "lineadj"; + else if (execution.flags.get(ExecutionModeInputLines)) + prim = "line"; + else if (execution.flags.get(ExecutionModeInputTrianglesAdjacency)) + prim = "triangleadj"; + else if (execution.flags.get(ExecutionModeTriangles)) + prim = "triangle"; + else + prim = "point"; + + string stream_type; + if (execution.flags.get(ExecutionModeOutputPoints)) + { + stream_type = "PointStream"; + } + else if (execution.flags.get(ExecutionModeOutputLineStrip)) + { + stream_type = "LineStream"; + } + else + { + stream_type = "TriangleStream"; + } + + statement("[maxvertexcount(", execution.output_vertices, ")]"); + arguments.push_back(join(prim, " SPIRV_Cross_Input stage_input[", input_vertices, "]")); + if (active_input_builtins.get(BuiltInPrimitiveId)) + arguments.push_back("uint gl_PrimitiveID : SV_PrimitiveID"); + arguments.push_back(join("inout ", stream_type, " ", "geometry_stream")); + break; + } + case ExecutionModelTaskEXT: + case ExecutionModelMeshEXT: + case ExecutionModelGLCompute: + { + if (execution.model == ExecutionModelMeshEXT) + { + if (execution.flags.get(ExecutionModeOutputTrianglesEXT)) + statement("[outputtopology(\"triangle\")]"); + else if (execution.flags.get(ExecutionModeOutputLinesEXT)) + statement("[outputtopology(\"line\")]"); + else if (execution.flags.get(ExecutionModeOutputPoints)) + SPIRV_CROSS_THROW("Topology mode \"points\" is not supported in DirectX"); + + auto &func = get(ir.default_entry_point); + for (auto &arg : func.arguments) + { + auto &var = get(arg.id); + auto &base_type = get(var.basetype); + bool block = has_decoration(base_type.self, DecorationBlock); + if (var.storage == StorageClassTaskPayloadWorkgroupEXT) + { + arguments.push_back("in payload " + variable_decl(var)); + } + else if (block) + { + auto flags = get_buffer_block_flags(var.self); + if (flags.get(DecorationPerPrimitiveEXT) || has_decoration(arg.id, DecorationPerPrimitiveEXT)) + { + arguments.push_back("out primitives gl_MeshPerPrimitiveEXT gl_MeshPrimitivesEXT[" + + std::to_string(execution.output_primitives) + "]"); + } + else + { + arguments.push_back("out vertices gl_MeshPerVertexEXT gl_MeshVerticesEXT[" + + std::to_string(execution.output_vertices) + "]"); + } + } + else + { + if (execution.flags.get(ExecutionModeOutputTrianglesEXT)) + { + arguments.push_back("out indices uint3 gl_PrimitiveTriangleIndicesEXT[" + + std::to_string(execution.output_primitives) + "]"); + } + else + { + arguments.push_back("out indices uint2 gl_PrimitiveLineIndicesEXT[" + + std::to_string(execution.output_primitives) + "]"); + } + } + } + } + SpecializationConstant wg_x, wg_y, wg_z; + get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + + uint32_t x = execution.workgroup_size.x; + uint32_t y = execution.workgroup_size.y; + uint32_t z = execution.workgroup_size.z; + + if (!execution.workgroup_size.constant && execution.flags.get(ExecutionModeLocalSizeId)) + { + if (execution.workgroup_size.id_x) + x = get(execution.workgroup_size.id_x).scalar(); + if (execution.workgroup_size.id_y) + y = get(execution.workgroup_size.id_y).scalar(); + if (execution.workgroup_size.id_z) + z = get(execution.workgroup_size.id_z).scalar(); + } + + auto x_expr = wg_x.id ? get(wg_x.id).specialization_constant_macro_name : to_string(x); + auto y_expr = wg_y.id ? get(wg_y.id).specialization_constant_macro_name : to_string(y); + auto z_expr = wg_z.id ? get(wg_z.id).specialization_constant_macro_name : to_string(z); + + statement("[numthreads(", x_expr, ", ", y_expr, ", ", z_expr, ")]"); + break; + } + case ExecutionModelFragment: + if (execution.flags.get(ExecutionModeEarlyFragmentTests)) + statement("[earlydepthstencil]"); + break; + default: + break; + } + + const char *entry_point_name; + if (hlsl_options.use_entry_point_name) + entry_point_name = get_entry_point().name.c_str(); + else + entry_point_name = "main"; + + statement(require_output ? "SPIRV_Cross_Output " : "void ", entry_point_name, "(", merge(arguments), ")"); + begin_scope(); + bool legacy = hlsl_options.shader_model <= 30; + + // Copy builtins from entry point arguments to globals. + active_input_builtins.for_each_bit([&](uint32_t i) { + auto builtin = builtin_to_glsl(static_cast(i), StorageClassInput); + switch (static_cast(i)) + { + case BuiltInPosition: + if (execution.model == ExecutionModelGeometry) + { + statement("for (int i = 0; i < ", input_vertices, "; i++)"); + begin_scope(); + statement(builtin, "[i] = stage_input[i].", builtin, ";"); + end_scope(); + } + else + statement(builtin, " = stage_input.", builtin, ";"); + break; + case BuiltInFragCoord: + // VPOS in D3D9 is sampled at integer locations, apply half-pixel offset to be consistent. + // TODO: Do we need an option here? Any reason why a D3D9 shader would be used + // on a D3D10+ system with a different rasterization config? + if (legacy) + statement(builtin, " = stage_input.", builtin, " + float4(0.5f, 0.5f, 0.0f, 0.0f);"); + else + { + statement(builtin, " = stage_input.", builtin, ";"); + // ZW are undefined in D3D9, only do this fixup here. + statement(builtin, ".w = 1.0 / ", builtin, ".w;"); + } + break; + + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInInstanceIndex: + // D3D semantics are uint, but shader wants int. + if (hlsl_options.support_nonzero_base_vertex_base_instance || hlsl_options.shader_model >= 68) + { + if (hlsl_options.shader_model >= 68) + { + if (static_cast(i) == BuiltInInstanceIndex) + statement(builtin, " = int(stage_input.", builtin, " + stage_input.gl_BaseInstanceARB);"); + else + statement(builtin, " = int(stage_input.", builtin, " + stage_input.gl_BaseVertexARB);"); + } + else + { + if (static_cast(i) == BuiltInInstanceIndex) + statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseInstance;"); + else + statement(builtin, " = int(stage_input.", builtin, ") + SPIRV_Cross_BaseVertex;"); + } + } + else + statement(builtin, " = int(stage_input.", builtin, ");"); + break; + + case BuiltInBaseVertex: + if (hlsl_options.shader_model >= 68) + statement(builtin, " = stage_input.gl_BaseVertexARB;"); + else + statement(builtin, " = SPIRV_Cross_BaseVertex;"); + break; + + case BuiltInBaseInstance: + if (hlsl_options.shader_model >= 68) + statement(builtin, " = stage_input.gl_BaseInstanceARB;"); + else + statement(builtin, " = SPIRV_Cross_BaseInstance;"); + break; + + case BuiltInInstanceId: + // D3D semantics are uint, but shader wants int. + statement(builtin, " = int(stage_input.", builtin, ");"); + break; + + case BuiltInSampleMask: + statement(builtin, "[0] = stage_input.", builtin, ";"); + break; + + case BuiltInNumWorkgroups: + case BuiltInPointCoord: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + case BuiltInHelperInvocation: + break; + + case BuiltInPrimitiveId: + if (execution.model == ExecutionModelGeometry) + { + // PrimitiveId is a separate function parameter for GS. + // The global is named gl_PrimitiveIDIn (GLSL convention). + statement(builtin, " = gl_PrimitiveID;"); + } + else + statement(builtin, " = stage_input.", builtin, ";"); + break; + + case BuiltInInvocationId: + if (execution.model == ExecutionModelTessellationControl) + { + // Copy from function parameter to global. + statement(builtin, " = uCPID;"); + } + else + { + // For geometry shaders, copy from struct as usual. + statement(builtin, " = stage_input[0].", builtin, ";"); + } + break; + + case BuiltInSubgroupEqMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("gl_SubgroupEqMask = 1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96));"); + statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupEqMask.x = 0;"); + statement("if (WaveGetLaneIndex() >= 64 || WaveGetLaneIndex() < 32) gl_SubgroupEqMask.y = 0;"); + statement("if (WaveGetLaneIndex() >= 96 || WaveGetLaneIndex() < 64) gl_SubgroupEqMask.z = 0;"); + statement("if (WaveGetLaneIndex() < 96) gl_SubgroupEqMask.w = 0;"); + break; + + case BuiltInSubgroupGeMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("gl_SubgroupGeMask = ~((1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u);"); + statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupGeMask.x = 0u;"); + statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupGeMask.y = 0u;"); + statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupGeMask.z = 0u;"); + statement("if (WaveGetLaneIndex() < 32) gl_SubgroupGeMask.y = ~0u;"); + statement("if (WaveGetLaneIndex() < 64) gl_SubgroupGeMask.z = ~0u;"); + statement("if (WaveGetLaneIndex() < 96) gl_SubgroupGeMask.w = ~0u;"); + break; + + case BuiltInSubgroupGtMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("uint gt_lane_index = WaveGetLaneIndex() + 1;"); + statement("gl_SubgroupGtMask = ~((1u << (gt_lane_index - uint4(0, 32, 64, 96))) - 1u);"); + statement("if (gt_lane_index >= 32) gl_SubgroupGtMask.x = 0u;"); + statement("if (gt_lane_index >= 64) gl_SubgroupGtMask.y = 0u;"); + statement("if (gt_lane_index >= 96) gl_SubgroupGtMask.z = 0u;"); + statement("if (gt_lane_index >= 128) gl_SubgroupGtMask.w = 0u;"); + statement("if (gt_lane_index < 32) gl_SubgroupGtMask.y = ~0u;"); + statement("if (gt_lane_index < 64) gl_SubgroupGtMask.z = ~0u;"); + statement("if (gt_lane_index < 96) gl_SubgroupGtMask.w = ~0u;"); + break; + + case BuiltInSubgroupLeMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("uint le_lane_index = WaveGetLaneIndex() + 1;"); + statement("gl_SubgroupLeMask = (1u << (le_lane_index - uint4(0, 32, 64, 96))) - 1u;"); + statement("if (le_lane_index >= 32) gl_SubgroupLeMask.x = ~0u;"); + statement("if (le_lane_index >= 64) gl_SubgroupLeMask.y = ~0u;"); + statement("if (le_lane_index >= 96) gl_SubgroupLeMask.z = ~0u;"); + statement("if (le_lane_index >= 128) gl_SubgroupLeMask.w = ~0u;"); + statement("if (le_lane_index < 32) gl_SubgroupLeMask.y = 0u;"); + statement("if (le_lane_index < 64) gl_SubgroupLeMask.z = 0u;"); + statement("if (le_lane_index < 96) gl_SubgroupLeMask.w = 0u;"); + break; + + case BuiltInSubgroupLtMask: + // Emulate these ... + // No 64-bit in HLSL, so have to do it in 32-bit and unroll. + statement("gl_SubgroupLtMask = (1u << (WaveGetLaneIndex() - uint4(0, 32, 64, 96))) - 1u;"); + statement("if (WaveGetLaneIndex() >= 32) gl_SubgroupLtMask.x = ~0u;"); + statement("if (WaveGetLaneIndex() >= 64) gl_SubgroupLtMask.y = ~0u;"); + statement("if (WaveGetLaneIndex() >= 96) gl_SubgroupLtMask.z = ~0u;"); + statement("if (WaveGetLaneIndex() < 32) gl_SubgroupLtMask.y = 0u;"); + statement("if (WaveGetLaneIndex() < 64) gl_SubgroupLtMask.z = 0u;"); + statement("if (WaveGetLaneIndex() < 96) gl_SubgroupLtMask.w = 0u;"); + break; + + case BuiltInClipDistance: + for (uint32_t clip = 0; clip < clip_distance_count; clip++) + statement("gl_ClipDistance[", clip, "] = stage_input.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3], + ";"); + break; + + case BuiltInCullDistance: + for (uint32_t cull = 0; cull < cull_distance_count; cull++) + statement("gl_CullDistance[", cull, "] = stage_input.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3], + ";"); + break; + + default: + statement(builtin, " = stage_input.", builtin, ";"); + break; + } + }); + + // Copy from stage input struct to globals. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + + if (var.storage != StorageClassInput) + return; + + bool is_hidden = is_hidden_io_variable(var); + + bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex; + + if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self) && !is_hidden) + { + if (block) + { + auto type_name = to_name(type.self); + auto var_name = to_name(var.self); + bool is_per_vertex = has_decoration(var.self, DecorationPerVertexKHR); + uint32_t array_size = is_per_vertex ? to_array_size_literal(type) : 0; + + for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++) + { + auto mbr_name = to_member_name(type, mbr_idx); + auto flat_name = join(type_name, "_", mbr_name); + + if (is_per_vertex) + { + for (uint32_t i = 0; i < array_size; i++) + statement(var_name, "[", i, "].", mbr_name, " = GetAttributeAtVertex(stage_input.", flat_name, ", ", i, ");"); + } + else + { + statement(var_name, ".", mbr_name, " = stage_input.", flat_name, ";"); + } + } + } + else + { + auto name = to_name(var.self); + auto &mtype = this->get(var.basetype); + if (need_matrix_unroll && mtype.columns > 1) + { + // Unroll matrices. + for (uint32_t col = 0; col < mtype.columns; col++) + statement(name, "[", col, "] = stage_input.", name, "_", col, ";"); + } + else if (has_decoration(var.self, DecorationPerVertexKHR)) + { + uint32_t array_size = to_array_size_literal(type); + for (uint32_t i = 0; i < array_size; i++) + statement(name, "[", i, "]", " = GetAttributeAtVertex(stage_input.", name, ", ", i, ");"); + } + else + { + if (execution.model == ExecutionModelGeometry) + { + statement("for (int i = 0; i < ", input_vertices, "; i++)"); + begin_scope(); + statement(name, "[i] = stage_input[i].", name, ";"); + end_scope(); + } + else + statement(name, " = stage_input.", name, ";"); + } + } + } + }); + + // Run the shader. + if (execution.model == ExecutionModelVertex || execution.model == ExecutionModelFragment || + execution.model == ExecutionModelGLCompute || execution.model == ExecutionModelMeshEXT || + execution.model == ExecutionModelGeometry || execution.model == ExecutionModelTaskEXT) + { + // For mesh shaders, we receive special arguments that we must pass down as function arguments. + // HLSL does not support proper reference types for passing these IO blocks, + // but DXC post-inlining seems to magically fix it up anyways *shrug*. + SmallVector arglist; + auto &func = get(ir.default_entry_point); + // The arguments are marked out, avoid detecting reads and emitting inout. + + for (auto &arg : func.arguments) + arglist.push_back(to_expression(arg.id, false)); + + if (execution.model == ExecutionModelGeometry) + { + arglist.push_back("stage_input"); + arglist.push_back("geometry_stream"); + } + + statement(get_inner_entry_point_name(), "(", merge(arglist), ");"); + } + else + SPIRV_CROSS_THROW("Unsupported shader stage."); + + // Copy stage outputs. + if (require_output) + { + statement("SPIRV_Cross_Output stage_output;"); + + // Copy builtins from globals to return struct. + active_output_builtins.for_each_bit([&](uint32_t i) { + // PointSize doesn't exist in HLSL SM 4+. + if (i == BuiltInPointSize && !legacy) + return; + + switch (static_cast(i)) + { + case BuiltInClipDistance: + for (uint32_t clip = 0; clip < clip_distance_count; clip++) + statement("stage_output.gl_ClipDistance", clip / 4, ".", "xyzw"[clip & 3], " = gl_ClipDistance[", + clip, "];"); + break; + + case BuiltInCullDistance: + for (uint32_t cull = 0; cull < cull_distance_count; cull++) + statement("stage_output.gl_CullDistance", cull / 4, ".", "xyzw"[cull & 3], " = gl_CullDistance[", + cull, "];"); + break; + + case BuiltInSampleMask: + statement("stage_output.gl_SampleMask = gl_SampleMask[0];"); + break; + + default: + { + auto builtin_expr = builtin_to_glsl(static_cast(i), StorageClassOutput); + statement("stage_output.", builtin_expr, " = ", builtin_expr, ";"); + break; + } + } + }); + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = this->get(var.basetype); + bool block = has_decoration(type.self, DecorationBlock); + + if (var.storage != StorageClassOutput) + return; + + if (!var.remapped_variable && type.pointer && + !is_builtin_variable(var) && + interface_variable_exists_in_entry_point(var.self)) + { + if (block) + { + // I/O blocks need to flatten output. + auto type_name = to_name(type.self); + auto var_name = to_name(var.self); + for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++) + { + auto mbr_name = to_member_name(type, mbr_idx); + auto flat_name = join(type_name, "_", mbr_name); + statement("stage_output.", flat_name, " = ", var_name, ".", mbr_name, ";"); + } + } + else + { + auto name = to_name(var.self); + + if (legacy && execution.model == ExecutionModelFragment) + { + string output_filler; + for (uint32_t size = type.vecsize; size < 4; ++size) + output_filler += ", 0.0"; + + statement("stage_output.", name, " = float4(", name, output_filler, ");"); + } + else + { + statement("stage_output.", name, " = ", name, ";"); + } + } + } + }); + + statement("return stage_output;"); + } + + end_scope(); +} + +void CompilerHLSL::emit_fixup() +{ + if (is_vertex_like_shader() && active_output_builtins.get(BuiltInPosition)) + { + // Do various mangling on the gl_Position. + if (hlsl_options.shader_model <= 30) + { + statement("gl_Position.x = gl_Position.x - gl_HalfPixel.x * " + "gl_Position.w;"); + statement("gl_Position.y = gl_Position.y + gl_HalfPixel.y * " + "gl_Position.w;"); + } + + if (options.vertex.flip_vert_y) + statement("gl_Position.y = -gl_Position.y;"); + if (options.vertex.fixup_clipspace) + statement("gl_Position.z = (gl_Position.z + gl_Position.w) * 0.5;"); + } +} + +void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse) +{ + if (sparse) + SPIRV_CROSS_THROW("Sparse feedback not yet supported in HLSL."); + + auto *ops = stream(i); + auto op = static_cast(i.op); + uint32_t length = i.length; + + SmallVector inherited_expressions; + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + VariableID img = ops[2]; + uint32_t coord = ops[3]; + uint32_t dref = 0; + uint32_t comp = 0; + bool gather = false; + bool proj = false; + const uint32_t *opt = nullptr; + auto *combined_image = maybe_get(img); + + if (combined_image && has_decoration(img, DecorationNonUniform)) + { + set_decoration(combined_image->image, DecorationNonUniform); + set_decoration(combined_image->sampler, DecorationNonUniform); + } + + auto img_expr = to_non_uniform_aware_expression(combined_image ? combined_image->image : img); + + inherited_expressions.push_back(coord); + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleDrefExplicitLod: + dref = ops[4]; + opt = &ops[5]; + length -= 5; + break; + + case OpImageSampleProjDrefImplicitLod: + case OpImageSampleProjDrefExplicitLod: + dref = ops[4]; + proj = true; + opt = &ops[5]; + length -= 5; + break; + + case OpImageDrefGather: + dref = ops[4]; + opt = &ops[5]; + gather = true; + length -= 5; + break; + + case OpImageGather: + comp = ops[4]; + opt = &ops[5]; + gather = true; + length -= 5; + break; + + case OpImageSampleProjImplicitLod: + case OpImageSampleProjExplicitLod: + opt = &ops[4]; + length -= 4; + proj = true; + break; + + case OpImageQueryLod: + opt = &ops[4]; + length -= 4; + break; + + default: + opt = &ops[4]; + length -= 4; + break; + } + + auto &imgtype = expression_type(img); + uint32_t coord_components = 0; + switch (imgtype.image.dim) + { + case Dim1D: + coord_components = 1; + break; + case Dim2D: + coord_components = 2; + break; + case Dim3D: + coord_components = 3; + break; + case DimCube: + coord_components = 3; + break; + case DimBuffer: + coord_components = 1; + break; + default: + coord_components = 2; + break; + } + + if (dref) + inherited_expressions.push_back(dref); + + if (imgtype.image.arrayed && op != OpImageQueryLod) + coord_components++; + + uint32_t bias = 0; + uint32_t lod = 0; + uint32_t grad_x = 0; + uint32_t grad_y = 0; + uint32_t coffset = 0; + uint32_t offset = 0; + uint32_t coffsets = 0; + uint32_t sample = 0; + uint32_t minlod = 0; + uint32_t flags = 0; + + if (length) + { + flags = opt[0]; + opt++; + length--; + } + + auto test = [&](uint32_t &v, uint32_t flag) { + if (length && (flags & flag)) + { + v = *opt++; + inherited_expressions.push_back(v); + length--; + } + }; + + test(bias, ImageOperandsBiasMask); + test(lod, ImageOperandsLodMask); + test(grad_x, ImageOperandsGradMask); + test(grad_y, ImageOperandsGradMask); + test(coffset, ImageOperandsConstOffsetMask); + test(offset, ImageOperandsOffsetMask); + test(coffsets, ImageOperandsConstOffsetsMask); + test(sample, ImageOperandsSampleMask); + test(minlod, ImageOperandsMinLodMask); + + string expr; + string texop; + + if (minlod != 0) + SPIRV_CROSS_THROW("MinLod texture operand not supported in HLSL."); + + if (op == OpImageFetch) + { + if (hlsl_options.shader_model < 40) + { + SPIRV_CROSS_THROW("texelFetch is not supported in HLSL shader model 2/3."); + } + texop += img_expr; + texop += ".Load"; + } + else if (op == OpImageQueryLod) + { + texop += img_expr; + texop += ".CalculateLevelOfDetail"; + } + else + { + auto &imgformat = get(imgtype.image.type); + if (hlsl_options.shader_model < 67 && imgformat.basetype != SPIRType::Float && !gather) + { + SPIRV_CROSS_THROW("Sampling non-float textures is not supported in HLSL SM < 6.7."); + } + + if (hlsl_options.shader_model >= 40) + { + texop += img_expr; + + if (is_depth_image(imgtype, img)) + { + if (gather) + { + texop += ".GatherCmp"; + } + else if (lod || grad_x || grad_y) + { + // Assume we want a fixed level, and the only thing we can get in HLSL is SampleCmpLevelZero. + texop += ".SampleCmpLevelZero"; + } + else + texop += ".SampleCmp"; + } + else if (gather) + { + uint32_t comp_num = evaluate_constant_u32(comp); + if (hlsl_options.shader_model >= 50) + { + switch (comp_num) + { + case 0: + texop += ".GatherRed"; + break; + case 1: + texop += ".GatherGreen"; + break; + case 2: + texop += ".GatherBlue"; + break; + case 3: + texop += ".GatherAlpha"; + break; + default: + SPIRV_CROSS_THROW("Invalid component."); + } + } + else + { + if (comp_num == 0) + texop += ".Gather"; + else + SPIRV_CROSS_THROW("HLSL shader model 4 can only gather from the red component."); + } + } + else if (bias) + texop += ".SampleBias"; + else if (grad_x || grad_y) + texop += ".SampleGrad"; + else if (lod) + texop += ".SampleLevel"; + else + texop += ".Sample"; + } + else + { + switch (imgtype.image.dim) + { + case Dim1D: + texop += "tex1D"; + break; + case Dim2D: + texop += "tex2D"; + break; + case Dim3D: + texop += "tex3D"; + break; + case DimCube: + texop += "texCUBE"; + break; + case DimRect: + case DimBuffer: + case DimSubpassData: + SPIRV_CROSS_THROW("Buffer texture support is not yet implemented for HLSL"); // TODO + default: + SPIRV_CROSS_THROW("Invalid dimension."); + } + + if (gather) + SPIRV_CROSS_THROW("textureGather is not supported in HLSL shader model 2/3."); + if (offset || coffset) + SPIRV_CROSS_THROW("textureOffset is not supported in HLSL shader model 2/3."); + + if (grad_x || grad_y) + texop += "grad"; + else if (lod) + texop += "lod"; + else if (bias) + texop += "bias"; + else if (proj || dref) + texop += "proj"; + } + } + + expr += texop; + expr += "("; + if (hlsl_options.shader_model < 40) + { + if (combined_image) + SPIRV_CROSS_THROW("Separate images/samplers are not supported in HLSL shader model 2/3."); + expr += to_expression(img); + } + else if (op != OpImageFetch) + { + string sampler_expr; + if (combined_image) + sampler_expr = to_non_uniform_aware_expression(combined_image->sampler); + else + sampler_expr = to_sampler_expression(img); + expr += sampler_expr; + } + + auto swizzle = [](uint32_t comps, uint32_t in_comps) -> const char * { + if (comps == in_comps) + return ""; + + switch (comps) + { + case 1: + return ".x"; + case 2: + return ".xy"; + case 3: + return ".xyz"; + default: + return ""; + } + }; + + bool forward = should_forward(coord); + + // The IR can give us more components than we need, so chop them off as needed. + string coord_expr; + auto &coord_type = expression_type(coord); + if (coord_components != coord_type.vecsize) + coord_expr = to_enclosed_expression(coord) + swizzle(coord_components, expression_type(coord).vecsize); + else + coord_expr = to_expression(coord); + + if (proj && hlsl_options.shader_model >= 40) // Legacy HLSL has "proj" operations which do this for us. + coord_expr = coord_expr + " / " + to_extract_component_expression(coord, coord_components); + + if (hlsl_options.shader_model < 40) + { + if (dref) + { + if (imgtype.image.dim != Dim1D && imgtype.image.dim != Dim2D) + { + SPIRV_CROSS_THROW( + "Depth comparison is only supported for 1D and 2D textures in HLSL shader model 2/3."); + } + + if (grad_x || grad_y) + SPIRV_CROSS_THROW("Depth comparison is not supported for grad sampling in HLSL shader model 2/3."); + + for (uint32_t size = coord_components; size < 2; ++size) + coord_expr += ", 0.0"; + + forward = forward && should_forward(dref); + coord_expr += ", " + to_expression(dref); + } + else if (lod || bias || proj) + { + for (uint32_t size = coord_components; size < 3; ++size) + coord_expr += ", 0.0"; + } + + if (lod) + { + coord_expr = "float4(" + coord_expr + ", " + to_expression(lod) + ")"; + } + else if (bias) + { + coord_expr = "float4(" + coord_expr + ", " + to_expression(bias) + ")"; + } + else if (proj) + { + coord_expr = "float4(" + coord_expr + ", " + to_extract_component_expression(coord, coord_components) + ")"; + } + else if (dref) + { + // A "normal" sample gets fed into tex2Dproj as well, because the + // regular tex2D accepts only two coordinates. + coord_expr = "float4(" + coord_expr + ", 1.0)"; + } + + if (!!lod + !!bias + !!proj > 1) + SPIRV_CROSS_THROW("Legacy HLSL can only use one of lod/bias/proj modifiers."); + } + + if (op == OpImageFetch) + { + if (imgtype.image.dim != DimBuffer && !imgtype.image.ms) + coord_expr = + join("int", coord_components + 1, "(", coord_expr, ", ", lod ? to_expression(lod) : string("0"), ")"); + } + else + expr += ", "; + expr += coord_expr; + + if (dref && hlsl_options.shader_model >= 40) + { + forward = forward && should_forward(dref); + expr += ", "; + + if (proj) + expr += to_enclosed_expression(dref) + " / " + to_extract_component_expression(coord, coord_components); + else + expr += to_expression(dref); + } + + if (!dref && (grad_x || grad_y)) + { + forward = forward && should_forward(grad_x); + forward = forward && should_forward(grad_y); + expr += ", "; + expr += to_expression(grad_x); + expr += ", "; + expr += to_expression(grad_y); + } + + if (!dref && lod && hlsl_options.shader_model >= 40 && op != OpImageFetch) + { + forward = forward && should_forward(lod); + expr += ", "; + expr += to_expression(lod); + } + + if (!dref && bias && hlsl_options.shader_model >= 40) + { + forward = forward && should_forward(bias); + expr += ", "; + expr += to_expression(bias); + } + + if (coffset) + { + forward = forward && should_forward(coffset); + expr += ", "; + expr += to_expression(coffset); + } + else if (offset) + { + forward = forward && should_forward(offset); + expr += ", "; + expr += to_expression(offset); + } + + if (sample) + { + expr += ", "; + expr += to_expression(sample); + } + + expr += ")"; + + if (dref && hlsl_options.shader_model < 40) + expr += ".x"; + + if (op == OpImageQueryLod) + { + // This is rather awkward. + // textureQueryLod returns two values, the "accessed level", + // as well as the actual LOD lambda. + // As far as I can tell, there is no way to get the .x component + // according to GLSL spec, and it depends on the sampler itself. + // Just assume X == Y, so we will need to splat the result to a float2. + statement("float _", id, "_tmp = ", expr, ";"); + statement("float2 _", id, " = _", id, "_tmp.xx;"); + set(id, join("_", id), result_type, true); + } + else + { + emit_op(result_type, id, expr, forward, false); + } + + for (auto &inherit : inherited_expressions) + inherit_expression_dependencies(id, inherit); + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjDrefImplicitLod: + register_control_dependent_expression(id); + break; + + default: + break; + } +} + +string CompilerHLSL::to_resource_binding(const SPIRVariable &var) +{ + const auto &type = get(var.basetype); + + // We can remap push constant blocks, even if they don't have any binding decoration. + if (type.storage != StorageClassPushConstant && !has_decoration(var.self, DecorationBinding)) + return ""; + + char space = '\0'; + + HLSLBindingFlagBits resource_flags = HLSL_BINDING_AUTO_NONE_BIT; + + switch (type.basetype) + { + case SPIRType::SampledImage: + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + break; + + case SPIRType::Image: + if (type.image.sampled == 2 && type.image.dim != DimSubpassData) + { + if (has_decoration(var.self, DecorationNonWritable) && hlsl_options.nonwritable_uav_texture_as_srv) + { + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + } + else + { + space = 'u'; // UAV + resource_flags = HLSL_BINDING_AUTO_UAV_BIT; + } + } + else + { + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + } + break; + + case SPIRType::Sampler: + space = 's'; + resource_flags = HLSL_BINDING_AUTO_SAMPLER_BIT; + break; + + case SPIRType::AccelerationStructure: + space = 't'; // SRV + resource_flags = HLSL_BINDING_AUTO_SRV_BIT; + break; + + case SPIRType::Struct: + { + auto storage = type.storage; + if (storage == StorageClassUniform) + { + if (has_decoration(type.self, DecorationBufferBlock)) + { + Bitset flags = ir.get_buffer_block_flags(var); + bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self); + space = is_readonly ? 't' : 'u'; // UAV + resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT; + } + else if (has_decoration(type.self, DecorationBlock)) + { + space = 'b'; // Constant buffers + resource_flags = HLSL_BINDING_AUTO_CBV_BIT; + } + } + else if (storage == StorageClassPushConstant) + { + space = 'b'; // Constant buffers + resource_flags = HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT; + } + else if (storage == StorageClassStorageBuffer) + { + // UAV or SRV depending on readonly flag. + Bitset flags = ir.get_buffer_block_flags(var); + bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self); + space = is_readonly ? 't' : 'u'; + resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT; + } + + break; + } + default: + break; + } + + if (!space) + return ""; + + uint32_t desc_set = + resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantDescriptorSet : 0u; + uint32_t binding = resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantBinding : 0u; + + if (has_decoration(var.self, DecorationBinding)) + binding = get_decoration(var.self, DecorationBinding); + if (has_decoration(var.self, DecorationDescriptorSet)) + desc_set = get_decoration(var.self, DecorationDescriptorSet); + + return to_resource_register(resource_flags, space, binding, desc_set); +} + +string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) +{ + // For combined image samplers. + if (!has_decoration(var.self, DecorationBinding)) + return ""; + + return to_resource_register(HLSL_BINDING_AUTO_SAMPLER_BIT, 's', get_decoration(var.self, DecorationBinding), + get_decoration(var.self, DecorationDescriptorSet)); +} + +void CompilerHLSL::remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding) +{ + auto itr = resource_bindings.find({ get_execution_model(), desc_set, binding }); + if (itr != end(resource_bindings)) + { + auto &remap = itr->second; + remap.second = true; + + switch (type) + { + case HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT: + case HLSL_BINDING_AUTO_CBV_BIT: + desc_set = remap.first.cbv.register_space; + binding = remap.first.cbv.register_binding; + break; + + case HLSL_BINDING_AUTO_SRV_BIT: + desc_set = remap.first.srv.register_space; + binding = remap.first.srv.register_binding; + break; + + case HLSL_BINDING_AUTO_SAMPLER_BIT: + desc_set = remap.first.sampler.register_space; + binding = remap.first.sampler.register_binding; + break; + + case HLSL_BINDING_AUTO_UAV_BIT: + desc_set = remap.first.uav.register_space; + binding = remap.first.uav.register_binding; + break; + + default: + break; + } + } +} + +string CompilerHLSL::to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t space_set) +{ + if ((flag & resource_binding_flags) == 0) + { + remap_hlsl_resource_binding(flag, space_set, binding); + + // The push constant block did not have a binding, and there were no remap for it, + // so, declare without register binding. + if (flag == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT && space_set == ResourceBindingPushConstantDescriptorSet) + return ""; + + if (hlsl_options.shader_model >= 51) + return join(" : register(", space, binding, ", space", space_set, ")"); + else + return join(" : register(", space, binding, ")"); + } + else + return ""; +} + +void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + switch (type.basetype) + { + case SPIRType::SampledImage: + case SPIRType::Image: + { + 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, var.self), to_resource_binding(var), ";"); + + if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer) + { + // For combined image samplers, also emit a combined image sampler. + if (is_depth_image(type, var.self)) + statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type, var.self), + to_resource_binding_sampler(var), ";"); + else + statement("SamplerState ", to_sampler_expression(var.self), type_to_array_glsl(type, var.self), + to_resource_binding_sampler(var), ";"); + } + break; + } + + case SPIRType::Sampler: + if (comparison_ids.count(var.self)) + statement("SamplerComparisonState ", to_name(var.self), type_to_array_glsl(type, var.self), to_resource_binding(var), + ";"); + else + statement("SamplerState ", to_name(var.self), type_to_array_glsl(type, var.self), to_resource_binding(var), ";"); + break; + + default: + statement(variable_decl(var), to_resource_binding(var), ";"); + break; + } +} + +void CompilerHLSL::emit_legacy_uniform(const SPIRVariable &var) +{ + auto &type = get(var.basetype); + switch (type.basetype) + { + case SPIRType::Sampler: + case SPIRType::Image: + SPIRV_CROSS_THROW("Separate image and samplers not supported in legacy HLSL."); + + default: + statement(variable_decl(var), ";"); + break; + } +} + +void CompilerHLSL::emit_uniform(const SPIRVariable &var) +{ + add_resource_name(var.self); + if (hlsl_options.shader_model >= 40) + emit_modern_uniform(var); + else + emit_legacy_uniform(var); +} + +bool CompilerHLSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) +{ + return false; +} + +void CompilerHLSL::append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist) +{ + CompilerGLSL::append_global_func_args(func, index, arglist); + + if (func.emits_geometry) + arglist.push_back("geometry_stream"); +} + +string CompilerHLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) +{ + if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Int) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Int64) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Float) + return "asuint"; + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::UInt) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::UInt64) + return type_to_glsl(out_type); + else if (out_type.basetype == SPIRType::Int && in_type.basetype == SPIRType::Float) + return "asint"; + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::UInt) + return "asfloat"; + else if (out_type.basetype == SPIRType::Float && in_type.basetype == SPIRType::Int) + return "asfloat"; + else if (out_type.basetype == SPIRType::Int64 && in_type.basetype == SPIRType::Double) + SPIRV_CROSS_THROW("Double to Int64 is not supported in HLSL."); + else if (out_type.basetype == SPIRType::UInt64 && in_type.basetype == SPIRType::Double) + SPIRV_CROSS_THROW("Double to UInt64 is not supported in HLSL."); + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::Int64) + return "asdouble"; + else if (out_type.basetype == SPIRType::Double && in_type.basetype == SPIRType::UInt64) + return "asdouble"; + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UInt && in_type.vecsize == 1) + { + if (!requires_explicit_fp16_packing) + { + requires_explicit_fp16_packing = true; + force_recompile(); + } + return "spvUnpackFloat2x16"; + } + else if (out_type.basetype == SPIRType::UInt && in_type.basetype == SPIRType::Half && in_type.vecsize == 2) + { + if (!requires_explicit_fp16_packing) + { + requires_explicit_fp16_packing = true; + force_recompile(); + } + return "spvPackFloat2x16"; + } + else if (out_type.basetype == SPIRType::UShort && in_type.basetype == SPIRType::Half) + { + if (hlsl_options.shader_model < 40) + SPIRV_CROSS_THROW("Half to UShort requires Shader Model 4."); + return "(" + type_to_glsl(out_type) + ")f32tof16"; + } + else if (out_type.basetype == SPIRType::Half && in_type.basetype == SPIRType::UShort) + { + if (hlsl_options.shader_model < 40) + SPIRV_CROSS_THROW("UShort to Half requires Shader Model 4."); + return "(" + type_to_glsl(out_type) + ")f16tof32"; + } + else + return ""; +} + +void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count) +{ + auto op = static_cast(eop); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, count); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + op = get_remapped_glsl_op(op); + + switch (op) + { + case GLSLstd450InverseSqrt: + emit_unary_func_op(result_type, id, args[0], "rsqrt"); + break; + + case GLSLstd450Fract: + emit_unary_func_op(result_type, id, args[0], "frac"); + break; + + case GLSLstd450RoundEven: + if (hlsl_options.shader_model < 40) + SPIRV_CROSS_THROW("roundEven is not supported in HLSL shader model 2/3."); + emit_unary_func_op(result_type, id, args[0], "round"); + break; + + case GLSLstd450Trunc: + emit_unary_func_op(result_type, id, args[0], "trunc"); + break; + + case GLSLstd450Acosh: + case GLSLstd450Asinh: + case GLSLstd450Atanh: + // These are not supported in HLSL, always emulate them. + emit_emulated_ahyper_op(result_type, id, args[0], op); + break; + + case GLSLstd450FMix: + case GLSLstd450IMix: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "lerp"); + break; + + case GLSLstd450Atan2: + emit_binary_func_op(result_type, id, args[0], args[1], "atan2"); + break; + + case GLSLstd450Fma: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "mad"); + break; + + case GLSLstd450InterpolateAtCentroid: + emit_unary_func_op(result_type, id, args[0], "EvaluateAttributeAtCentroid"); + break; + case GLSLstd450InterpolateAtSample: + emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeAtSample"); + break; + case GLSLstd450InterpolateAtOffset: + emit_binary_func_op(result_type, id, args[0], args[1], "EvaluateAttributeSnapped"); + break; + + case GLSLstd450PackHalf2x16: + if (!requires_fp16_packing) + { + requires_fp16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvPackHalf2x16"); + break; + + case GLSLstd450UnpackHalf2x16: + if (!requires_fp16_packing) + { + requires_fp16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvUnpackHalf2x16"); + break; + + case GLSLstd450PackSnorm4x8: + if (!requires_snorm8_packing) + { + requires_snorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvPackSnorm4x8"); + break; + + case GLSLstd450UnpackSnorm4x8: + if (!requires_snorm8_packing) + { + requires_snorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvUnpackSnorm4x8"); + break; + + case GLSLstd450PackUnorm4x8: + if (!requires_unorm8_packing) + { + requires_unorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvPackUnorm4x8"); + break; + + case GLSLstd450UnpackUnorm4x8: + if (!requires_unorm8_packing) + { + requires_unorm8_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvUnpackUnorm4x8"); + break; + + case GLSLstd450PackSnorm2x16: + if (!requires_snorm16_packing) + { + requires_snorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvPackSnorm2x16"); + break; + + case GLSLstd450UnpackSnorm2x16: + if (!requires_snorm16_packing) + { + requires_snorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvUnpackSnorm2x16"); + break; + + case GLSLstd450PackUnorm2x16: + if (!requires_unorm16_packing) + { + requires_unorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvPackUnorm2x16"); + break; + + case GLSLstd450UnpackUnorm2x16: + if (!requires_unorm16_packing) + { + requires_unorm16_packing = true; + force_recompile(); + } + emit_unary_func_op(result_type, id, args[0], "spvUnpackUnorm2x16"); + break; + + case GLSLstd450PackDouble2x32: + case GLSLstd450UnpackDouble2x32: + SPIRV_CROSS_THROW("packDouble2x32/unpackDouble2x32 not supported in HLSL."); + + case GLSLstd450FindILsb: + { + auto basetype = expression_type(args[0]).basetype; + emit_unary_func_op_cast(result_type, id, args[0], "firstbitlow", basetype, basetype); + break; + } + + case GLSLstd450FindSMsb: + emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", int_type, int_type); + break; + + case GLSLstd450FindUMsb: + emit_unary_func_op_cast(result_type, id, args[0], "firstbithigh", uint_type, uint_type); + break; + + case GLSLstd450MatrixInverse: + { + auto &type = get(result_type); + if (type.vecsize == 2 && type.columns == 2) + { + if (!requires_inverse_2x2) + { + requires_inverse_2x2 = true; + force_recompile(); + } + } + else if (type.vecsize == 3 && type.columns == 3) + { + if (!requires_inverse_3x3) + { + requires_inverse_3x3 = true; + force_recompile(); + } + } + else if (type.vecsize == 4 && type.columns == 4) + { + if (!requires_inverse_4x4) + { + requires_inverse_4x4 = true; + force_recompile(); + } + } + emit_unary_func_op(result_type, id, args[0], "spvInverse"); + break; + } + + case GLSLstd450Normalize: + // HLSL does not support scalar versions here. + if (expression_type(args[0]).vecsize == 1) + { + // Returns -1 or 1 for valid input, sign() does the job. + emit_unary_func_op(result_type, id, args[0], "sign"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Reflect: + if (get(result_type).vecsize == 1) + { + if (!requires_scalar_reflect) + { + requires_scalar_reflect = true; + force_recompile(); + } + emit_binary_func_op(result_type, id, args[0], args[1], "spvReflect"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Refract: + if (get(result_type).vecsize == 1) + { + if (!requires_scalar_refract) + { + requires_scalar_refract = true; + force_recompile(); + } + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvRefract"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450FaceForward: + if (get(result_type).vecsize == 1) + { + if (!requires_scalar_faceforward) + { + requires_scalar_faceforward = true; + force_recompile(); + } + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvFaceForward"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450NMin: + CompilerGLSL::emit_glsl_op(result_type, id, GLSLstd450FMin, args, count); + break; + + case GLSLstd450NMax: + CompilerGLSL::emit_glsl_op(result_type, id, GLSLstd450FMax, args, count); + break; + + case GLSLstd450NClamp: + CompilerGLSL::emit_glsl_op(result_type, id, GLSLstd450FClamp, args, count); + break; + + default: + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + } +} + +void CompilerHLSL::read_access_chain_array(const string &lhs, const SPIRAccessChain &chain) +{ + auto &type = get(chain.basetype); + + // Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops. + auto ident = get_unique_identifier(); + + statement("[unroll]"); + statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ", + ident, "++)"); + begin_scope(); + auto subchain = chain; + subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index); + subchain.basetype = type.parent_type; + if (!get(subchain.basetype).array.empty()) + subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride); + read_access_chain(nullptr, join(lhs, "[", ident, "]"), subchain); + end_scope(); +} + +void CompilerHLSL::read_access_chain_struct(const string &lhs, const SPIRAccessChain &chain) +{ + auto &type = get(chain.basetype); + auto subchain = chain; + uint32_t member_count = uint32_t(type.member_types.size()); + + for (uint32_t i = 0; i < member_count; i++) + { + uint32_t offset = type_struct_member_offset(type, i); + subchain.static_index = chain.static_index + offset; + subchain.basetype = type.member_types[i]; + + subchain.matrix_stride = 0; + subchain.array_stride = 0; + subchain.row_major_matrix = false; + + auto &member_type = get(subchain.basetype); + if (member_type.columns > 1) + { + subchain.matrix_stride = type_struct_member_matrix_stride(type, i); + subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor); + } + + if (!member_type.array.empty()) + subchain.array_stride = type_struct_member_array_stride(type, i); + + read_access_chain(nullptr, join(lhs, ".", to_member_name(type, i)), subchain); + } +} + +void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIRAccessChain &chain) +{ + auto &type = get(chain.basetype); + + SPIRType target_type { is_scalar(type) ? OpTypeInt : type.op }; + target_type.basetype = SPIRType::UInt; + target_type.vecsize = type.vecsize; + target_type.columns = type.columns; + + if (!type.array.empty()) + { + read_access_chain_array(lhs, chain); + return; + } + else if (type.basetype == SPIRType::Struct) + { + read_access_chain_struct(lhs, chain); + return; + } + else if (type.width != 32 && !hlsl_options.enable_16bit_types) + SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported, unless SM 6.2 and " + "native 16-bit types are enabled."); + + string base = chain.base; + if (has_decoration(chain.self, DecorationNonUniform)) + convert_non_uniform_expression(base, chain.self); + + bool templated_load = hlsl_options.shader_model >= 62; + string load_expr; + + string template_expr; + if (templated_load) + template_expr = join("<", type_to_glsl(type), ">"); + + // Load a vector or scalar. + if (type.columns == 1 && !chain.row_major_matrix) + { + const char *load_op = nullptr; + switch (type.vecsize) + { + case 1: + load_op = "Load"; + break; + case 2: + load_op = "Load2"; + break; + case 3: + load_op = "Load3"; + break; + case 4: + load_op = "Load4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + if (templated_load) + load_op = "Load"; + + load_expr = join(base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")"); + } + else if (type.columns == 1) + { + // Strided load since we are loading a column from a row-major matrix. + if (templated_load) + { + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + if (type.vecsize > 1) + load_expr += type_to_glsl(type) + "("; + } + else if (type.vecsize > 1) + { + load_expr = type_to_glsl(target_type); + load_expr += "("; + } + + for (uint32_t r = 0; r < type.vecsize; r++) + { + load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index, + chain.static_index + r * chain.matrix_stride, ")"); + if (r + 1 < type.vecsize) + load_expr += ", "; + } + + if (type.vecsize > 1) + load_expr += ")"; + } + else if (!chain.row_major_matrix) + { + // Load a matrix, column-major, the easy case. + const char *load_op = nullptr; + switch (type.vecsize) + { + case 1: + load_op = "Load"; + break; + case 2: + load_op = "Load2"; + break; + case 3: + load_op = "Load3"; + break; + case 4: + load_op = "Load4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + if (templated_load) + { + auto vector_type = type; + vector_type.columns = 1; + template_expr = join("<", type_to_glsl(vector_type), ">"); + load_expr = type_to_glsl(type); + load_op = "Load"; + } + else + { + // Note, this loading style in HLSL is *actually* row-major, but we always treat matrices as transposed in this backend, + // so row-major is technically column-major ... + load_expr = type_to_glsl(target_type); + } + load_expr += "("; + + for (uint32_t c = 0; c < type.columns; c++) + { + load_expr += join(base, ".", load_op, template_expr, "(", chain.dynamic_index, + chain.static_index + c * chain.matrix_stride, ")"); + if (c + 1 < type.columns) + load_expr += ", "; + } + load_expr += ")"; + } + else + { + // Pick out elements one by one ... Hopefully compilers are smart enough to recognize this pattern + // considering HLSL is "row-major decl", but "column-major" memory layout (basically implicit transpose model, ugh) ... + + if (templated_load) + { + load_expr = type_to_glsl(type); + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + } + else + load_expr = type_to_glsl(target_type); + + load_expr += "("; + + for (uint32_t c = 0; c < type.columns; c++) + { + for (uint32_t r = 0; r < type.vecsize; r++) + { + load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index, + chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")"); + + if ((r + 1 < type.vecsize) || (c + 1 < type.columns)) + load_expr += ", "; + } + } + load_expr += ")"; + } + + if (!templated_load) + { + auto bitcast_op = bitcast_glsl_op(type, target_type); + if (!bitcast_op.empty()) + load_expr = join(bitcast_op, "(", load_expr, ")"); + } + + if (lhs.empty()) + { + assert(expr); + *expr = std::move(load_expr); + } + else + statement(lhs, " = ", load_expr, ";"); +} + +void CompilerHLSL::emit_load(const Instruction &instruction) +{ + auto ops = stream(instruction); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + + auto *chain = maybe_get(ptr); + if (chain) + { + auto &type = get(result_type); + bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct; + + if (composite_load) + { + // We cannot make this work in one single expression as we might have nested structures and arrays, + // so unroll the load to an uninitialized temporary. + emit_uninitialized_temporary_expression(result_type, id); + read_access_chain(nullptr, to_expression(id), *chain); + track_expression_read(chain->self); + } + else + { + string load_expr; + read_access_chain(&load_expr, "", *chain); + + bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries); + + // If we are forwarding this load, + // don't register the read to access chain here, defer that to when we actually use the expression, + // using the add_implied_read_expression mechanism. + if (!forward) + track_expression_read(chain->self); + + // Do not forward complex load sequences like matrices, structs and arrays. + if (type.columns > 1) + forward = false; + + auto &e = emit_op(result_type, id, load_expr, forward, true); + e.need_transpose = false; + register_read(id, ptr, forward); + inherit_expression_dependencies(id, ptr); + if (forward) + add_implied_read_expression(e, chain->self); + } + } + else + { + // Very special case where we cannot rely on IO lowering. + // Mesh shader clip/cull arrays ... Cursed. + auto &res_type = get(result_type); + if (get_execution_model() == ExecutionModelMeshEXT && + has_decoration(ptr, DecorationBuiltIn) && + (get_decoration(ptr, DecorationBuiltIn) == BuiltInClipDistance || + get_decoration(ptr, DecorationBuiltIn) == BuiltInCullDistance) && + is_array(res_type) && !is_array(get(res_type.parent_type)) && + to_array_size_literal(res_type) > 1) + { + track_expression_read(ptr); + string load_expr = "{ "; + uint32_t num_elements = to_array_size_literal(res_type); + for (uint32_t i = 0; i < num_elements; i++) + { + load_expr += join(to_expression(ptr), ".", index_to_swizzle(i)); + if (i + 1 < num_elements) + load_expr += ", "; + } + load_expr += " }"; + emit_op(result_type, id, load_expr, false); + register_read(id, ptr, false); + inherit_expression_dependencies(id, ptr); + } + else + { + CompilerGLSL::emit_instruction(instruction); + } + } +} + +void CompilerHLSL::write_access_chain_array(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain) +{ + auto *ptype = &get(chain.basetype); + while (ptype->pointer) + { + ptype = &get(ptype->basetype); + } + auto &type = *ptype; + + // Need to use a reserved identifier here since it might shadow an identifier in the access chain input or other loops. + auto ident = get_unique_identifier(); + + uint32_t id = ir.increase_bound_by(2); + uint32_t int_type_id = id + 1; + SPIRType int_type { OpTypeInt }; + int_type.basetype = SPIRType::Int; + int_type.width = 32; + set(int_type_id, int_type); + set(id, ident, int_type_id, true); + set_name(id, ident); + suppressed_usage_tracking.insert(id); + + statement("[unroll]"); + statement("for (int ", ident, " = 0; ", ident, " < ", to_array_size(type, uint32_t(type.array.size() - 1)), "; ", + ident, "++)"); + begin_scope(); + auto subchain = chain; + subchain.dynamic_index = join(ident, " * ", chain.array_stride, " + ", chain.dynamic_index); + subchain.basetype = type.parent_type; + + // Forcefully allow us to use an ID here by setting MSB. + auto subcomposite_chain = composite_chain; + subcomposite_chain.push_back(0x80000000u | id); + + if (!get(subchain.basetype).array.empty()) + subchain.array_stride = get_decoration(subchain.basetype, DecorationArrayStride); + + write_access_chain(subchain, value, subcomposite_chain); + end_scope(); +} + +void CompilerHLSL::write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain) +{ + auto &type = get(chain.basetype); + uint32_t member_count = uint32_t(type.member_types.size()); + auto subchain = chain; + + auto subcomposite_chain = composite_chain; + subcomposite_chain.push_back(0); + + for (uint32_t i = 0; i < member_count; i++) + { + uint32_t offset = type_struct_member_offset(type, i); + subchain.static_index = chain.static_index + offset; + subchain.basetype = type.member_types[i]; + + subchain.matrix_stride = 0; + subchain.array_stride = 0; + subchain.row_major_matrix = false; + + auto &member_type = get(subchain.basetype); + if (member_type.columns > 1) + { + subchain.matrix_stride = type_struct_member_matrix_stride(type, i); + subchain.row_major_matrix = has_member_decoration(type.self, i, DecorationRowMajor); + } + + if (!member_type.array.empty()) + subchain.array_stride = type_struct_member_array_stride(type, i); + + subcomposite_chain.back() = i; + write_access_chain(subchain, value, subcomposite_chain); + } +} + +string CompilerHLSL::write_access_chain_value(uint32_t value, const SmallVector &composite_chain, + bool enclose) +{ + string ret; + if (composite_chain.empty()) + ret = to_expression(value); + else + { + AccessChainMeta meta; + ret = access_chain_internal(value, composite_chain.data(), uint32_t(composite_chain.size()), + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_LITERAL_MSB_FORCE_ID, &meta, + nullptr); + } + + if (enclose) + ret = enclose_expression(ret); + return ret; +} + +void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain) +{ + auto &type = get(chain.basetype); + + // Make sure we trigger a read of the constituents in the access chain. + track_expression_read(chain.self); + + SPIRType target_type { is_scalar(type) ? OpTypeInt : type.op }; + target_type.basetype = SPIRType::UInt; + target_type.vecsize = type.vecsize; + target_type.columns = type.columns; + + if (!type.array.empty()) + { + write_access_chain_array(chain, value, composite_chain); + register_write(chain.self); + return; + } + else if (type.basetype == SPIRType::Struct) + { + write_access_chain_struct(chain, value, composite_chain); + register_write(chain.self); + return; + } + else if (type.width != 32 && !hlsl_options.enable_16bit_types) + SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported, unless SM 6.2 and " + "native 16-bit types are enabled."); + + bool templated_store = hlsl_options.shader_model >= 62; + + auto base = chain.base; + if (has_decoration(chain.self, DecorationNonUniform)) + convert_non_uniform_expression(base, chain.self); + + string template_expr; + if (templated_store) + template_expr = join("<", type_to_glsl(type), ">"); + + if (type.columns == 1 && !chain.row_major_matrix) + { + const char *store_op = nullptr; + switch (type.vecsize) + { + case 1: + store_op = "Store"; + break; + case 2: + store_op = "Store2"; + break; + case 3: + store_op = "Store3"; + break; + case 4: + store_op = "Store4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + auto store_expr = write_access_chain_value(value, composite_chain, false); + + if (!templated_store) + { + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + } + else + store_op = "Store"; + statement(base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ", + store_expr, ");"); + } + else if (type.columns == 1) + { + if (templated_store) + { + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + } + + // Strided store. + for (uint32_t r = 0; r < type.vecsize; r++) + { + auto store_expr = write_access_chain_value(value, composite_chain, true); + if (type.vecsize > 1) + { + store_expr += "."; + store_expr += index_to_swizzle(r); + } + remove_duplicate_swizzle(store_expr); + + if (!templated_store) + { + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + } + + statement(base, ".Store", template_expr, "(", chain.dynamic_index, + chain.static_index + chain.matrix_stride * r, ", ", store_expr, ");"); + } + } + else if (!chain.row_major_matrix) + { + const char *store_op = nullptr; + switch (type.vecsize) + { + case 1: + store_op = "Store"; + break; + case 2: + store_op = "Store2"; + break; + case 3: + store_op = "Store3"; + break; + case 4: + store_op = "Store4"; + break; + default: + SPIRV_CROSS_THROW("Unknown vector size."); + } + + if (templated_store) + { + store_op = "Store"; + auto vector_type = type; + vector_type.columns = 1; + template_expr = join("<", type_to_glsl(vector_type), ">"); + } + + for (uint32_t c = 0; c < type.columns; c++) + { + auto store_expr = join(write_access_chain_value(value, composite_chain, true), "[", c, "]"); + + if (!templated_store) + { + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + } + + statement(base, ".", store_op, template_expr, "(", chain.dynamic_index, + chain.static_index + c * chain.matrix_stride, ", ", store_expr, ");"); + } + } + else + { + if (templated_store) + { + auto scalar_type = type; + scalar_type.vecsize = 1; + scalar_type.columns = 1; + template_expr = join("<", type_to_glsl(scalar_type), ">"); + } + + for (uint32_t r = 0; r < type.vecsize; r++) + { + for (uint32_t c = 0; c < type.columns; c++) + { + auto store_expr = + join(write_access_chain_value(value, composite_chain, true), "[", c, "].", index_to_swizzle(r)); + remove_duplicate_swizzle(store_expr); + auto bitcast_op = bitcast_glsl_op(target_type, type); + if (!bitcast_op.empty()) + store_expr = join(bitcast_op, "(", store_expr, ")"); + statement(base, ".Store", template_expr, "(", chain.dynamic_index, + chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");"); + } + } + } + + register_write(chain.self); +} + +void CompilerHLSL::emit_store(const Instruction &instruction) +{ + auto ops = stream(instruction); + if (options.vertex.flip_vert_y) + { + auto *expr = maybe_get(ops[0]); + if (expr != nullptr && expr->access_meshlet_position_y) + { + auto lhs = to_dereferenced_expression(ops[0]); + auto rhs = to_unpacked_expression(ops[1]); + statement(lhs, " = spvFlipVertY(", rhs, ");"); + register_write(ops[0]); + return; + } + } + + auto *chain = maybe_get(ops[0]); + if (chain) + write_access_chain(*chain, ops[1], {}); + else + CompilerGLSL::emit_instruction(instruction); +} + +void CompilerHLSL::emit_access_chain(const Instruction &instruction) +{ + auto ops = stream(instruction); + uint32_t length = instruction.length; + + bool need_byte_access_chain = false; + auto &type = expression_type(ops[2]); + const auto *chain = maybe_get(ops[2]); + + if (chain) + { + // Keep tacking on an existing access chain. + need_byte_access_chain = true; + } + else if (type.storage == StorageClassStorageBuffer || has_decoration(type.self, DecorationBufferBlock)) + { + // If we are starting to poke into an SSBO, we are dealing with ByteAddressBuffers, and we need + // to emit SPIRAccessChain rather than a plain SPIRExpression. + uint32_t chain_arguments = length - 3; + if (chain_arguments > type.array.size()) + need_byte_access_chain = true; + } + + if (need_byte_access_chain) + { + // If we have a chain variable, we are already inside the SSBO, and any array type will refer to arrays within a block, + // and not array of SSBO. + uint32_t to_plain_buffer_length = chain ? 0u : static_cast(type.array.size()); + + auto *backing_variable = maybe_get_backing_variable(ops[2]); + + if (backing_variable != nullptr && is_user_type_structured(backing_variable->self)) + { + CompilerGLSL::emit_instruction(instruction); + return; + } + + string base; + if (to_plain_buffer_length != 0) + base = access_chain(ops[2], &ops[3], to_plain_buffer_length, get(ops[0])); + else if (chain) + base = chain->base; + else + base = to_expression(ops[2]); + + // Start traversing type hierarchy at the proper non-pointer types. + auto *basetype = &get_pointee_type(type); + + // Traverse the type hierarchy down to the actual buffer types. + for (uint32_t i = 0; i < to_plain_buffer_length; i++) + { + assert(basetype->parent_type); + basetype = &get(basetype->parent_type); + } + + uint32_t matrix_stride = 0; + uint32_t array_stride = 0; + bool row_major_matrix = false; + + // Inherit matrix information. + if (chain) + { + matrix_stride = chain->matrix_stride; + row_major_matrix = chain->row_major_matrix; + array_stride = chain->array_stride; + } + + auto offsets = flattened_access_chain_offset(*basetype, &ops[3 + to_plain_buffer_length], + length - 3 - to_plain_buffer_length, 0, 1, &row_major_matrix, + &matrix_stride, &array_stride); + + auto &e = set(ops[1], ops[0], type.storage, base, offsets.first, offsets.second); + e.row_major_matrix = row_major_matrix; + e.matrix_stride = matrix_stride; + e.array_stride = array_stride; + e.immutable = should_forward(ops[2]); + e.loaded_from = backing_variable ? backing_variable->self : ID(0); + + if (chain) + { + e.dynamic_index += chain->dynamic_index; + e.static_index += chain->static_index; + } + + for (uint32_t i = 2; i < length; i++) + { + inherit_expression_dependencies(ops[1], ops[i]); + add_implied_read_expression(e, ops[i]); + } + } + else + { + CompilerGLSL::emit_instruction(instruction); + } +} + +void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, Op op) +{ + const char *atomic_op = nullptr; + + string value_expr; + if (op != OpAtomicIDecrement && op != OpAtomicIIncrement && op != OpAtomicLoad && op != OpAtomicStore) + value_expr = to_expression(ops[op == OpAtomicCompareExchange ? 6 : 5]); + + bool is_atomic_store = false; + + switch (op) + { + case OpAtomicIIncrement: + atomic_op = "InterlockedAdd"; + value_expr = "1"; + break; + + case OpAtomicIDecrement: + atomic_op = "InterlockedAdd"; + value_expr = "-1"; + break; + + case OpAtomicLoad: + atomic_op = "InterlockedAdd"; + value_expr = "0"; + break; + + case OpAtomicISub: + atomic_op = "InterlockedAdd"; + value_expr = join("-", enclose_expression(value_expr)); + break; + + case OpAtomicSMin: + case OpAtomicUMin: + atomic_op = "InterlockedMin"; + break; + + case OpAtomicSMax: + case OpAtomicUMax: + atomic_op = "InterlockedMax"; + break; + + case OpAtomicAnd: + atomic_op = "InterlockedAnd"; + break; + + case OpAtomicOr: + atomic_op = "InterlockedOr"; + break; + + case OpAtomicXor: + atomic_op = "InterlockedXor"; + break; + + case OpAtomicIAdd: + atomic_op = "InterlockedAdd"; + break; + + case OpAtomicExchange: + atomic_op = "InterlockedExchange"; + break; + + case OpAtomicStore: + atomic_op = "InterlockedExchange"; + is_atomic_store = true; + break; + + case OpAtomicCompareExchange: + if (length < 8) + SPIRV_CROSS_THROW("Not enough data for opcode."); + atomic_op = "InterlockedCompareExchange"; + value_expr = join(to_expression(ops[7]), ", ", value_expr); + break; + + default: + SPIRV_CROSS_THROW("Unknown atomic opcode."); + } + + if (is_atomic_store) + { + auto &data_type = expression_type(ops[0]); + auto *chain = maybe_get(ops[0]); + + auto &tmp_id = extra_sub_expressions[ops[0]]; + if (!tmp_id) + { + tmp_id = ir.increase_bound_by(1); + emit_uninitialized_temporary_expression(get_pointee_type(data_type).self, tmp_id); + } + + if (data_type.storage == StorageClassImage || !chain) + { + statement(atomic_op, "(", to_non_uniform_aware_expression(ops[0]), ", ", + to_expression(ops[3]), ", ", to_expression(tmp_id), ");"); + } + else + { + string base = chain->base; + if (has_decoration(chain->self, DecorationNonUniform)) + convert_non_uniform_expression(base, chain->self); + // RWByteAddress buffer is always uint in its underlying type. + statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", + to_expression(ops[3]), ", ", to_expression(tmp_id), ");"); + } + } + else + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + forced_temporaries.insert(ops[1]); + + auto &type = get(result_type); + statement(variable_decl(type, to_name(id)), ";"); + + auto &data_type = expression_type(ops[2]); + auto *chain = maybe_get(ops[2]); + SPIRType::BaseType expr_type; + if (data_type.storage == StorageClassImage || !chain) + { + statement(atomic_op, "(", to_non_uniform_aware_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");"); + expr_type = data_type.basetype; + } + else + { + // RWByteAddress buffer is always uint in its underlying type. + string base = chain->base; + if (has_decoration(chain->self, DecorationNonUniform)) + convert_non_uniform_expression(base, chain->self); + expr_type = SPIRType::UInt; + statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr, + ", ", to_name(id), ");"); + } + + auto expr = bitcast_expression(type, expr_type, to_name(id)); + set(id, expr, result_type, true); + } + flush_all_atomic_capable_variables(); +} + +void CompilerHLSL::emit_subgroup_op(const Instruction &i) +{ + if (hlsl_options.shader_model < 60) + SPIRV_CROSS_THROW("Wave ops requires SM 6.0 or higher."); + + const uint32_t *ops = stream(i); + auto op = static_cast(i.op); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto scope = static_cast(evaluate_constant_u32(ops[2])); + if (scope != ScopeSubgroup) + SPIRV_CROSS_THROW("Only subgroup scope is supported."); + + const auto make_inclusive_Sum = [&](const string &expr) -> string { + return join(expr, " + ", to_expression(ops[4])); + }; + + const auto make_inclusive_Product = [&](const string &expr) -> string { + return join(expr, " * ", to_expression(ops[4])); + }; + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + +#define make_inclusive_BitAnd(expr) "" +#define make_inclusive_BitOr(expr) "" +#define make_inclusive_BitXor(expr) "" +#define make_inclusive_Min(expr) "" +#define make_inclusive_Max(expr) "" + + switch (op) + { + case OpGroupNonUniformElect: + emit_op(result_type, id, "WaveIsFirstLane()", true); + break; + + case OpGroupNonUniformBroadcast: + emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt"); + break; + + case OpGroupNonUniformBroadcastFirst: + emit_unary_func_op(result_type, id, ops[3], "WaveReadLaneFirst"); + break; + + case OpGroupNonUniformBallot: + emit_unary_func_op(result_type, id, ops[3], "WaveActiveBallot"); + break; + + case OpGroupNonUniformInverseBallot: + SPIRV_CROSS_THROW("Cannot trivially implement InverseBallot in HLSL."); + + case OpGroupNonUniformBallotBitExtract: + SPIRV_CROSS_THROW("Cannot trivially implement BallotBitExtract in HLSL."); + + case OpGroupNonUniformBallotFindLSB: + SPIRV_CROSS_THROW("Cannot trivially implement BallotFindLSB in HLSL."); + + case OpGroupNonUniformBallotFindMSB: + SPIRV_CROSS_THROW("Cannot trivially implement BallotFindMSB in HLSL."); + + case OpGroupNonUniformBallotBitCount: + { + auto operation = static_cast(ops[3]); + bool forward = should_forward(ops[4]); + if (operation == GroupOperationReduce) + { + auto left = join("countbits(", to_enclosed_expression(ops[4]), ".x) + countbits(", + to_enclosed_expression(ops[4]), ".y)"); + auto right = join("countbits(", to_enclosed_expression(ops[4]), ".z) + countbits(", + to_enclosed_expression(ops[4]), ".w)"); + emit_op(result_type, id, join(left, " + ", right), forward); + inherit_expression_dependencies(id, ops[4]); + } + else if (operation == GroupOperationInclusiveScan) + { + auto left = join("countbits(", to_enclosed_expression(ops[4]), ".x & gl_SubgroupLeMask.x) + countbits(", + to_enclosed_expression(ops[4]), ".y & gl_SubgroupLeMask.y)"); + auto right = join("countbits(", to_enclosed_expression(ops[4]), ".z & gl_SubgroupLeMask.z) + countbits(", + to_enclosed_expression(ops[4]), ".w & gl_SubgroupLeMask.w)"); + emit_op(result_type, id, join(left, " + ", right), forward); + if (!active_input_builtins.get(BuiltInSubgroupLeMask)) + { + active_input_builtins.set(BuiltInSubgroupLeMask); + force_recompile_guarantee_forward_progress(); + } + } + else if (operation == GroupOperationExclusiveScan) + { + auto left = join("countbits(", to_enclosed_expression(ops[4]), ".x & gl_SubgroupLtMask.x) + countbits(", + to_enclosed_expression(ops[4]), ".y & gl_SubgroupLtMask.y)"); + auto right = join("countbits(", to_enclosed_expression(ops[4]), ".z & gl_SubgroupLtMask.z) + countbits(", + to_enclosed_expression(ops[4]), ".w & gl_SubgroupLtMask.w)"); + emit_op(result_type, id, join(left, " + ", right), forward); + if (!active_input_builtins.get(BuiltInSubgroupLtMask)) + { + active_input_builtins.set(BuiltInSubgroupLtMask); + force_recompile_guarantee_forward_progress(); + } + } + else + SPIRV_CROSS_THROW("Invalid BitCount operation."); + break; + } + + case OpGroupNonUniformShuffle: + emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt"); + break; + case OpGroupNonUniformShuffleXor: + { + bool forward = should_forward(ops[3]); + emit_op(ops[0], ops[1], + join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ", + "WaveGetLaneIndex() ^ ", to_enclosed_expression(ops[4]), ")"), forward); + inherit_expression_dependencies(ops[1], ops[3]); + break; + } + case OpGroupNonUniformShuffleUp: + { + bool forward = should_forward(ops[3]); + emit_op(ops[0], ops[1], + join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ", + "WaveGetLaneIndex() - ", to_enclosed_expression(ops[4]), ")"), forward); + inherit_expression_dependencies(ops[1], ops[3]); + break; + } + case OpGroupNonUniformShuffleDown: + { + bool forward = should_forward(ops[3]); + emit_op(ops[0], ops[1], + join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ", + "WaveGetLaneIndex() + ", to_enclosed_expression(ops[4]), ")"), forward); + inherit_expression_dependencies(ops[1], ops[3]); + break; + } + + case OpGroupNonUniformAll: + emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllTrue"); + break; + + case OpGroupNonUniformAny: + emit_unary_func_op(result_type, id, ops[3], "WaveActiveAnyTrue"); + break; + + case OpGroupNonUniformAllEqual: + emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllEqual"); + break; + + // clang-format off +#define HLSL_GROUP_OP(op, hlsl_op, supports_scan) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[4], "WaveActive" #hlsl_op); \ + else if (operation == GroupOperationInclusiveScan && supports_scan) \ + { \ + bool forward = should_forward(ops[4]); \ + emit_op(result_type, id, make_inclusive_##hlsl_op (join("WavePrefix" #hlsl_op, "(", to_expression(ops[4]), ")")), forward); \ + inherit_expression_dependencies(id, ops[4]); \ + } \ + else if (operation == GroupOperationExclusiveScan && supports_scan) \ + emit_unary_func_op(result_type, id, ops[4], "WavePrefix" #hlsl_op); \ + else if (operation == GroupOperationClusteredReduce) \ + SPIRV_CROSS_THROW("Cannot trivially implement ClusteredReduce in HLSL."); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + +#define HLSL_GROUP_OP_CAST(op, hlsl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[3]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[4], "WaveActive" #hlsl_op, type, type); \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + + 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_CAST(SMin, Min, int_type) + HLSL_GROUP_OP_CAST(SMax, Max, int_type) + HLSL_GROUP_OP_CAST(UMin, Min, uint_type) + HLSL_GROUP_OP_CAST(UMax, Max, uint_type) + HLSL_GROUP_OP(BitwiseAnd, BitAnd, false) + HLSL_GROUP_OP(BitwiseOr, BitOr, false) + HLSL_GROUP_OP(BitwiseXor, BitXor, false) + HLSL_GROUP_OP_CAST(LogicalAnd, BitAnd, uint_type) + HLSL_GROUP_OP_CAST(LogicalOr, BitOr, uint_type) + HLSL_GROUP_OP_CAST(LogicalXor, BitXor, uint_type) + +#undef HLSL_GROUP_OP +#undef HLSL_GROUP_OP_CAST + // clang-format on + + case OpGroupNonUniformQuadSwap: + { + uint32_t direction = evaluate_constant_u32(ops[4]); + if (direction == 0) + emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossX"); + else if (direction == 1) + emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossY"); + else if (direction == 2) + emit_unary_func_op(result_type, id, ops[3], "QuadReadAcrossDiagonal"); + else + SPIRV_CROSS_THROW("Invalid quad swap direction."); + break; + } + + case OpGroupNonUniformQuadBroadcast: + { + emit_binary_func_op(result_type, id, ops[3], ops[4], "QuadReadLaneAt"); + break; + } + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + register_control_dependent_expression(id); +} + +void CompilerHLSL::emit_instruction(const Instruction &instruction) +{ + auto ops = stream(instruction); + auto opcode = static_cast(instruction.op); + +#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, opcode_is_sign_invariant(opcode), false) +#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, opcode_is_sign_invariant(opcode)) +#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) + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(instruction); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + opcode = get_remapped_spirv_op(opcode); + + switch (opcode) + { + case OpAccessChain: + case OpInBoundsAccessChain: + { + emit_access_chain(instruction); + break; + } + case OpBitcast: + { + auto bitcast_type = get_bitcast_type(ops[0], ops[2]); + if (bitcast_type == CompilerHLSL::TypeNormal) + CompilerGLSL::emit_instruction(instruction); + else + { + if (!requires_uint2_packing) + { + requires_uint2_packing = true; + force_recompile(); + } + + if (bitcast_type == CompilerHLSL::TypePackUint2x32) + emit_unary_func_op(ops[0], ops[1], ops[2], "spvPackUint2x32"); + else + emit_unary_func_op(ops[0], ops[1], ops[2], "spvUnpackUint2x32"); + } + + break; + } + + case OpSelect: + { + auto &value_type = expression_type(ops[3]); + if (value_type.basetype == SPIRType::Struct || is_array(value_type)) + { + // HLSL does not support ternary expressions on composites. + // Cannot use branches, since we might be in a continue block + // where explicit control flow is prohibited. + // Emit a helper function where we can use control flow. + TypeID value_type_id = expression_type_id(ops[3]); + auto itr = std::find(composite_selection_workaround_types.begin(), + composite_selection_workaround_types.end(), + value_type_id); + if (itr == composite_selection_workaround_types.end()) + { + composite_selection_workaround_types.push_back(value_type_id); + force_recompile(); + } + emit_uninitialized_temporary_expression(ops[0], ops[1]); + statement("spvSelectComposite(", + to_expression(ops[1]), ", ", to_expression(ops[2]), ", ", + to_expression(ops[3]), ", ", to_expression(ops[4]), ");"); + } + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpStore: + { + emit_store(instruction); + break; + } + + case OpLoad: + { + emit_load(instruction); + break; + } + + case OpMatrixTimesVector: + { + // Matrices are kept in a transposed state all the time, flip multiplication order always. + emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul"); + break; + } + + case OpVectorTimesMatrix: + { + // Matrices are kept in a transposed state all the time, flip multiplication order always. + emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul"); + break; + } + + case OpMatrixTimesMatrix: + { + // Matrices are kept in a transposed state all the time, flip multiplication order always. + emit_binary_func_op(ops[0], ops[1], ops[3], ops[2], "mul"); + break; + } + + case OpOuterProduct: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2]; + uint32_t b = ops[3]; + + auto &type = get(result_type); + string expr = type_to_glsl_constructor(type); + expr += "("; + for (uint32_t col = 0; col < type.columns; col++) + { + expr += to_enclosed_expression(a); + expr += " * "; + expr += to_extract_component_expression(b, col); + if (col + 1 < type.columns) + expr += ", "; + } + expr += ")"; + emit_op(result_type, id, expr, should_forward(a) && should_forward(b)); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + case OpFMod: + { + if (!requires_op_fmod) + { + requires_op_fmod = true; + force_recompile(); + } + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpFRem: + emit_binary_func_op(ops[0], ops[1], ops[2], ops[3], "fmod"); + break; + + case OpImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto *combined = maybe_get(ops[2]); + + if (combined) + { + auto &e = emit_op(result_type, id, to_expression(combined->image), true, true); + auto *var = maybe_get_backing_variable(combined->image); + if (var) + e.loaded_from = var->self; + } + else + { + auto &e = emit_op(result_type, id, to_expression(ops[2]), true, true); + auto *var = maybe_get_backing_variable(ops[2]); + if (var) + e.loaded_from = var->self; + } + break; + } + + case OpDPdx: + HLSL_UFOP(ddx); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdy: + HLSL_UFOP(ddy); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxFine: + HLSL_UFOP(ddx_fine); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyFine: + HLSL_UFOP(ddy_fine); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdxCoarse: + HLSL_UFOP(ddx_coarse); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdyCoarse: + HLSL_UFOP(ddy_coarse); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidth: + case OpFwidthCoarse: + case OpFwidthFine: + HLSL_UFOP(fwidth); + register_control_dependent_expression(ops[1]); + break; + + case OpLogicalNot: + { + auto result_type = ops[0]; + auto id = ops[1]; + auto &type = get(result_type); + + if (type.vecsize > 1) + emit_unrolled_unary_op(result_type, id, ops[2], "!"); + else + HLSL_UOP(!); + break; + } + + case OpIEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==", false, SPIRType::Unknown); + else + HLSL_BOP_CAST(==, int_type); + break; + } + + case OpLogicalEqual: + case OpFOrdEqual: + case OpFUnordEqual: + { + // HLSL != operator is unordered. + // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-float-rules. + // isnan() is apparently implemented as x != x as well. + // We cannot implement UnordEqual as !(OrdNotEqual), as HLSL cannot express OrdNotEqual. + // HACK: FUnordEqual will be implemented as FOrdEqual. + + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "==", false, SPIRType::Unknown); + else + HLSL_BOP(==); + break; + } + + case OpINotEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=", false, SPIRType::Unknown); + else + HLSL_BOP_CAST(!=, int_type); + break; + } + + case OpLogicalNotEqual: + case OpFOrdNotEqual: + case OpFUnordNotEqual: + { + // HLSL != operator is unordered. + // https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-float-rules. + // isnan() is apparently implemented as x != x as well. + + // FIXME: FOrdNotEqual cannot be implemented in a crisp and simple way here. + // We would need to do something like not(UnordEqual), but that cannot be expressed either. + // Adding a lot of NaN checks would be a breaking change from perspective of performance. + // SPIR-V will generally use isnan() checks when this even matters. + // HACK: FOrdNotEqual will be implemented as FUnordEqual. + + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "!=", false, SPIRType::Unknown); + else + HLSL_BOP(!=); + break; + } + + case OpUGreaterThan: + case OpSGreaterThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + auto type = opcode == OpUGreaterThan ? uint_type : int_type; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", false, type); + else + HLSL_BOP_CAST(>, type); + break; + } + + case OpFOrdGreaterThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", false, SPIRType::Unknown); + else + HLSL_BOP(>); + break; + } + + case OpFUnordGreaterThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpUGreaterThanEqual: + case OpSGreaterThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + auto type = opcode == OpUGreaterThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", false, type); + else + HLSL_BOP_CAST(>=, type); + break; + } + + case OpFOrdGreaterThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", false, SPIRType::Unknown); + else + HLSL_BOP(>=); + break; + } + + case OpFUnordGreaterThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpULessThan: + case OpSLessThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + auto type = opcode == OpULessThan ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", false, type); + else + HLSL_BOP_CAST(<, type); + break; + } + + case OpFOrdLessThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<", false, SPIRType::Unknown); + else + HLSL_BOP(<); + break; + } + + case OpFUnordLessThan: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">=", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpULessThanEqual: + case OpSLessThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + auto type = opcode == OpULessThanEqual ? uint_type : int_type; + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", false, type); + else + HLSL_BOP_CAST(<=, type); + break; + } + + case OpFOrdLessThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], "<=", false, SPIRType::Unknown); + else + HLSL_BOP(<=); + break; + } + + case OpFUnordLessThanEqual: + { + auto result_type = ops[0]; + auto id = ops[1]; + + if (expression_type(ops[2]).vecsize > 1) + emit_unrolled_binary_op(result_type, id, ops[2], ops[3], ">", true, SPIRType::Unknown); + else + CompilerGLSL::emit_instruction(instruction); + break; + } + + case OpImageQueryLod: + emit_texture_op(instruction, false); + break; + + case OpImageQuerySizeLod: + { + auto result_type = ops[0]; + auto id = ops[1]; + + require_texture_query_variant(ops[2]); + auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter"); + statement("uint ", dummy_samples_levels, ";"); + + auto expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", ", + bitcast_expression(SPIRType::UInt, ops[3]), ", ", dummy_samples_levels, ")"); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::UInt, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpImageQuerySize: + { + auto result_type = ops[0]; + auto id = ops[1]; + + require_texture_query_variant(ops[2]); + bool uav = expression_type(ops[2]).image.sampled == 2; + + if (const auto *var = maybe_get_backing_variable(ops[2])) + if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable)) + uav = false; + + auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter"); + statement("uint ", dummy_samples_levels, ";"); + + string expr; + if (uav) + expr = join("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", dummy_samples_levels, ")"); + else + expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")"); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::UInt, expr); + emit_op(result_type, id, expr, true); + break; + } + + case OpImageQuerySamples: + case OpImageQueryLevels: + { + auto result_type = ops[0]; + auto id = ops[1]; + + require_texture_query_variant(ops[2]); + bool uav = expression_type(ops[2]).image.sampled == 2; + if (opcode == OpImageQueryLevels && uav) + SPIRV_CROSS_THROW("Cannot query levels for UAV images."); + + if (const auto *var = maybe_get_backing_variable(ops[2])) + if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable)) + uav = false; + + // Keep it simple and do not emit special variants to make this look nicer ... + // This stuff is barely, if ever, used. + forced_temporaries.insert(id); + auto &type = get(result_type); + statement(variable_decl(type, to_name(id)), ";"); + + if (uav) + statement("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", to_name(id), ");"); + else + statement("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", to_name(id), ");"); + + auto &restype = get(ops[0]); + auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id)); + set(id, expr, result_type, true); + break; + } + + case OpImageRead: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto *var = maybe_get_backing_variable(ops[2]); + auto &type = expression_type(ops[2]); + bool subpass_data = type.image.dim == DimSubpassData; + bool pure = false; + + string imgexpr; + + if (subpass_data) + { + if (hlsl_options.shader_model < 40) + SPIRV_CROSS_THROW("Subpass loads are not supported in HLSL shader model 2/3."); + + // Similar to GLSL, implement subpass loads using texelFetch. + if (type.image.ms) + { + uint32_t operands = ops[4]; + if (operands != ImageOperandsSampleMask || instruction.length != 6) + SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used."); + uint32_t sample = ops[5]; + imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")"); + } + else + imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))"); + + pure = true; + } + else + { + imgexpr = join(to_non_uniform_aware_expression(ops[2]), "[", to_expression(ops[3]), "]"); + // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4", + // except that the underlying type changes how the data is interpreted. + + bool force_srv = + hlsl_options.nonwritable_uav_texture_as_srv && var && has_decoration(var->self, DecorationNonWritable); + pure = force_srv; + + if (var && !subpass_data && !force_srv) + imgexpr = remap_swizzle(get(result_type), + image_format_to_components(get(var->basetype).image.format), imgexpr); + } + + if (var) + { + bool forward = forced_temporaries.find(id) == end(forced_temporaries); + auto &e = emit_op(result_type, id, imgexpr, forward); + + if (!pure) + { + e.loaded_from = var->self; + if (forward) + var->dependees.push_back(id); + } + } + else + emit_op(result_type, id, imgexpr, false); + + inherit_expression_dependencies(id, ops[2]); + if (type.image.ms) + inherit_expression_dependencies(id, ops[5]); + break; + } + + case OpImageWrite: + { + auto *var = maybe_get_backing_variable(ops[0]); + + // The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4", + // except that the underlying type changes how the data is interpreted. + auto value_expr = to_expression(ops[2]); + if (var) + { + auto &type = get(var->basetype); + auto narrowed_type = get(type.image.type); + narrowed_type.vecsize = image_format_to_components(type.image.format); + value_expr = remap_swizzle(narrowed_type, expression_type(ops[2]).vecsize, value_expr); + } + + statement(to_non_uniform_aware_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";"); + if (var && variable_storage_is_aliased(*var)) + flush_all_aliased_variables(); + break; + } + + case OpImageTexelPointer: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + auto expr = to_expression(ops[2]); + expr += join("[", to_expression(ops[3]), "]"); + auto &e = set(id, expr, result_type, true); + + // When using the pointer, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); + break; + } + + case OpAtomicFAddEXT: + case OpAtomicFMinEXT: + case OpAtomicFMaxEXT: + SPIRV_CROSS_THROW("Floating-point atomics are not supported in HLSL."); + + case OpAtomicCompareExchange: + case OpAtomicExchange: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + case OpAtomicIAdd: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicLoad: + case OpAtomicStore: + { + emit_atomic(ops, instruction.length, opcode); + break; + } + + case OpControlBarrier: + case OpMemoryBarrier: + { + uint32_t memory; + uint32_t semantics; + + if (opcode == OpMemoryBarrier) + { + memory = evaluate_constant_u32(ops[0]); + semantics = evaluate_constant_u32(ops[1]); + } + else + { + memory = evaluate_constant_u32(ops[1]); + semantics = evaluate_constant_u32(ops[2]); + } + + if (memory == ScopeSubgroup) + { + // No Wave-barriers in HLSL. + break; + } + + // We only care about these flags, acquire/release and friends are not relevant to GLSL. + semantics = mask_relevant_memory_semantics(semantics); + + if (opcode == OpMemoryBarrier) + { + // If we are a memory barrier, and the next instruction is a control barrier, check if that memory barrier + // does what we need, so we avoid redundant barriers. + const Instruction *next = get_next_instruction_in_block(instruction); + if (next && next->op == OpControlBarrier) + { + auto *next_ops = stream(*next); + uint32_t next_memory = evaluate_constant_u32(next_ops[1]); + uint32_t next_semantics = evaluate_constant_u32(next_ops[2]); + next_semantics = mask_relevant_memory_semantics(next_semantics); + + // There is no "just execution barrier" in HLSL. + // If there are no memory semantics for next instruction, we will imply group shared memory is synced. + if (next_semantics == 0) + next_semantics = MemorySemanticsWorkgroupMemoryMask; + + bool memory_scope_covered = false; + if (next_memory == memory) + memory_scope_covered = true; + else if (next_semantics == MemorySemanticsWorkgroupMemoryMask) + { + // If we only care about workgroup memory, either Device or Workgroup scope is fine, + // scope does not have to match. + if ((next_memory == ScopeDevice || next_memory == ScopeWorkgroup) && + (memory == ScopeDevice || memory == ScopeWorkgroup)) + { + memory_scope_covered = true; + } + } + else if (memory == ScopeWorkgroup && next_memory == ScopeDevice) + { + // The control barrier has device scope, but the memory barrier just has workgroup scope. + memory_scope_covered = true; + } + + // If we have the same memory scope, and all memory types are covered, we're good. + if (memory_scope_covered && (semantics & next_semantics) == semantics) + break; + } + } + + // We are synchronizing some memory or syncing execution, + // so we cannot forward any loads beyond the memory barrier. + if (semantics || opcode == OpControlBarrier) + { + assert(current_emitting_block); + flush_control_dependent_expressions(current_emitting_block->self); + flush_all_active_variables(); + } + + if (opcode == OpControlBarrier) + { + // We cannot emit just execution barrier, for no memory semantics pick the cheapest option. + if (semantics == MemorySemanticsWorkgroupMemoryMask || semantics == 0) + statement("GroupMemoryBarrierWithGroupSync();"); + else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0) + statement("DeviceMemoryBarrierWithGroupSync();"); + else + statement("AllMemoryBarrierWithGroupSync();"); + } + else + { + if (semantics == MemorySemanticsWorkgroupMemoryMask) + statement("GroupMemoryBarrier();"); + else if (semantics != 0 && (semantics & MemorySemanticsWorkgroupMemoryMask) == 0) + statement("DeviceMemoryBarrier();"); + else + statement("AllMemoryBarrier();"); + } + break; + } + + case OpBitFieldInsert: + { + if (!requires_bitfield_insert) + { + requires_bitfield_insert = true; + force_recompile(); + } + + auto expr = join("spvBitfieldInsert(", to_expression(ops[2]), ", ", to_expression(ops[3]), ", ", + to_expression(ops[4]), ", ", to_expression(ops[5]), ")"); + + bool forward = + should_forward(ops[2]) && should_forward(ops[3]) && should_forward(ops[4]) && should_forward(ops[5]); + + auto &restype = get(ops[0]); + expr = bitcast_expression(restype, SPIRType::UInt, expr); + emit_op(ops[0], ops[1], expr, forward); + break; + } + + case OpBitFieldSExtract: + case OpBitFieldUExtract: + { + if (!requires_bitfield_extract) + { + requires_bitfield_extract = true; + force_recompile(); + } + + if (opcode == OpBitFieldSExtract) + HLSL_TFOP(spvBitfieldSExtract); + else + HLSL_TFOP(spvBitfieldUExtract); + break; + } + + case OpBitCount: + { + auto basetype = expression_type(ops[2]).basetype; + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "countbits", basetype, basetype); + break; + } + + case OpBitReverse: + HLSL_UFOP(reversebits); + break; + + case OpArrayLength: + { + auto *var = maybe_get_backing_variable(ops[2]); + if (!var) + SPIRV_CROSS_THROW("Array length must point directly to an SSBO block."); + + auto &type = get(var->basetype); + if (!has_decoration(type.self, DecorationBlock) && !has_decoration(type.self, DecorationBufferBlock)) + SPIRV_CROSS_THROW("Array length expression must point to a block type."); + + // This must be 32-bit uint, so we're good to go. + emit_uninitialized_temporary_expression(ops[0], ops[1]); + statement(to_non_uniform_aware_expression(ops[2]), ".GetDimensions(", to_expression(ops[1]), ");"); + uint32_t offset = type_struct_member_offset(type, ops[3]); + uint32_t stride = type_struct_member_array_stride(type, ops[3]); + statement(to_expression(ops[1]), " = (", to_expression(ops[1]), " - ", offset, ") / ", stride, ";"); + break; + } + + case OpIsHelperInvocationEXT: + if (hlsl_options.shader_model < 50 || get_entry_point().model != ExecutionModelFragment) + SPIRV_CROSS_THROW("Helper Invocation input is only supported in PS 5.0 or higher."); + // Helper lane state with demote is volatile by nature. + // Do not forward this. + emit_op(ops[0], ops[1], "IsHelperLane()", false); + break; + + case OpBeginInvocationInterlockEXT: + case OpEndInvocationInterlockEXT: + if (hlsl_options.shader_model < 51) + SPIRV_CROSS_THROW("Rasterizer order views require Shader Model 5.1."); + break; // Nothing to do in the body + + case OpRayQueryInitializeKHR: + { + flush_variable_declaration(ops[0]); + + std::string ray_desc_name = get_unique_identifier(); + statement("RayDesc ", ray_desc_name, " = {", to_expression(ops[4]), ", ", to_expression(ops[5]), ", ", + to_expression(ops[6]), ", ", to_expression(ops[7]), "};"); + + statement(to_expression(ops[0]), ".TraceRayInline(", + to_expression(ops[1]), ", ", // acc structure + to_expression(ops[2]), ", ", // ray flags + to_expression(ops[3]), ", ", // mask + ray_desc_name, ");"); // ray + break; + } + case OpRayQueryProceedKHR: + { + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".Proceed()"), false); + break; + } + case OpRayQueryTerminateKHR: + { + flush_variable_declaration(ops[0]); + statement(to_expression(ops[0]), ".Abort();"); + break; + } + case OpRayQueryGenerateIntersectionKHR: + { + flush_variable_declaration(ops[0]); + statement(to_expression(ops[0]), ".CommitProceduralPrimitiveHit(", to_expression(ops[1]), ");"); + break; + } + case OpRayQueryConfirmIntersectionKHR: + { + flush_variable_declaration(ops[0]); + statement(to_expression(ops[0]), ".CommitNonOpaqueTriangleHit();"); + break; + } + case OpRayQueryGetIntersectionTypeKHR: + { + emit_rayquery_function(".CommittedStatus()", ".CandidateType()", ops); + break; + } + case OpRayQueryGetIntersectionTKHR: + { + emit_rayquery_function(".CommittedRayT()", ".CandidateTriangleRayT()", ops); + break; + } + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: + { + emit_rayquery_function(".CommittedInstanceID()", ".CandidateInstanceID()", ops); + break; + } + case OpRayQueryGetIntersectionInstanceIdKHR: + { + emit_rayquery_function(".CommittedInstanceIndex()", ".CandidateInstanceIndex()", ops); + break; + } + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: + { + emit_rayquery_function(".CommittedInstanceContributionToHitGroupIndex()", + ".CandidateInstanceContributionToHitGroupIndex()", ops); + break; + } + case OpRayQueryGetIntersectionGeometryIndexKHR: + { + emit_rayquery_function(".CommittedGeometryIndex()", + ".CandidateGeometryIndex()", ops); + break; + } + case OpRayQueryGetIntersectionPrimitiveIndexKHR: + { + emit_rayquery_function(".CommittedPrimitiveIndex()", ".CandidatePrimitiveIndex()", ops); + break; + } + case OpRayQueryGetIntersectionBarycentricsKHR: + { + emit_rayquery_function(".CommittedTriangleBarycentrics()", ".CandidateTriangleBarycentrics()", ops); + break; + } + case OpRayQueryGetIntersectionFrontFaceKHR: + { + emit_rayquery_function(".CommittedTriangleFrontFace()", ".CandidateTriangleFrontFace()", ops); + break; + } + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: + { + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".CandidateProceduralPrimitiveNonOpaque()"), false); + break; + } + case OpRayQueryGetIntersectionObjectRayDirectionKHR: + { + emit_rayquery_function(".CommittedObjectRayDirection()", ".CandidateObjectRayDirection()", ops); + break; + } + case OpRayQueryGetIntersectionObjectRayOriginKHR: + { + flush_variable_declaration(ops[0]); + emit_rayquery_function(".CommittedObjectRayOrigin()", ".CandidateObjectRayOrigin()", ops); + break; + } + case OpRayQueryGetIntersectionObjectToWorldKHR: + { + emit_rayquery_function(".CommittedObjectToWorld4x3()", ".CandidateObjectToWorld4x3()", ops); + break; + } + case OpRayQueryGetIntersectionWorldToObjectKHR: + { + emit_rayquery_function(".CommittedWorldToObject4x3()", ".CandidateWorldToObject4x3()", ops); + break; + } + case OpRayQueryGetRayFlagsKHR: + { + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".RayFlags()"), false); + break; + } + case OpRayQueryGetRayTMinKHR: + { + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".RayTMin()"), false); + break; + } + case OpRayQueryGetWorldRayOriginKHR: + { + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".WorldRayOrigin()"), false); + break; + } + case OpRayQueryGetWorldRayDirectionKHR: + { + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".WorldRayDirection()"), false); + break; + } + case OpSetMeshOutputsEXT: + { + statement("SetMeshOutputCounts(", to_unpacked_expression(ops[0]), ", ", to_unpacked_expression(ops[1]), ");"); + break; + } + case OpEmitVertex: + { + emit_geometry_stream_append(); + break; + } + case OpEndPrimitive: + { + statement("geometry_stream.RestartStrip();"); + break; + } + default: + CompilerGLSL::emit_instruction(instruction); + break; + } +} + +void CompilerHLSL::require_texture_query_variant(uint32_t var_id) +{ + if (const auto *var = maybe_get_backing_variable(var_id)) + var_id = var->self; + + auto &type = expression_type(var_id); + bool uav = type.image.sampled == 2; + if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var_id, DecorationNonWritable)) + uav = false; + + uint32_t bit = 0; + switch (type.image.dim) + { + case Dim1D: + bit = type.image.arrayed ? Query1DArray : Query1D; + break; + + case Dim2D: + if (type.image.ms) + bit = type.image.arrayed ? Query2DMSArray : Query2DMS; + else + bit = type.image.arrayed ? Query2DArray : Query2D; + break; + + case Dim3D: + bit = Query3D; + break; + + case DimCube: + bit = type.image.arrayed ? QueryCubeArray : QueryCube; + break; + + case DimBuffer: + bit = QueryBuffer; + break; + + default: + SPIRV_CROSS_THROW("Unsupported query type."); + } + + switch (get(type.image.type).basetype) + { + case SPIRType::Float: + bit += QueryTypeFloat; + break; + + case SPIRType::Int: + bit += QueryTypeInt; + break; + + case SPIRType::UInt: + bit += QueryTypeUInt; + break; + + default: + SPIRV_CROSS_THROW("Unsupported query type."); + } + + auto norm_state = image_format_to_normalized_state(type.image.format); + auto &variant = uav ? required_texture_size_variants + .uav[uint32_t(norm_state)][image_format_to_components(type.image.format) - 1] : + required_texture_size_variants.srv; + + uint64_t mask = 1ull << bit; + if ((variant & mask) == 0) + { + force_recompile(); + variant |= mask; + } +} + +void CompilerHLSL::set_root_constant_layouts(std::vector layout) +{ + root_constants_layout = std::move(layout); +} + +void CompilerHLSL::add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes) +{ + remap_vertex_attributes.push_back(vertex_attributes); +} + +VariableID CompilerHLSL::remap_num_workgroups_builtin() +{ + update_active_builtins(); + + if (!active_input_builtins.get(BuiltInNumWorkgroups)) + return 0; + + // Create a new, fake UBO. + uint32_t offset = ir.increase_bound_by(4); + + uint32_t uint_type_id = offset; + uint32_t block_type_id = offset + 1; + uint32_t block_pointer_type_id = offset + 2; + uint32_t variable_id = offset + 3; + + SPIRType uint_type { OpTypeVector }; + uint_type.basetype = SPIRType::UInt; + uint_type.width = 32; + uint_type.vecsize = 3; + uint_type.columns = 1; + set(uint_type_id, uint_type); + + SPIRType block_type { OpTypeStruct }; + block_type.basetype = SPIRType::Struct; + block_type.member_types.push_back(uint_type_id); + set(block_type_id, block_type); + set_decoration(block_type_id, DecorationBlock); + set_member_name(block_type_id, 0, "count"); + set_member_decoration(block_type_id, 0, DecorationOffset, 0); + + SPIRType block_pointer_type = block_type; + block_pointer_type.pointer = true; + block_pointer_type.storage = StorageClassUniform; + block_pointer_type.parent_type = block_type_id; + auto &ptr_type = set(block_pointer_type_id, block_pointer_type); + + // Preserve self. + ptr_type.self = block_type_id; + + set(variable_id, block_pointer_type_id, StorageClassUniform); + ir.meta[variable_id].decoration.alias = "SPIRV_Cross_NumWorkgroups"; + + num_workgroups_builtin = variable_id; + get_entry_point().interface_variables.push_back(num_workgroups_builtin); + return variable_id; +} + +void CompilerHLSL::set_resource_binding_flags(HLSLBindingFlags flags) +{ + resource_binding_flags = flags; +} + +void CompilerHLSL::validate_shader_model() +{ + // Check for nonuniform qualifier. + // Instead of looping over all decorations to find this, just look at capabilities. + for (auto &cap : ir.declared_capabilities) + { + switch (cap) + { + case CapabilityShaderNonUniformEXT: + case CapabilityRuntimeDescriptorArrayEXT: + if (hlsl_options.shader_model < 51) + SPIRV_CROSS_THROW( + "Shader model 5.1 or higher is required to use bindless resources or NonUniformResourceIndex."); + break; + + case CapabilityVariablePointers: + case CapabilityVariablePointersStorageBuffer: + SPIRV_CROSS_THROW("VariablePointers capability is not supported in HLSL."); + + default: + break; + } + } + + if (ir.addressing_model != AddressingModelLogical) + SPIRV_CROSS_THROW("Only Logical addressing model can be used with HLSL."); + + if (hlsl_options.enable_16bit_types && hlsl_options.shader_model < 62) + SPIRV_CROSS_THROW("Need at least shader model 6.2 when enabling native 16-bit type support."); +} + +string CompilerHLSL::compile() +{ + ir.fixup_reserved_names(); + + // Do not deal with ES-isms like precision, older extensions and such. + options.es = false; + options.version = 450; + options.vulkan_semantics = true; + backend.float_literal_suffix = true; + backend.double_literal_suffix = false; + backend.long_long_literal_suffix = true; + backend.uint32_t_literal_suffix = true; + backend.int16_t_literal_suffix = ""; + backend.uint16_t_literal_suffix = "u"; + backend.basic_int_type = "int"; + backend.basic_uint_type = "uint"; + backend.demote_literal = "discard"; + backend.boolean_mix_function = ""; + backend.swizzle_is_function = false; + backend.shared_is_implied = true; + backend.unsized_array_supported = true; + backend.explicit_struct_type = false; + backend.use_initializer_list = true; + backend.use_constructor_splatting = false; + backend.can_swizzle_scalar = true; + backend.can_declare_struct_inline = false; + backend.can_declare_arrays_inline = false; + backend.can_return_array = false; + backend.nonuniform_qualifier = "NonUniformResourceIndex"; + backend.support_case_fallthrough = false; + backend.requires_phi_undef_zero_init = true; + backend.force_merged_mesh_block = get_execution_model() == ExecutionModelMeshEXT; + backend.force_gl_in_out_block = backend.force_merged_mesh_block; + backend.supports_empty_struct = hlsl_options.shader_model <= 30; + + // SM 4.1 does not support precise for some reason. + backend.support_precise_qualifier = hlsl_options.shader_model >= 50 || hlsl_options.shader_model == 40; + + fixup_anonymous_struct_names(); + fixup_type_alias(); + reorder_type_alias(); + build_function_control_flow_graphs_and_analyze(); + validate_shader_model(); + update_active_builtins(); + analyze_image_and_sampler_usage(); + analyze_interlocked_resource_usage(); + if (get_execution_model() == ExecutionModelMeshEXT) + analyze_meshlet_writes(); + + if (get_execution_model() == ExecutionModelGeometry) + discover_geometry_emitters(); + + // Subpass input needs SV_Position. + if (need_subpass_input) + active_input_builtins.set(BuiltInFragCoord); + + // Need to offset by BaseVertex/BaseInstance in SM 6.8+. + if (hlsl_options.shader_model >= 68) + { + if (active_input_builtins.get(BuiltInVertexIndex)) + active_input_builtins.set(BuiltInBaseVertex); + if (active_input_builtins.get(BuiltInInstanceIndex)) + active_input_builtins.set(BuiltInBaseInstance); + } + + uint32_t pass_count = 0; + do + { + reset(pass_count); + + // Move constructor for this type is broken on GCC 4.9 ... + buffer.reset(); + + emit_header(); + emit_resources(); + + emit_function(get(ir.default_entry_point), Bitset()); + emit_hlsl_entry_point(); + + pass_count++; + } while (is_forcing_recompilation()); + + // Entry point in HLSL is always main() for the time being. + get_entry_point().name = "main"; + + 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; + } +} + +string CompilerHLSL::get_unique_identifier() +{ + return join("_", unique_identifier_count++, "ident"); +} + +void CompilerHLSL::add_hlsl_resource_binding(const HLSLResourceBinding &binding) +{ + StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding }; + resource_bindings[tuple] = { binding, false }; +} + +bool CompilerHLSL::is_hlsl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const +{ + StageSetBinding tuple = { model, desc_set, binding }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) && itr->second.second; +} + +CompilerHLSL::BitcastType CompilerHLSL::get_bitcast_type(uint32_t result_type, uint32_t op0) +{ + auto &rslt_type = get(result_type); + auto &expr_type = expression_type(op0); + + if (rslt_type.basetype == SPIRType::BaseType::UInt64 && expr_type.basetype == SPIRType::BaseType::UInt && + expr_type.vecsize == 2) + return BitcastType::TypePackUint2x32; + else if (rslt_type.basetype == SPIRType::BaseType::UInt && rslt_type.vecsize == 2 && + expr_type.basetype == SPIRType::BaseType::UInt64) + return BitcastType::TypeUnpackUint64; + + return BitcastType::TypeNormal; +} + +bool CompilerHLSL::is_hlsl_force_storage_buffer_as_uav(ID id) const +{ + if (hlsl_options.force_storage_buffer_as_uav) + { + return true; + } + + const uint32_t desc_set = get_decoration(id, DecorationDescriptorSet); + const uint32_t binding = get_decoration(id, DecorationBinding); + + return (force_uav_buffer_bindings.find({ desc_set, binding }) != force_uav_buffer_bindings.end()); +} + +bool CompilerHLSL::is_hidden_io_variable(const SPIRVariable &var) const +{ + if (!is_hidden_variable(var)) + return false; + + // It is too risky to remove stage IO variables that are linkable since it affects link compatibility. + // For vertex inputs and fragment outputs, it's less of a concern and we want reflection data + // to match reality. + + bool is_external_linkage = + (get_execution_model() == ExecutionModelVertex && var.storage == StorageClassInput) || + (get_execution_model() == ExecutionModelFragment && var.storage == StorageClassOutput); + + if (!is_external_linkage) + return false; + + // Unused output I/O variables might still be required to implement framebuffer fetch. + if (var.storage == StorageClassOutput && !is_legacy() && + location_is_framebuffer_fetch(get_decoration(var.self, DecorationLocation)) != 0) + return false; + + return true; +} + +void CompilerHLSL::set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding) +{ + SetBindingPair pair = { desc_set, binding }; + force_uav_buffer_bindings.insert(pair); +} + +bool CompilerHLSL::is_user_type_structured(uint32_t id) const +{ + if (hlsl_options.preserve_structured_buffers) + { + // Compare left hand side of string only as these user types can contain more meta data such as their subtypes, + // e.g. "structuredbuffer:int" + const std::string &user_type = get_decoration_string(id, DecorationUserTypeGOOGLE); + return user_type.compare(0, 16, "structuredbuffer") == 0 || + user_type.compare(0, 18, "rwstructuredbuffer") == 0 || + user_type.compare(0, 35, "globallycoherent rwstructuredbuffer") == 0 || + user_type.compare(0, 33, "rasterizerorderedstructuredbuffer") == 0; + } + return false; +} + +void CompilerHLSL::cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) +{ + // Loading a full array of ClipDistance needs special consideration in mesh shaders + // since we cannot lower them by wrapping the variables in global statics. + // Fortunately, clip/cull is a proper vector in HLSL so we can lower with simple rvalue casts. + if (get_execution_model() != ExecutionModelMeshEXT || + !has_decoration(target_id, DecorationBuiltIn) || + !is_array(expr_type)) + { + CompilerGLSL::cast_to_variable_store(target_id, expr, expr_type); + return; + } + + auto builtin = BuiltIn(get_decoration(target_id, DecorationBuiltIn)); + if (builtin != BuiltInClipDistance && builtin != BuiltInCullDistance) + { + CompilerGLSL::cast_to_variable_store(target_id, expr, expr_type); + return; + } + + // Array of array means one thread is storing clip distance for all vertices. Nonsensical? + if (is_array(get(expr_type.parent_type))) + SPIRV_CROSS_THROW("Attempting to store all mesh vertices in one go. This is not supported."); + + uint32_t num_clip = to_array_size_literal(expr_type); + if (num_clip > 4) + SPIRV_CROSS_THROW("Number of clip or cull distances exceeds 4, this will not work with mesh shaders."); + + if (num_clip == 1) + { + // We already emit array here. + CompilerGLSL::cast_to_variable_store(target_id, expr, expr_type); + return; + } + + auto unrolled_expr = join("float", num_clip, "("); + for (uint32_t i = 0; i < num_clip; i++) + { + unrolled_expr += join(expr, "[", i, "]"); + if (i + 1 < num_clip) + unrolled_expr += ", "; + } + + unrolled_expr += ")"; + expr = std::move(unrolled_expr); +} diff --git a/thirdparty/SPIRV-Cross/spirv_hlsl.hpp b/thirdparty/SPIRV-Cross/spirv_hlsl.hpp new file mode 100644 index 000000000..0d5181b9d --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_hlsl.hpp @@ -0,0 +1,425 @@ +/* + * Copyright 2016-2021 Robert Konrad + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_HLSL_HPP +#define SPIRV_HLSL_HPP + +#include "spirv_glsl.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +// Interface which remaps vertex inputs to a fixed semantic name to make linking easier. +struct HLSLVertexAttributeRemap +{ + uint32_t location; + std::string semantic; +}; +// Specifying a root constant (d3d12) or push constant range (vulkan). +// +// `start` and `end` denotes the range of the root constant in bytes. +// Both values need to be multiple of 4. +struct RootConstants +{ + uint32_t start; + uint32_t end; + + uint32_t binding; + uint32_t space; +}; + +// For finer control, decorations may be removed from specific resources instead with unset_decoration(). +enum HLSLBindingFlagBits +{ + HLSL_BINDING_AUTO_NONE_BIT = 0, + + // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration. + // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land. + // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it. + HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, + + // cbuffer resources will be declared as CBVs (b-space) without a register() declaration. + // A register will be automatically assigned, but must be reflected in D3D-land. + HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, + + // All SRVs (t-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, + + // All UAVs (u-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_UAV_BIT = 1 << 3, + + // All samplers (s-space) will be declared without a register() declaration. + HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4, + + // No resources will be declared with register(). + HLSL_BINDING_AUTO_ALL = 0x7fffffff +}; +using HLSLBindingFlags = uint32_t; + +// By matching stage, desc_set and binding for a SPIR-V resource, +// register bindings are set based on whether the HLSL resource is a +// CBV, UAV, SRV or Sampler. A single binding in SPIR-V might contain multiple +// resource types, e.g. COMBINED_IMAGE_SAMPLER, and SRV/Sampler bindings will be used respectively. +// On SM 5.0 and lower, register_space is ignored. +// +// To remap a push constant block which does not have any desc_set/binding associated with it, +// use ResourceBindingPushConstant{DescriptorSet,Binding} as values for desc_set/binding. +// For deeper control of push constants, set_root_constant_layouts() can be used instead. +struct HLSLResourceBinding +{ + ExecutionModel stage = ExecutionModelMax; + uint32_t desc_set = 0; + uint32_t binding = 0; + + struct Binding + { + uint32_t register_space = 0; + uint32_t register_binding = 0; + } cbv, uav, srv, sampler; +}; + +enum HLSLAuxBinding +{ + HLSL_AUX_BINDING_BASE_VERTEX_INSTANCE = 0 +}; + +class CompilerHLSL : public CompilerGLSL +{ +public: + struct Options + { + uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow + + // Allows the PointSize builtin in SM 4.0+, and ignores it, as PointSize is not supported in SM 4+. + bool point_size_compat = false; + + // Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL. + bool point_coord_compat = false; + + // If true, the backend will assume that VertexIndex and InstanceIndex will need to apply + // a base offset, and you will need to fill in a cbuffer with offsets. + // Set to false if you know you will never use base instance or base vertex + // functionality as it might remove an internal cbuffer. + bool support_nonzero_base_vertex_base_instance = false; + + // Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used. + // By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead. + // Alternatively, use set_hlsl_force_storage_buffer_as_uav to specify individually. + bool force_storage_buffer_as_uav = false; + + // Forces any storage image type marked as NonWritable to be considered an SRV instead. + // For this to work with function call parameters, NonWritable must be considered to be part of the type system + // so that NonWritable image arguments are also translated to Texture rather than RWTexture. + bool nonwritable_uav_texture_as_srv = false; + + // Enables native 16-bit types. Needs SM 6.2. + // Uses half/int16_t/uint16_t instead of min16* types. + // Also adds support for 16-bit load-store from (RW)ByteAddressBuffer. + bool enable_16bit_types = false; + + // If matrices are used as IO variables, flatten the attribute declaration to use + // TEXCOORD{N,N+1,N+2,...} rather than TEXCOORDN_{0,1,2,3}. + // If add_vertex_attribute_remap is used and this feature is used, + // the semantic name will be queried once per active location. + bool flatten_matrix_vertex_input_semantics = false; + + // Rather than emitting main() for the entry point, use the name in SPIR-V. + bool use_entry_point_name = false; + + // Preserve (RW)StructuredBuffer types if the input source was HLSL. + // This relies on UserTypeGOOGLE to encode the buffer type either as "structuredbuffer" or "rwstructuredbuffer" + // whereas the type can be extended with an optional subtype, e.g. "structuredbuffer:int". + bool preserve_structured_buffers = false; + + // Use UserSemantic decoration info (if specified), otherwise use default mechanism (such as add_vertex_attribute_remap or TEXCOORD#). + bool user_semantic = false; + }; + + explicit CompilerHLSL(std::vector spirv_) + : CompilerGLSL(std::move(spirv_)) + { + } + + CompilerHLSL(const uint32_t *ir_, size_t size) + : CompilerGLSL(ir_, size) + { + } + + explicit CompilerHLSL(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + } + + explicit CompilerHLSL(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + } + + const Options &get_hlsl_options() const + { + return hlsl_options; + } + + void set_hlsl_options(const Options &opts) + { + hlsl_options = opts; + } + + // Optionally specify a custom root constant layout. + // + // Push constants ranges will be split up according to the + // layout specified. + void set_root_constant_layouts(std::vector layout); + + // Compiles and remaps vertex attributes at specific locations to a fixed semantic. + // The default is TEXCOORD# where # denotes location. + // Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row. + // $SEMANTIC is either TEXCOORD# or a semantic name specified here. + void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes); + std::string compile() override; + + // This is a special HLSL workaround for the NumWorkGroups builtin. + // This does not exist in HLSL, so the calling application must create a dummy cbuffer in + // which the application will store this builtin. + // The cbuffer layout will be: + // cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; }; + // This must be called before compile(). + // The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point. + // If non-zero, this returns the variable ID of a cbuffer which corresponds to + // the cbuffer declared above. By default, no binding or descriptor set decoration is set, + // so the calling application should declare explicit bindings on this ID before calling compile(). + VariableID remap_num_workgroups_builtin(); + + // Controls how resource bindings are declared in the output HLSL. + void set_resource_binding_flags(HLSLBindingFlags flags); + + // resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding + // to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_hlsl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the HLSL code. + void add_hlsl_resource_binding(const HLSLResourceBinding &resource); + bool is_hlsl_resource_binding_used(ExecutionModel model, uint32_t set, uint32_t binding) const; + + // Controls which storage buffer bindings will be forced to be declared as UAVs. + void set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding); + + // By default, these magic buffers are not assigned a specific binding. + void set_hlsl_aux_buffer_binding(HLSLAuxBinding binding, uint32_t register_index, uint32_t register_space); + void unset_hlsl_aux_buffer_binding(HLSLAuxBinding binding); + bool is_hlsl_aux_buffer_binding_used(HLSLAuxBinding binding) const; + +private: + std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; + 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); + uint32_t input_vertices_from_execution_mode(SPIREntryPoint &execution) const; + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + void emit_hlsl_entry_point(); + void emit_header() override; + void emit_resources(); + void emit_interface_block_globally(const SPIRVariable &type); + void emit_interface_block_in_struct(const SPIRVariable &var, std::unordered_set &active_locations); + void emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index, uint32_t location, + std::unordered_set &active_locations); + void emit_builtin_inputs_in_struct(); + void emit_builtin_outputs_in_struct(); + void emit_builtin_primitive_outputs_in_struct(); + void emit_texture_op(const Instruction &i, bool sparse) override; + void emit_instruction(const Instruction &instruction) override; + void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count) override; + void emit_buffer_block(const SPIRVariable &type) override; + void emit_push_constant_block(const SPIRVariable &var) override; + void emit_uniform(const SPIRVariable &var) override; + void emit_modern_uniform(const SPIRVariable &var); + void emit_legacy_uniform(const SPIRVariable &var); + void emit_specialization_constants_and_structs(); + void emit_composite_constants(); + void emit_fixup() override; + std::string builtin_to_glsl(BuiltIn builtin, StorageClass storage) override; + std::string layout_for_member(const SPIRType &type, uint32_t index) override; + std::string to_interpolation_qualifiers(const Bitset &flags) override; + std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_type, uint32_t id, uint32_t op0) override; + void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector &arglist) override; + + std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; + std::string to_sampler_expression(uint32_t id); + std::string to_resource_binding(const SPIRVariable &var); + std::string to_resource_binding_sampler(const SPIRVariable &var); + std::string to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t set); + std::string to_initializer_expression(const SPIRVariable &var) override; + void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; + void emit_access_chain(const Instruction &instruction); + void emit_load(const Instruction &instruction); + void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain); + void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain); + void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain); + void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector &composite_chain); + void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain); + void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value, + const SmallVector &composite_chain); + std::string write_access_chain_value(uint32_t value, const SmallVector &composite_chain, bool enclose); + void emit_store(const Instruction &instruction); + void emit_atomic(const uint32_t *ops, uint32_t length, 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; + void emit_rayquery_function(const char *commited, const char *candidate, const uint32_t *ops); + void emit_mesh_tasks(SPIRBlock &block) override; + void emit_geometry_stream_append(); + + const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override; + void replace_illegal_names() override; + + SPIRType::BaseType get_builtin_basetype(BuiltIn builtin, SPIRType::BaseType default_type) override; + + bool is_hlsl_force_storage_buffer_as_uav(ID id) const; + bool is_hidden_io_variable(const SPIRVariable &var) const; + + Options hlsl_options; + + // TODO: Refactor this to be more similar to MSL, maybe have some common system in place? + bool requires_op_fmod = false; + bool requires_fp16_packing = false; + bool requires_uint2_packing = false; + bool requires_explicit_fp16_packing = false; + bool requires_unorm8_packing = false; + bool requires_snorm8_packing = false; + bool requires_unorm16_packing = false; + bool requires_snorm16_packing = false; + bool requires_bitfield_insert = false; + bool requires_bitfield_extract = false; + bool requires_inverse_2x2 = false; + bool requires_inverse_3x3 = false; + bool requires_inverse_4x4 = false; + bool requires_scalar_reflect = false; + bool requires_scalar_refract = false; + bool requires_scalar_faceforward = false; + + struct TextureSizeVariants + { + // MSVC 2013 workaround. + TextureSizeVariants() + { + srv = 0; + for (auto &unorm : uav) + for (auto &u : unorm) + u = 0; + } + uint64_t srv; + uint64_t uav[3][4]; + } required_texture_size_variants; + + void require_texture_query_variant(uint32_t var_id); + void emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav, + const char *type_qualifier); + + enum TextureQueryVariantDim + { + Query1D = 0, + Query1DArray, + Query2D, + Query2DArray, + Query3D, + QueryBuffer, + QueryCube, + QueryCubeArray, + Query2DMS, + Query2DMSArray, + QueryDimCount + }; + + enum TextureQueryVariantType + { + QueryTypeFloat = 0, + QueryTypeInt = 16, + QueryTypeUInt = 32, + QueryTypeCount = 3 + }; + + enum BitcastType + { + TypeNormal, + TypePackUint2x32, + TypeUnpackUint64 + }; + + void analyze_meshlet_writes(); + void analyze_meshlet_writes(uint32_t func_id, uint32_t id_per_vertex, uint32_t id_per_primitive, + std::unordered_set &processed_func_ids); + + BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0); + + void emit_builtin_variables(); + bool require_output = false; + bool require_input = false; + SmallVector remap_vertex_attributes; + + uint32_t type_to_consumed_locations(const SPIRType &type) const; + + std::string to_semantic(uint32_t location, ExecutionModel em, StorageClass sc); + + uint32_t num_workgroups_builtin = 0; + HLSLBindingFlags resource_binding_flags = 0; + + // Custom root constant layout, which should be emitted + // when translating push constant ranges. + std::vector root_constants_layout; + + void validate_shader_model(); + + std::string get_unique_identifier(); + uint32_t unique_identifier_count = 0; + + std::unordered_map, InternalHasher> resource_bindings; + void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding); + + std::unordered_set force_uav_buffer_bindings; + + struct + { + uint32_t register_index = 0; + uint32_t register_space = 0; + bool explicit_binding = false; + bool used = false; + } base_vertex_info; + + // Returns true if the specified ID has a UserTypeGOOGLE decoration for StructuredBuffer or RWStructuredBuffer resources. + bool is_user_type_structured(uint32_t id) const override; + + std::vector composite_selection_workaround_types; + + std::string get_inner_entry_point_name() const; + + void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_msl.cpp b/thirdparty/SPIRV-Cross/spirv_msl.cpp new file mode 100644 index 000000000..df4811a05 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_msl.cpp @@ -0,0 +1,20918 @@ +/* + * Copyright 2016-2021 The Brenwill Workshop Ltd. + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_msl.hpp" +#include "GLSL.std.450.h" + +#include +#include +#include + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +static const uint32_t k_unknown_location = ~0u; +static const uint32_t k_unknown_component = ~0u; +static const char *force_inline = "static inline __attribute__((always_inline))"; + +CompilerMSL::CompilerMSL(std::vector spirv_) + : CompilerGLSL(std::move(spirv_)) +{ +} + +CompilerMSL::CompilerMSL(const uint32_t *ir_, size_t word_count) + : CompilerGLSL(ir_, word_count) +{ +} + +CompilerMSL::CompilerMSL(const ParsedIR &ir_) + : CompilerGLSL(ir_) +{ +} + +CompilerMSL::CompilerMSL(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) +{ +} + +void CompilerMSL::add_msl_shader_input(const MSLShaderInterfaceVariable &si) +{ + inputs_by_location[{si.location, si.component}] = si; + if (si.builtin != BuiltInMax && !inputs_by_builtin.count(si.builtin)) + inputs_by_builtin[si.builtin] = si; +} + +void CompilerMSL::add_msl_shader_output(const MSLShaderInterfaceVariable &so) +{ + outputs_by_location[{so.location, so.component}] = so; + if (so.builtin != BuiltInMax && !outputs_by_builtin.count(so.builtin)) + outputs_by_builtin[so.builtin] = so; +} + +void CompilerMSL::add_msl_resource_binding(const MSLResourceBinding &binding) +{ + StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding }; + resource_bindings[tuple] = { binding, false }; + + // If we might need to pad argument buffer members to positionally align + // arg buffer indexes, also maintain a lookup by argument buffer index. + if (msl_options.pad_argument_buffer_resources) + { + StageSetBinding arg_idx_tuple = { binding.stage, binding.desc_set, k_unknown_component }; + +#define ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(rez) \ + arg_idx_tuple.binding = binding.msl_##rez; \ + resource_arg_buff_idx_to_binding_number[arg_idx_tuple] = binding.binding + + switch (binding.basetype) + { + case SPIRType::Void: + case SPIRType::Boolean: + case SPIRType::SByte: + case SPIRType::UByte: + case SPIRType::Short: + case SPIRType::UShort: + case SPIRType::Int: + case SPIRType::UInt: + case SPIRType::Int64: + case SPIRType::UInt64: + case SPIRType::AtomicCounter: + case SPIRType::Half: + case SPIRType::Float: + case SPIRType::Double: + ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(buffer); + break; + case SPIRType::Image: + ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(texture); + break; + case SPIRType::Sampler: + ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(sampler); + break; + case SPIRType::SampledImage: + ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(texture); + ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP(sampler); + break; + default: + SPIRV_CROSS_THROW("Unexpected argument buffer resource base type. When padding argument buffer elements, " + "all descriptor set resources must be supplied with a base type by the app."); + } +#undef ADD_ARG_IDX_TO_BINDING_NUM_LOOKUP + } +} + +void CompilerMSL::add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index) +{ + SetBindingPair pair = { desc_set, binding }; + buffers_requiring_dynamic_offset[pair] = { index, 0, "" }; +} + +void CompilerMSL::add_inline_uniform_block(uint32_t desc_set, uint32_t binding) +{ + SetBindingPair pair = { desc_set, binding }; + inline_uniform_blocks.insert(pair); +} + +void CompilerMSL::add_discrete_descriptor_set(uint32_t desc_set) +{ + if (desc_set < kMaxArgumentBuffers) + argument_buffer_discrete_mask |= 1u << desc_set; +} + +void CompilerMSL::set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage) +{ + if (desc_set < kMaxArgumentBuffers) + { + if (device_storage) + argument_buffer_device_storage_mask |= 1u << desc_set; + else + argument_buffer_device_storage_mask &= ~(1u << desc_set); + } +} + +bool CompilerMSL::is_msl_shader_input_used(uint32_t location) +{ + // Don't report internal location allocations to app. + return location_inputs_in_use.count(location) != 0 && + location_inputs_in_use_fallback.count(location) == 0; +} + +bool CompilerMSL::is_msl_shader_output_used(uint32_t location) +{ + // Don't report internal location allocations to app. + return location_outputs_in_use.count(location) != 0 && + location_outputs_in_use_fallback.count(location) == 0; +} + +uint32_t CompilerMSL::get_automatic_builtin_input_location(BuiltIn builtin) const +{ + auto itr = builtin_to_automatic_input_location.find(builtin); + if (itr == builtin_to_automatic_input_location.end()) + return k_unknown_location; + else + return itr->second; +} + +uint32_t CompilerMSL::get_automatic_builtin_output_location(BuiltIn builtin) const +{ + auto itr = builtin_to_automatic_output_location.find(builtin); + if (itr == builtin_to_automatic_output_location.end()) + return k_unknown_location; + else + return itr->second; +} + +bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const +{ + StageSetBinding tuple = { model, desc_set, binding }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) && itr->second.second; +} + +bool CompilerMSL::is_var_runtime_size_array(const SPIRVariable &var) const +{ + auto& type = get_variable_data_type(var); + return is_runtime_size_array(type) && get_resource_array_size(type, var.self) == 0; +} + +// Returns the size of the array of resources used by the variable with the specified type and id. +// The size is first retrieved from the type, but in the case of runtime array sizing, +// the size is retrieved from the resource binding added using add_msl_resource_binding(). +uint32_t CompilerMSL::get_resource_array_size(const SPIRType &type, uint32_t id) const +{ + uint32_t array_size = to_array_size_literal(type); + + if (id == 0) + return array_size; + + // If we have argument buffers, we need to honor the ABI by using the correct array size + // from the layout. Only use shader declared size if we're not using argument buffers. + uint32_t desc_set = get_decoration(id, DecorationDescriptorSet); + if (!descriptor_set_is_argument_buffer(desc_set) && array_size) + return array_size; + + StageSetBinding tuple = { get_entry_point().model, desc_set, + get_decoration(id, DecorationBinding) }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) ? itr->second.first.count : array_size; +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexPrimary); +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding_secondary(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexSecondary); +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding_tertiary(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexTertiary); +} + +uint32_t CompilerMSL::get_automatic_msl_resource_binding_quaternary(uint32_t id) const +{ + return get_extended_decoration(id, SPIRVCrossDecorationResourceIndexQuaternary); +} + +void CompilerMSL::set_fragment_output_components(uint32_t location, uint32_t components) +{ + fragment_output_components[location] = components; +} + +bool CompilerMSL::builtin_translates_to_nonarray(BuiltIn builtin) const +{ + return (builtin == BuiltInSampleMask); +} + +void CompilerMSL::build_implicit_builtins() +{ + bool need_sample_pos = active_input_builtins.get(BuiltInSamplePosition); + bool need_vertex_params = capture_output_to_buffer && get_execution_model() == ExecutionModelVertex && + !msl_options.vertex_for_tessellation; + bool need_tesc_params = is_tesc_shader(); + bool need_tese_params = is_tese_shader() && msl_options.raw_buffer_tese_input; + bool need_subgroup_mask = + active_input_builtins.get(BuiltInSubgroupEqMask) || active_input_builtins.get(BuiltInSubgroupGeMask) || + active_input_builtins.get(BuiltInSubgroupGtMask) || active_input_builtins.get(BuiltInSubgroupLeMask) || + active_input_builtins.get(BuiltInSubgroupLtMask); + bool need_subgroup_ge_mask = !msl_options.is_ios() && (active_input_builtins.get(BuiltInSubgroupGeMask) || + active_input_builtins.get(BuiltInSubgroupGtMask)); + bool need_multiview = get_execution_model() == ExecutionModelVertex && !msl_options.view_index_from_device_index && + msl_options.multiview_layered_rendering && + (msl_options.multiview || active_input_builtins.get(BuiltInViewIndex)); + bool need_dispatch_base = + msl_options.dispatch_base && get_execution_model() == ExecutionModelGLCompute && + (active_input_builtins.get(BuiltInWorkgroupId) || active_input_builtins.get(BuiltInGlobalInvocationId)); + bool need_grid_params = get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation; + bool need_vertex_base_params = + need_grid_params && + (active_input_builtins.get(BuiltInVertexId) || active_input_builtins.get(BuiltInVertexIndex) || + active_input_builtins.get(BuiltInBaseVertex) || active_input_builtins.get(BuiltInInstanceId) || + active_input_builtins.get(BuiltInInstanceIndex) || active_input_builtins.get(BuiltInBaseInstance)); + bool need_local_invocation_index = + (msl_options.emulate_subgroups && active_input_builtins.get(BuiltInSubgroupId)) || is_mesh_shader() || + needs_workgroup_zero_init || needs_local_invocation_index; + bool need_workgroup_size = msl_options.emulate_subgroups && active_input_builtins.get(BuiltInNumSubgroups); + bool force_frag_depth_passthrough = + get_execution_model() == ExecutionModelFragment && !uses_explicit_early_fragment_test() && need_subpass_input && + msl_options.enable_frag_depth_builtin && msl_options.input_attachment_is_ds_attachment; + needs_point_size_output = + msl_options.enable_point_size_builtin && msl_options.enable_point_size_default && + entry_point_is_vertex(); + + if (need_subpass_input || need_sample_pos || need_subgroup_mask || need_vertex_params || need_tesc_params || + need_tese_params || need_multiview || need_dispatch_base || need_vertex_base_params || need_grid_params || + needs_sample_id || needs_subgroup_invocation_id || needs_subgroup_size || needs_helper_invocation || + has_additional_fixed_sample_mask() || need_local_invocation_index || need_workgroup_size || + force_frag_depth_passthrough || needs_point_size_output || is_mesh_shader()) + { + bool has_frag_coord = false; + bool has_sample_id = false; + bool has_vertex_idx = false; + bool has_base_vertex = false; + bool has_instance_idx = false; + bool has_base_instance = false; + bool has_invocation_id = false; + bool has_primitive_id = false; + bool has_subgroup_invocation_id = false; + bool has_subgroup_size = false; + bool has_view_idx = false; + bool has_layer = false; + bool has_helper_invocation = false; + bool has_local_invocation_index = false; + bool has_workgroup_size = false; + bool has_frag_depth = false; + bool has_point_size = false; + uint32_t workgroup_id_type = 0; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (var.storage != StorageClassInput && var.storage != StorageClassOutput) + return; + if (!interface_variable_exists_in_entry_point(var.self)) + return; + + auto &type = this->get(var.basetype); + if (needs_point_size_output && has_decoration(type.self, DecorationBlock)) + { + const auto member_count = static_cast(type.member_types.size()); + for (uint32_t i = 0; i < member_count; i++) + { + if (get_member_decoration(type.self, i, DecorationBuiltIn) == BuiltInPointSize) + { + has_point_size = true; + active_output_builtins.set(BuiltInPointSize); + break; + } + } + } + + if (!has_decoration(var.self, DecorationBuiltIn)) + return; + + BuiltIn builtin = ir.meta[var.self].decoration.builtin_type; + + if (var.storage == StorageClassOutput) + { + if (has_additional_fixed_sample_mask() && builtin == BuiltInSampleMask) + { + builtin_sample_mask_id = var.self; + mark_implicit_builtin(StorageClassOutput, BuiltInSampleMask, var.self); + does_shader_write_sample_mask = true; + } + + if (force_frag_depth_passthrough && builtin == BuiltInFragDepth) + { + builtin_frag_depth_id = var.self; + mark_implicit_builtin(StorageClassOutput, BuiltInFragDepth, var.self); + has_frag_depth = true; + } + } + + if (builtin == BuiltInPointSize) + { + has_point_size = true; + active_output_builtins.set(BuiltInPointSize); + } + + if (builtin == BuiltInPrimitivePointIndicesEXT || + builtin == BuiltInPrimitiveLineIndicesEXT || + builtin == BuiltInPrimitiveTriangleIndicesEXT) + { + builtin_mesh_primitive_indices_id = var.self; + } + + if (var.storage != StorageClassInput) + return; + + // Use Metal's native frame-buffer fetch API for subpass inputs. + if (need_subpass_input && (!msl_options.use_framebuffer_fetch_subpasses)) + { + switch (builtin) + { + case BuiltInFragCoord: + mark_implicit_builtin(StorageClassInput, BuiltInFragCoord, var.self); + builtin_frag_coord_id = var.self; + has_frag_coord = true; + break; + case BuiltInLayer: + if (!msl_options.arrayed_subpass_input || msl_options.multiview) + break; + mark_implicit_builtin(StorageClassInput, BuiltInLayer, var.self); + builtin_layer_id = var.self; + has_layer = true; + break; + case BuiltInViewIndex: + if (!msl_options.multiview) + break; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var.self); + builtin_view_idx_id = var.self; + has_view_idx = true; + break; + default: + break; + } + } + + if ((need_sample_pos || needs_sample_id) && builtin == BuiltInSampleId) + { + builtin_sample_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSampleId, var.self); + has_sample_id = true; + } + + if (need_vertex_params) + { + switch (builtin) + { + case BuiltInVertexIndex: + builtin_vertex_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInVertexIndex, var.self); + has_vertex_idx = true; + break; + case BuiltInBaseVertex: + builtin_base_vertex_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseVertex, var.self); + has_base_vertex = true; + break; + case BuiltInInstanceIndex: + builtin_instance_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var.self); + has_instance_idx = true; + break; + case BuiltInBaseInstance: + builtin_base_instance_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var.self); + has_base_instance = true; + break; + default: + break; + } + } + + if (need_tesc_params && builtin == BuiltInInvocationId) + { + builtin_invocation_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var.self); + has_invocation_id = true; + } + + if ((need_tesc_params || need_tese_params) && builtin == BuiltInPrimitiveId) + { + builtin_primitive_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var.self); + has_primitive_id = true; + } + + if (need_tese_params && builtin == BuiltInTessLevelOuter) + { + tess_level_outer_var_id = var.self; + } + + if (need_tese_params && builtin == BuiltInTessLevelInner) + { + tess_level_inner_var_id = var.self; + } + + if ((need_subgroup_mask || needs_subgroup_invocation_id) && builtin == BuiltInSubgroupLocalInvocationId) + { + builtin_subgroup_invocation_id_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupLocalInvocationId, var.self); + has_subgroup_invocation_id = true; + } + + if ((need_subgroup_ge_mask || needs_subgroup_size) && builtin == BuiltInSubgroupSize) + { + builtin_subgroup_size_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupSize, var.self); + has_subgroup_size = true; + } + + if (need_multiview) + { + switch (builtin) + { + case BuiltInInstanceIndex: + // The view index here is derived from the instance index. + builtin_instance_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var.self); + has_instance_idx = true; + break; + case BuiltInBaseInstance: + // If a non-zero base instance is used, we need to adjust for it when calculating the view index. + builtin_base_instance_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var.self); + has_base_instance = true; + break; + case BuiltInViewIndex: + builtin_view_idx_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var.self); + has_view_idx = true; + break; + default: + break; + } + } + + if (needs_helper_invocation && builtin == BuiltInHelperInvocation) + { + builtin_helper_invocation_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInHelperInvocation, var.self); + has_helper_invocation = true; + } + + if (need_local_invocation_index && builtin == BuiltInLocalInvocationIndex) + { + builtin_local_invocation_index_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInLocalInvocationIndex, var.self); + has_local_invocation_index = true; + } + + if (need_workgroup_size && builtin == BuiltInWorkgroupSize) + { + builtin_workgroup_size_id = var.self; + mark_implicit_builtin(StorageClassInput, BuiltInWorkgroupSize, var.self); + has_workgroup_size = true; + } + + // The base workgroup needs to have the same type and vector size + // as the workgroup or invocation ID, so keep track of the type that + // was used. + if (need_dispatch_base && workgroup_id_type == 0 && + (builtin == BuiltInWorkgroupId || builtin == BuiltInGlobalInvocationId)) + workgroup_id_type = var.basetype; + }); + + // Use Metal's native frame-buffer fetch API for subpass inputs. + if ((!has_frag_coord || (msl_options.multiview && !has_view_idx) || + (msl_options.arrayed_subpass_input && !msl_options.multiview && !has_layer)) && + (!msl_options.use_framebuffer_fetch_subpasses) && need_subpass_input) + { + if (!has_frag_coord) + { + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_id = offset; + uint32_t type_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create gl_FragCoord. + SPIRType vec4_type { OpTypeVector }; + vec4_type.basetype = SPIRType::Float; + vec4_type.width = 32; + vec4_type.vecsize = 4; + set(type_id, vec4_type); + + SPIRType vec4_type_ptr = vec4_type; + vec4_type_ptr.op = OpTypePointer; + vec4_type_ptr.pointer = true; + vec4_type_ptr.pointer_depth++; + vec4_type_ptr.parent_type = type_id; + vec4_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, vec4_type_ptr); + ptr_type.self = type_id; + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInFragCoord); + builtin_frag_coord_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInFragCoord, var_id); + } + + if (!has_layer && msl_options.arrayed_subpass_input && !msl_options.multiview) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_Layer. + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInLayer); + builtin_layer_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInLayer, var_id); + } + + if (!has_view_idx && msl_options.multiview) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_ViewIndex. + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInViewIndex); + builtin_view_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var_id); + } + } + + if (!has_sample_id && (need_sample_pos || needs_sample_id)) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_SampleID. + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSampleId); + builtin_sample_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInSampleId, var_id); + } + + if ((need_vertex_params && (!has_vertex_idx || !has_base_vertex || !has_instance_idx || !has_base_instance)) || + (need_multiview && (!has_instance_idx || !has_base_instance || !has_view_idx))) + { + uint32_t type_ptr_id = ir.increase_bound_by(1); + + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + if (need_vertex_params && !has_vertex_idx) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_VertexIndex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInVertexIndex); + builtin_vertex_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInVertexIndex, var_id); + } + + if (need_vertex_params && !has_base_vertex) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_BaseVertex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInBaseVertex); + builtin_base_vertex_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInBaseVertex, var_id); + } + + if (!has_instance_idx) // Needed by both multiview and tessellation + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_InstanceIndex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInInstanceIndex); + builtin_instance_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInInstanceIndex, var_id); + } + + if (!has_base_instance) // Needed by both multiview and tessellation + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_BaseInstance. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInBaseInstance); + builtin_base_instance_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInBaseInstance, var_id); + } + + if (need_multiview && !has_view_idx) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_ViewIndex. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInViewIndex); + builtin_view_idx_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInViewIndex, var_id); + } + } + + if (need_multiview) + { + // Multiview shaders are not allowed to write to gl_Layer, ostensibly because + // it is implicitly written from gl_ViewIndex, but we have to do that explicitly. + // Note that we can't just abuse gl_ViewIndex for this purpose: it's an input, but + // gl_Layer is an output in vertex-pipeline shaders. + uint32_t type_ptr_out_id = ir.increase_bound_by(2); + SPIRType uint_type_ptr_out = get_uint_type(); + uint_type_ptr_out.op = OpTypePointer; + uint_type_ptr_out.pointer = true; + uint_type_ptr_out.pointer_depth++; + uint_type_ptr_out.parent_type = get_uint_type_id(); + uint_type_ptr_out.storage = StorageClassOutput; + auto &ptr_out_type = set(type_ptr_out_id, uint_type_ptr_out); + ptr_out_type.self = get_uint_type_id(); + uint32_t var_id = type_ptr_out_id + 1; + set(var_id, type_ptr_out_id, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInLayer); + builtin_layer_id = var_id; + mark_implicit_builtin(StorageClassOutput, BuiltInLayer, var_id); + } + + if ((need_tesc_params && (msl_options.multi_patch_workgroup || !has_invocation_id || !has_primitive_id)) || + (need_tese_params && !has_primitive_id) || need_grid_params) + { + uint32_t type_ptr_id = ir.increase_bound_by(1); + + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + if ((need_tesc_params && msl_options.multi_patch_workgroup) || need_grid_params) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_GlobalInvocationID. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInGlobalInvocationId); + builtin_invocation_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInGlobalInvocationId, var_id); + } + else if (need_tesc_params && !has_invocation_id) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_InvocationID. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInInvocationId); + builtin_invocation_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var_id); + } + + if ((need_tesc_params || need_tese_params) && !has_primitive_id) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_PrimitiveID. + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInPrimitiveId); + builtin_primitive_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var_id); + } + + if (need_grid_params) + { + uint32_t var_id = ir.increase_bound_by(1); + + set(var_id, build_extended_vector_type(get_uint_type_id(), 3), StorageClassInput); + set_extended_decoration(var_id, SPIRVCrossDecorationBuiltInStageInputSize); + get_entry_point().interface_variables.push_back(var_id); + set_name(var_id, "spvStageInputSize"); + builtin_stage_input_size_id = var_id; + } + } + + if (!has_subgroup_invocation_id && (need_subgroup_mask || needs_subgroup_invocation_id)) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_SubgroupInvocationID. + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSubgroupLocalInvocationId); + builtin_subgroup_invocation_id_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupLocalInvocationId, var_id); + } + + if (!has_subgroup_size && (need_subgroup_ge_mask || needs_subgroup_size)) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_SubgroupSize. + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSubgroupSize); + builtin_subgroup_size_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInSubgroupSize, var_id); + } + + if (need_dispatch_base || need_vertex_base_params) + { + if (workgroup_id_type == 0) + workgroup_id_type = build_extended_vector_type(get_uint_type_id(), 3); + uint32_t var_id; + if (msl_options.supports_msl_version(1, 2)) + { + // If we have MSL 1.2, we can (ab)use the [[grid_origin]] builtin + // to convey this information and save a buffer slot. + uint32_t offset = ir.increase_bound_by(1); + var_id = offset; + + set(var_id, workgroup_id_type, StorageClassInput); + set_extended_decoration(var_id, SPIRVCrossDecorationBuiltInDispatchBase); + get_entry_point().interface_variables.push_back(var_id); + } + else + { + // Otherwise, we need to fall back to a good ol' fashioned buffer. + uint32_t offset = ir.increase_bound_by(2); + var_id = offset; + uint32_t type_id = offset + 1; + + SPIRType var_type = get(workgroup_id_type); + var_type.storage = StorageClassUniform; + set(type_id, var_type); + + set(var_id, type_id, StorageClassUniform); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, ~(5u)); + set_decoration(var_id, DecorationBinding, msl_options.indirect_params_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, + msl_options.indirect_params_buffer_index); + } + set_name(var_id, "spvDispatchBase"); + builtin_dispatch_base_id = var_id; + } + + if (has_additional_fixed_sample_mask() && !does_shader_write_sample_mask) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t var_id = offset + 1; + + // Create gl_SampleMask. + SPIRType uint_type_ptr_out = get_uint_type(); + uint_type_ptr_out.op = OpTypePointer; + uint_type_ptr_out.pointer = true; + uint_type_ptr_out.pointer_depth++; + uint_type_ptr_out.parent_type = get_uint_type_id(); + uint_type_ptr_out.storage = StorageClassOutput; + + auto &ptr_out_type = set(offset, uint_type_ptr_out); + ptr_out_type.self = get_uint_type_id(); + set(var_id, offset, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInSampleMask); + builtin_sample_mask_id = var_id; + mark_implicit_builtin(StorageClassOutput, BuiltInSampleMask, var_id); + } + + if (!has_helper_invocation && needs_helper_invocation) + { + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_id = offset; + uint32_t type_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create gl_HelperInvocation. + SPIRType bool_type { OpTypeBool }; + bool_type.basetype = SPIRType::Boolean; + bool_type.width = 8; + bool_type.vecsize = 1; + set(type_id, bool_type); + + SPIRType bool_type_ptr_in = bool_type; + bool_type_ptr_in.op = OpTypePointer; + bool_type_ptr_in.pointer = true; + bool_type_ptr_in.pointer_depth++; + bool_type_ptr_in.parent_type = type_id; + bool_type_ptr_in.storage = StorageClassInput; + + auto &ptr_in_type = set(type_ptr_id, bool_type_ptr_in); + ptr_in_type.self = type_id; + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInHelperInvocation); + builtin_helper_invocation_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInHelperInvocation, var_id); + } + + if (need_local_invocation_index && !has_local_invocation_index) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create gl_LocalInvocationIndex. + SPIRType uint_type_ptr = get_uint_type(); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = get_uint_type_id(); + uint_type_ptr.storage = StorageClassInput; + + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = get_uint_type_id(); + set(var_id, type_ptr_id, StorageClassInput); + set_decoration(var_id, DecorationBuiltIn, BuiltInLocalInvocationIndex); + builtin_local_invocation_index_id = var_id; + mark_implicit_builtin(StorageClassInput, BuiltInLocalInvocationIndex, var_id); + } + + if (need_workgroup_size && !has_workgroup_size) + { + auto &execution = get_entry_point(); + // First, check if the workgroup size _constant_ were defined. + // If it were, we don't need to do--in fact, shouldn't do--anything. + builtin_workgroup_size_id = execution.workgroup_size.constant; + if (builtin_workgroup_size_id == 0) + { + uint32_t var_id = ir.increase_bound_by(1); + + // Create gl_WorkgroupSize. + uint32_t type_id = build_extended_vector_type(get_uint_type_id(), 3); + // If we have LocalSize or LocalSizeId, use those to define the workgroup size. + if (execution.flags.get(ExecutionModeLocalSizeId)) + { + const SPIRConstant *init[] = { &get(execution.workgroup_size.id_x), + &get(execution.workgroup_size.id_y), + &get(execution.workgroup_size.id_z) }; + bool specialized = init[0]->specialization || init[1]->specialization || init[2]->specialization; + set(var_id, type_id, init, 3, specialized); + execution.workgroup_size.constant = var_id; + } + else if (execution.flags.get(ExecutionModeLocalSize)) + { + uint32_t offset = ir.increase_bound_by(3); + const SPIRConstant *init[] = { + &set(offset, get_uint_type_id(), execution.workgroup_size.x, false), + &set(offset + 1, get_uint_type_id(), execution.workgroup_size.y, false), + &set(offset + 2, get_uint_type_id(), execution.workgroup_size.z, false) + }; + set(var_id, type_id, init, 3, false); + execution.workgroup_size.constant = var_id; + } + else + { + uint32_t type_ptr_id = ir.increase_bound_by(1); + SPIRType uint_type_ptr = get(type_id); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = type_id; + uint_type_ptr.storage = StorageClassInput; + + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = type_id; + set(var_id, type_ptr_id, StorageClassInput); + mark_implicit_builtin(StorageClassInput, BuiltInWorkgroupSize, var_id); + } + set_decoration(var_id, DecorationBuiltIn, BuiltInWorkgroupSize); + builtin_workgroup_size_id = var_id; + } + } + + if (!has_frag_depth && force_frag_depth_passthrough) + { + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_id = offset; + uint32_t type_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create gl_FragDepth + SPIRType float_type { OpTypeFloat }; + float_type.basetype = SPIRType::Float; + float_type.width = 32; + float_type.vecsize = 1; + set(type_id, float_type); + + SPIRType float_type_ptr_in = float_type; + float_type_ptr_in.op = OpTypePointer; + float_type_ptr_in.pointer = true; + float_type_ptr_in.pointer_depth++; + float_type_ptr_in.parent_type = type_id; + float_type_ptr_in.storage = StorageClassOutput; + + auto &ptr_in_type = set(type_ptr_id, float_type_ptr_in); + ptr_in_type.self = type_id; + set(var_id, type_ptr_id, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInFragDepth); + builtin_frag_depth_id = var_id; + mark_implicit_builtin(StorageClassOutput, BuiltInFragDepth, var_id); + active_output_builtins.set(BuiltInFragDepth); + } + + if (!has_point_size && needs_point_size_output) + { + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_id = offset; + uint32_t type_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create gl_PointSize + SPIRType float_type { OpTypeFloat }; + float_type.basetype = SPIRType::Float; + float_type.width = 32; + float_type.vecsize = 1; + set(type_id, float_type); + + SPIRType float_type_ptr_in = float_type; + float_type_ptr_in.op = OpTypePointer; + float_type_ptr_in.pointer = true; + float_type_ptr_in.pointer_depth++; + float_type_ptr_in.parent_type = type_id; + float_type_ptr_in.storage = StorageClassOutput; + + auto &ptr_in_type = set(type_ptr_id, float_type_ptr_in); + ptr_in_type.self = type_id; + set(var_id, type_ptr_id, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInPointSize); + mark_implicit_builtin(StorageClassOutput, BuiltInPointSize, var_id); + } + } + + if (needs_swizzle_buffer_def) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvSwizzleConstants"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, kSwizzleBufferBinding); + set_decoration(var_id, DecorationBinding, msl_options.swizzle_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.swizzle_buffer_index); + swizzle_buffer_id = var_id; + } + + if (needs_buffer_size_buffer()) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvBufferSizeConstants"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, kBufferSizeBufferBinding); + set_decoration(var_id, DecorationBinding, msl_options.buffer_size_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.buffer_size_buffer_index); + buffer_size_buffer_id = var_id; + } + + if (needs_view_mask_buffer()) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvViewMask"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, ~(4u)); + set_decoration(var_id, DecorationBinding, msl_options.view_mask_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, msl_options.view_mask_buffer_index); + view_mask_buffer_id = var_id; + } + + if (!buffers_requiring_dynamic_offset.empty()) + { + uint32_t var_id = build_constant_uint_array_pointer(); + set_name(var_id, "spvDynamicOffsets"); + // This should never match anything. + set_decoration(var_id, DecorationDescriptorSet, ~(5u)); + set_decoration(var_id, DecorationBinding, msl_options.dynamic_offsets_buffer_index); + set_extended_decoration(var_id, SPIRVCrossDecorationResourceIndexPrimary, + msl_options.dynamic_offsets_buffer_index); + dynamic_offsets_buffer_id = var_id; + } + + // If we're returning a struct from a vertex-like entry point, we must return a position attribute. + bool need_position = (get_execution_model() == ExecutionModelVertex || is_tese_shader()) && + !capture_output_to_buffer && !get_is_rasterization_disabled() && + !msl_options.auto_disable_rasterization && + !active_output_builtins.get(BuiltInPosition); + + if (need_position) + { + // If we can get away with returning void from entry point, we don't need to care. + // If there is at least one other stage output, we need to return [[position]], + // so we need to create one if it doesn't appear in the SPIR-V. Before adding the + // implicit variable, check if it actually exists already, but just has not been used + // or initialized, and if so, mark it as active, and do not create the implicit variable. + bool has_output = false; + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self)) + { + has_output = true; + + // Check if the var is the Position builtin + if (has_decoration(var.self, DecorationBuiltIn) && get_decoration(var.self, DecorationBuiltIn) == BuiltInPosition) + active_output_builtins.set(BuiltInPosition); + + // If the var is a struct, check if any members is the Position builtin + auto &var_type = get_variable_element_type(var); + if (var_type.basetype == SPIRType::Struct) + { + auto mbr_cnt = var_type.member_types.size(); + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + auto builtin = BuiltInMax; + bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); + if (is_builtin && builtin == BuiltInPosition) + active_output_builtins.set(BuiltInPosition); + } + } + } + }); + need_position = has_output && !active_output_builtins.get(BuiltInPosition); + } + else if (!active_output_builtins.get(BuiltInPosition) && msl_options.auto_disable_rasterization) + { + is_rasterization_disabled = true; + } + + if (need_position) + { + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_id = offset; + uint32_t type_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create gl_Position. + SPIRType vec4_type { OpTypeVector }; + vec4_type.basetype = SPIRType::Float; + vec4_type.width = 32; + vec4_type.vecsize = 4; + set(type_id, vec4_type); + + SPIRType vec4_type_ptr = vec4_type; + vec4_type_ptr.op = OpTypePointer; + vec4_type_ptr.pointer = true; + vec4_type_ptr.pointer_depth++; + vec4_type_ptr.parent_type = type_id; + vec4_type_ptr.storage = StorageClassOutput; + auto &ptr_type = set(type_ptr_id, vec4_type_ptr); + ptr_type.self = type_id; + + set(var_id, type_ptr_id, StorageClassOutput); + set_decoration(var_id, DecorationBuiltIn, BuiltInPosition); + mark_implicit_builtin(StorageClassOutput, BuiltInPosition, var_id); + } + + if (is_mesh_shader()) + { + uint32_t offset = ir.increase_bound_by(2); + uint32_t type_ptr_id = offset; + uint32_t var_id = offset + 1; + + // Create variable to store meshlet size. + uint32_t type_id = build_extended_vector_type(get_uint_type_id(), 2); + SPIRType uint_type_ptr = get(type_id); + uint_type_ptr.op = OpTypePointer; + uint_type_ptr.pointer = true; + uint_type_ptr.pointer_depth++; + uint_type_ptr.parent_type = type_id; + uint_type_ptr.storage = StorageClassWorkgroup; + + auto &ptr_type = set(type_ptr_id, uint_type_ptr); + ptr_type.self = type_id; + set(var_id, type_ptr_id, StorageClassWorkgroup); + set_name(var_id, "spvMeshSizes"); + builtin_mesh_sizes_id = var_id; + } + + if (get_execution_model() == ExecutionModelTaskEXT) + { + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_id = offset; + uint32_t type_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + SPIRType mesh_grid_type { OpTypeStruct }; + mesh_grid_type.basetype = SPIRType::MeshGridProperties; + set(type_id, mesh_grid_type); + + SPIRType mesh_grid_type_ptr = mesh_grid_type; + mesh_grid_type_ptr.op = OpTypePointer; + mesh_grid_type_ptr.pointer = true; + mesh_grid_type_ptr.pointer_depth++; + mesh_grid_type_ptr.parent_type = type_id; + mesh_grid_type_ptr.storage = StorageClassOutput; + + auto &ptr_in_type = set(type_ptr_id, mesh_grid_type_ptr); + ptr_in_type.self = type_id; + set(var_id, type_ptr_id, StorageClassOutput); + set_name(var_id, "spvMgp"); + builtin_task_grid_id = var_id; + } +} + +// Checks if the specified builtin variable (e.g. gl_InstanceIndex) is marked as active. +// If not, it marks it as active and forces a recompilation. +// This might be used when the optimization of inactive builtins was too optimistic (e.g. when "spvOut" is emitted). +void CompilerMSL::ensure_builtin(StorageClass storage, BuiltIn builtin) +{ + Bitset *active_builtins = nullptr; + switch (storage) + { + case StorageClassInput: + active_builtins = &active_input_builtins; + break; + + case StorageClassOutput: + active_builtins = &active_output_builtins; + break; + + default: + break; + } + + // At this point, the specified builtin variable must have already been declared in the entry point. + // If not, mark as active and force recompile. + if (active_builtins != nullptr && !active_builtins->get(builtin)) + { + active_builtins->set(builtin); + force_recompile(); + } +} + +void CompilerMSL::mark_implicit_builtin(StorageClass storage, BuiltIn builtin, uint32_t id) +{ + Bitset *active_builtins = nullptr; + switch (storage) + { + case StorageClassInput: + active_builtins = &active_input_builtins; + break; + + case StorageClassOutput: + active_builtins = &active_output_builtins; + break; + + default: + break; + } + + assert(active_builtins != nullptr); + active_builtins->set(builtin); + + auto &var = get_entry_point().interface_variables; + if (find(begin(var), end(var), VariableID(id)) == end(var)) + var.push_back(id); +} + +uint32_t CompilerMSL::build_constant_uint_array_pointer() +{ + uint32_t offset = ir.increase_bound_by(3); + uint32_t type_ptr_id = offset; + uint32_t type_ptr_ptr_id = offset + 1; + uint32_t var_id = offset + 2; + + // Create a buffer to hold extra data, including the swizzle constants. + SPIRType uint_type_pointer = get_uint_type(); + uint_type_pointer.op = OpTypePointer; + uint_type_pointer.pointer = true; + uint_type_pointer.pointer_depth++; + uint_type_pointer.parent_type = get_uint_type_id(); + uint_type_pointer.storage = StorageClassUniform; + set(type_ptr_id, uint_type_pointer); + set_decoration(type_ptr_id, DecorationArrayStride, 4); + + SPIRType uint_type_pointer2 = uint_type_pointer; + uint_type_pointer2.pointer_depth++; + uint_type_pointer2.parent_type = type_ptr_id; + set(type_ptr_ptr_id, uint_type_pointer2); + + set(var_id, type_ptr_ptr_id, StorageClassUniformConstant); + return var_id; +} + +static string create_sampler_address(const char *prefix, MSLSamplerAddress addr) +{ + switch (addr) + { + case MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE: + return join(prefix, "address::clamp_to_edge"); + case MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO: + return join(prefix, "address::clamp_to_zero"); + case MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER: + return join(prefix, "address::clamp_to_border"); + case MSL_SAMPLER_ADDRESS_REPEAT: + return join(prefix, "address::repeat"); + case MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT: + return join(prefix, "address::mirrored_repeat"); + default: + SPIRV_CROSS_THROW("Invalid sampler addressing mode."); + } +} + +SPIRType &CompilerMSL::get_stage_in_struct_type() +{ + auto &si_var = get(stage_in_var_id); + return get_variable_data_type(si_var); +} + +SPIRType &CompilerMSL::get_stage_out_struct_type() +{ + auto &so_var = get(stage_out_var_id); + return get_variable_data_type(so_var); +} + +SPIRType &CompilerMSL::get_patch_stage_in_struct_type() +{ + auto &si_var = get(patch_stage_in_var_id); + return get_variable_data_type(si_var); +} + +SPIRType &CompilerMSL::get_patch_stage_out_struct_type() +{ + auto &so_var = get(patch_stage_out_var_id); + return get_variable_data_type(so_var); +} + +std::string CompilerMSL::get_tess_factor_struct_name() +{ + if (is_tessellating_triangles()) + return "MTLTriangleTessellationFactorsHalf"; + return "MTLQuadTessellationFactorsHalf"; +} + +SPIRType &CompilerMSL::get_uint_type() +{ + return get(get_uint_type_id()); +} + +uint32_t CompilerMSL::get_uint_type_id() +{ + if (uint_type_id != 0) + return uint_type_id; + + uint_type_id = ir.increase_bound_by(1); + + SPIRType type { OpTypeInt }; + type.basetype = SPIRType::UInt; + type.width = 32; + set(uint_type_id, type); + return uint_type_id; +} + +void CompilerMSL::emit_entry_point_declarations() +{ + // FIXME: Get test coverage here ... + // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries + declare_complex_constant_arrays(); + + // Emit constexpr samplers here. + for (auto &samp : constexpr_samplers_by_id) + { + auto &var = get(samp.first); + auto &type = get(var.basetype); + if (type.basetype == SPIRType::Sampler) + add_resource_name(samp.first); + + SmallVector args; + auto &s = samp.second; + + if (s.coord != MSL_SAMPLER_COORD_NORMALIZED) + args.push_back("coord::pixel"); + + if (s.min_filter == s.mag_filter) + { + if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("filter::linear"); + } + else + { + if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("min_filter::linear"); + if (s.mag_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("mag_filter::linear"); + } + + switch (s.mip_filter) + { + case MSL_SAMPLER_MIP_FILTER_NONE: + // Default + break; + case MSL_SAMPLER_MIP_FILTER_NEAREST: + args.push_back("mip_filter::nearest"); + break; + case MSL_SAMPLER_MIP_FILTER_LINEAR: + args.push_back("mip_filter::linear"); + break; + default: + SPIRV_CROSS_THROW("Invalid mip filter."); + } + + if (s.s_address == s.t_address && s.s_address == s.r_address) + { + if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("", s.s_address)); + } + else + { + if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("s_", s.s_address)); + if (s.t_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("t_", s.t_address)); + if (s.r_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("r_", s.r_address)); + } + + if (s.compare_enable) + { + switch (s.compare_func) + { + case MSL_SAMPLER_COMPARE_FUNC_ALWAYS: + args.push_back("compare_func::always"); + break; + case MSL_SAMPLER_COMPARE_FUNC_NEVER: + args.push_back("compare_func::never"); + break; + case MSL_SAMPLER_COMPARE_FUNC_EQUAL: + args.push_back("compare_func::equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL: + args.push_back("compare_func::not_equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_LESS: + args.push_back("compare_func::less"); + break; + case MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL: + args.push_back("compare_func::less_equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_GREATER: + args.push_back("compare_func::greater"); + break; + case MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL: + args.push_back("compare_func::greater_equal"); + break; + default: + SPIRV_CROSS_THROW("Invalid sampler compare function."); + } + } + + if (s.s_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER || s.t_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER || + s.r_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER) + { + switch (s.border_color) + { + case MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK: + args.push_back("border_color::opaque_black"); + break; + case MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE: + args.push_back("border_color::opaque_white"); + break; + case MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK: + args.push_back("border_color::transparent_black"); + break; + default: + SPIRV_CROSS_THROW("Invalid sampler border color."); + } + } + + if (s.anisotropy_enable) + args.push_back(join("max_anisotropy(", s.max_anisotropy, ")")); + if (s.lod_clamp_enable) + { + args.push_back(join("lod_clamp(", format_float(s.lod_clamp_min), ", ", format_float(s.lod_clamp_max), ")")); + } + + // If we would emit no arguments, then omit the parentheses entirely. Otherwise, + // we'll wind up with a "most vexing parse" situation. + if (args.empty()) + statement("constexpr sampler ", + type.basetype == SPIRType::SampledImage ? to_sampler_expression(samp.first) : to_name(samp.first), + ";"); + else + statement("constexpr sampler ", + type.basetype == SPIRType::SampledImage ? to_sampler_expression(samp.first) : to_name(samp.first), + "(", merge(args), ");"); + } + + // Emit dynamic buffers here. + for (auto &dynamic_buffer : buffers_requiring_dynamic_offset) + { + if (!dynamic_buffer.second.var_id) + { + // Could happen if no buffer was used at requested binding point. + continue; + } + + const auto &var = get(dynamic_buffer.second.var_id); + uint32_t var_id = var.self; + const auto &type = get_variable_data_type(var); + + add_local_variable_name(var.self); + string name = to_name(var.self); + + uint32_t desc_set = get_decoration(var.self, DecorationDescriptorSet); + uint32_t arg_id = argument_buffer_ids[desc_set]; + uint32_t base_index = dynamic_buffer.second.base_index; + + if (is_array(type)) + { + is_using_builtin_array = true; + statement(get_variable_address_space(var), " ", type_to_glsl(type), "* ", to_restrict(var_id, true), name, + type_to_array_glsl(type, var_id), " ="); + + uint32_t array_size = get_resource_array_size(type, var_id); + if (array_size == 0) + SPIRV_CROSS_THROW("Size of runtime array with dynamic offset could not be determined from resource bindings."); + + begin_scope(); + + for (uint32_t i = 0; i < array_size; i++) + { + statement("(", get_variable_address_space(var), " ", type_to_glsl(type), "* ", + to_restrict(var_id, false), ")((", get_variable_address_space(var), " char* ", + to_restrict(var_id, false), ")", to_name(arg_id), ".", dynamic_buffer.second.mbr_name, + "[", i, "]", " + ", to_name(dynamic_offsets_buffer_id), "[", base_index + i, "]),"); + } + + end_scope_decl(); + statement_no_indent(""); + is_using_builtin_array = false; + } + else + { + statement(get_variable_address_space(var), " auto& ", to_restrict(var_id, true), name, " = *(", + get_variable_address_space(var), " ", type_to_glsl(type), "* ", to_restrict(var_id, false), ")((", + get_variable_address_space(var), " char* ", to_restrict(var_id, false), ")", to_name(arg_id), ".", + dynamic_buffer.second.mbr_name, " + ", to_name(dynamic_offsets_buffer_id), "[", base_index, "]);"); + } + } + + bool has_runtime_array_declaration = false; + for (SPIRVariable *arg : entry_point_bindings) + { + const auto &var = *arg; + const auto &type = get_variable_data_type(var); + const auto &buffer_type = get_variable_element_type(var); + + // This has already been added as a resource name. + const string name = to_name(var.self); + + if (is_var_runtime_size_array(var)) + { + if (msl_options.argument_buffers_tier < Options::ArgumentBuffersTier::Tier2) + { + SPIRV_CROSS_THROW("Unsized array of descriptors requires argument buffer tier 2"); + } + + string resource_name; + + if (descriptor_set_is_argument_buffer(get_decoration(var.self, DecorationDescriptorSet))) + { + resource_name = ir.meta[var.self].decoration.qualified_alias; + } + else + { + bool is_aliased = std::find_if(buffer_aliases_discrete.begin(), buffer_aliases_discrete.end(), + [&](uint32_t id) { return var.self == id; }) != buffer_aliases_discrete.end(); + + uint32_t desc_set = get_decoration(var.self, DecorationDescriptorSet); + uint32_t desc_binding = get_decoration(var.self, DecorationBinding); + + if (is_aliased) + resource_name = join("spvBufferAliasSet", desc_set, "Binding", desc_binding); + else + resource_name = join("spvDescriptorSet", desc_set, "Binding", desc_binding); + } + + switch (type.basetype) + { + case SPIRType::Image: + case SPIRType::Sampler: + case SPIRType::AccelerationStructure: + statement("spvDescriptorArray<", type_to_glsl(buffer_type, var.self), "> ", name, " {", resource_name, "};"); + break; + case SPIRType::SampledImage: + statement("spvDescriptorArray<", type_to_glsl(buffer_type, var.self), "> ", name, " {", resource_name, "};"); + // Unsupported with argument buffer for now. + statement("spvDescriptorArray ", name, "Smplr {", resource_name, "Smplr};"); + break; + case SPIRType::Struct: + statement("spvDescriptorArray<", get_variable_address_space(var), " ", type_to_glsl(buffer_type), "*> ", + name, " {", resource_name, "};"); + break; + default: + break; + } + has_runtime_array_declaration = true; + } + else if (!type.array.empty() && type.basetype == SPIRType::Struct) + { + // Emit only buffer arrays here. + statement(get_variable_address_space(var), " ", type_to_glsl(buffer_type), "* ", + to_restrict(var.self, true), name, "[] ="); + begin_scope(); + uint32_t array_size = get_resource_array_size(type, var.self); + for (uint32_t i = 0; i < array_size; ++i) + statement(name, "_", i, ","); + end_scope_decl(); + statement_no_indent(""); + } + } + + if (has_runtime_array_declaration) + statement_no_indent(""); + + // Emit buffer aliases here. + for (auto &var_id : buffer_aliases_discrete) + { + const auto &var = get(var_id); + + // We already declare this alias in a different way. + if (is_var_runtime_size_array(var)) + continue; + + const auto &type = get_variable_data_type(var); + auto addr_space = get_variable_address_space(var); + // This resource name has already been added. + auto name = to_name(var_id); + + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + uint32_t desc_binding = get_decoration(var_id, DecorationBinding); + auto alias_name = join("spvBufferAliasSet", desc_set, "Binding", desc_binding); + + statement(addr_space, " auto& ", to_restrict(var_id, true), + name, + " = *(", addr_space, " ", type_to_glsl(type), "*)", alias_name, ";"); + } + // Discrete descriptors are processed in entry point emission every compiler iteration. + buffer_aliases_discrete.clear(); + + // Emit disabled fragment outputs. + std::sort(disabled_frag_outputs.begin(), disabled_frag_outputs.end()); + for (uint32_t var_id : disabled_frag_outputs) + { + auto &var = get(var_id); + add_local_variable_name(var_id); + statement(CompilerGLSL::variable_decl(var), ";"); + var.deferred_declaration = false; + } + + // Holds SetMeshOutputsEXT information. Threadgroup since first thread wins. + if (processing_entry_point && is_mesh_shader()) + statement("threadgroup uint2 spvMeshSizes;"); +} + +string CompilerMSL::compile() +{ + replace_illegal_entry_point_names(); + ir.fixup_reserved_names(); + + // Do not deal with GLES-isms like precision, older extensions and such. + options.vulkan_semantics = true; + options.es = false; + options.version = 450; + backend.null_pointer_literal = "nullptr"; + backend.float_literal_suffix = false; + backend.uint32_t_literal_suffix = true; + backend.int16_t_literal_suffix = ""; + backend.uint16_t_literal_suffix = ""; + backend.basic_int_type = "int"; + backend.basic_uint_type = "uint"; + backend.basic_int8_type = "char"; + backend.basic_uint8_type = "uchar"; + backend.basic_int16_type = "short"; + backend.basic_uint16_type = "ushort"; + backend.boolean_mix_function = "select"; + backend.printf_function = "os_log_default.log"; + backend.swizzle_is_function = false; + backend.shared_is_implied = false; + backend.use_initializer_list = true; + backend.use_typed_initializer_list = true; + backend.native_row_major_matrix = false; + backend.unsized_array_supported = false; + backend.can_declare_arrays_inline = false; + backend.allow_truncated_access_chain = true; + backend.comparison_image_samples_scalar = true; + backend.native_pointers = true; + backend.nonuniform_qualifier = ""; + backend.support_small_type_sampling_result = true; + backend.force_merged_mesh_block = false; + backend.force_gl_in_out_block = false; + backend.supports_empty_struct = true; + backend.support_64bit_switch = true; + backend.boolean_in_struct_remapped_type = SPIRType::Short; + + // Allow Metal to use the array template unless we force it off. + backend.can_return_array = !msl_options.force_native_arrays; + backend.array_is_value_type = !msl_options.force_native_arrays; + // Arrays which are part of buffer objects are never considered to be value types (just plain C-style). + backend.array_is_value_type_in_buffer_blocks = false; + backend.support_pointer_to_pointer = true; + backend.implicit_c_integer_promotion_rules = true; + backend.supports_spec_constant_array_size = false; + + capture_output_to_buffer = msl_options.capture_output_to_buffer; + is_rasterization_disabled = msl_options.disable_rasterization || capture_output_to_buffer; + + if (is_mesh_shader() && !get_entry_point().flags.get(ExecutionModeOutputPoints)) + msl_options.enable_point_size_builtin = false; + + // Initialize array here rather than constructor, MSVC 2013 workaround. + for (auto &id : next_metal_resource_ids) + id = 0; + + fixup_anonymous_struct_names(); + fixup_type_alias(); + replace_illegal_names(); + if (get_execution_model() == ExecutionModelMeshEXT) + { + // Emit proxy entry-point for the sake of copy-pass + emit_mesh_entry_point(); + } + sync_entry_point_aliases_and_names(); + + build_function_control_flow_graphs_and_analyze(); + update_active_builtins(); + analyze_image_and_sampler_usage(); + analyze_sampled_image_usage(); + analyze_interlocked_resource_usage(); + analyze_workgroup_variables(); + preprocess_op_codes(); + build_implicit_builtins(); + + if (needs_manual_helper_invocation_updates() && needs_helper_invocation) + { + string builtin_helper_invocation = builtin_to_glsl(BuiltInHelperInvocation, StorageClassInput); + string discard_expr = join(builtin_helper_invocation, " = true, discard_fragment()"); + if (msl_options.force_fragment_with_side_effects_execution) + discard_expr = join("!", builtin_helper_invocation, " ? (", discard_expr, ") : (void)0"); + backend.discard_literal = discard_expr; + backend.demote_literal = discard_expr; + } + else + { + backend.discard_literal = "discard_fragment()"; + backend.demote_literal = "discard_fragment()"; + } + + fixup_image_load_store_access(); + + set_enabled_interface_variables(get_active_interface_variables()); + if (msl_options.force_active_argument_buffer_resources) + activate_argument_buffer_resources(); + + if (swizzle_buffer_id) + add_active_interface_variable(swizzle_buffer_id); + if (buffer_size_buffer_id) + add_active_interface_variable(buffer_size_buffer_id); + if (view_mask_buffer_id) + add_active_interface_variable(view_mask_buffer_id); + if (dynamic_offsets_buffer_id) + add_active_interface_variable(dynamic_offsets_buffer_id); + if (builtin_layer_id) + add_active_interface_variable(builtin_layer_id); + if (builtin_dispatch_base_id && !msl_options.supports_msl_version(1, 2)) + add_active_interface_variable(builtin_dispatch_base_id); + if (builtin_sample_mask_id) + add_active_interface_variable(builtin_sample_mask_id); + if (builtin_frag_depth_id) + add_active_interface_variable(builtin_frag_depth_id); + + // Create structs to hold input, output and uniform variables. + // Do output first to ensure out. is declared at top of entry function. + qual_pos_var_name = ""; + if (is_mesh_shader()) + { + fixup_implicit_builtin_block_names(get_execution_model()); + } + else + { + stage_out_var_id = add_interface_block(StorageClassOutput); + patch_stage_out_var_id = add_interface_block(StorageClassOutput, true); + stage_in_var_id = add_interface_block(StorageClassInput); + } + + if (is_tese_shader()) + patch_stage_in_var_id = add_interface_block(StorageClassInput, true); + + if (is_tesc_shader()) + stage_out_ptr_var_id = add_interface_block_pointer(stage_out_var_id, StorageClassOutput); + if (is_tessellation_shader()) + stage_in_ptr_var_id = add_interface_block_pointer(stage_in_var_id, StorageClassInput); + + if (is_mesh_shader()) + { + mesh_out_per_vertex = add_meshlet_block(false); + mesh_out_per_primitive = add_meshlet_block(true); + } + + // Metal vertex functions that define no output must disable rasterization and return void. + if (!stage_out_var_id) + is_rasterization_disabled = true; + + // Convert the use of global variables to recursively-passed function parameters + localize_global_variables(); + extract_global_variables_from_functions(); + + // Mark any non-stage-in structs to be tightly packed. + mark_packable_structs(); + reorder_type_alias(); + + // Add fixup hooks required by shader inputs and outputs. This needs to happen before + // the loop, so the hooks aren't added multiple times. + fix_up_shader_inputs_outputs(); + + // If we are using argument buffers, we create argument buffer structures for them here. + // These buffers will be used in the entry point, not the individual resources. + if (msl_options.argument_buffers) + { + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("Argument buffers can only be used with MSL 2.0 and up."); + analyze_argument_buffers(); + } + + uint32_t pass_count = 0; + do + { + reset(pass_count); + + // Start bindings at zero. + next_metal_resource_index_buffer = 0; + next_metal_resource_index_texture = 0; + next_metal_resource_index_sampler = 0; + for (auto &id : next_metal_resource_ids) + id = 0; + + // Move constructor for this type is broken on GCC 4.9 ... + buffer.reset(); + + emit_header(); + emit_custom_templates(); + emit_custom_functions(); + emit_specialization_constants_and_structs(); + emit_resources(); + emit_function(get(ir.default_entry_point), Bitset()); + + pass_count++; + } while (is_forcing_recompilation()); + + return buffer.str(); +} + +// Register the need to output any custom functions. +void CompilerMSL::preprocess_op_codes() +{ + OpCodePreprocessor preproc(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), preproc); + + suppress_missing_prototypes = preproc.suppress_missing_prototypes; + + if (preproc.uses_atomics) + { + add_header_line("#include "); + add_pragma_line("#pragma clang diagnostic ignored \"-Wunused-variable\"", false); + } + + // Before MSL 2.1 (2.2 for textures), Metal vertex functions that write to + // resources must disable rasterization and return void. + if ((preproc.uses_buffer_write && !msl_options.supports_msl_version(2, 1)) || + (preproc.uses_image_write && !msl_options.supports_msl_version(2, 2))) + is_rasterization_disabled = true; + + // FIXME: This currently does not consider BDA side effects, so we cannot deduce const device for BDA. + has_descriptor_side_effects_buffer = preproc.uses_buffer_write; + + // Tessellation control shaders are run as compute functions in Metal, and so + // must capture their output to a buffer. + if (is_tesc_shader() || (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation)) + { + is_rasterization_disabled = true; + capture_output_to_buffer = true; + } + + if (preproc.needs_local_invocation_index) + needs_local_invocation_index = true; + if (preproc.needs_subgroup_invocation_id) + needs_subgroup_invocation_id = true; + if (preproc.needs_subgroup_size) + needs_subgroup_size = true; + // build_implicit_builtins() hasn't run yet, and in fact, this needs to execute + // before then so that gl_SampleID will get added; so we also need to check if + // that function would add gl_FragCoord. + if (preproc.needs_sample_id || msl_options.force_sample_rate_shading || + (is_sample_rate() && (active_input_builtins.get(BuiltInFragCoord) || + (need_subpass_input_ms && !msl_options.use_framebuffer_fetch_subpasses)))) + needs_sample_id = true; + if (preproc.needs_helper_invocation || active_input_builtins.get(BuiltInHelperInvocation)) + needs_helper_invocation = true; + + // OpKill is removed by the parser, so we need to identify those by inspecting + // blocks. + ir.for_each_typed_id([&preproc](uint32_t, SPIRBlock &block) { + if (block.terminator == SPIRBlock::Kill) + preproc.uses_discard = true; + }); + + // Fragment shaders that both write to storage resources and discard fragments + // need checks on the writes, to work around Metal allowing these writes despite + // the fragment being dead. We also require to force Metal to execute fragment + // shaders instead of being prematurely discarded. + if (preproc.uses_discard && (preproc.uses_buffer_write || preproc.uses_image_write)) + { + bool should_enable = (msl_options.check_discarded_frag_stores || msl_options.force_fragment_with_side_effects_execution); + frag_shader_needs_discard_checks |= msl_options.check_discarded_frag_stores; + needs_helper_invocation |= should_enable; + // Fragment discard store checks imply manual HelperInvocation updates. + msl_options.manual_helper_invocation_updates |= should_enable; + } + + if (is_intersection_query()) + { + add_header_line("#if __METAL_VERSION__ >= 230"); + add_header_line("#include "); + add_header_line("using namespace metal::raytracing;"); + add_header_line("#endif"); + } + + if (preproc.uses_cooperative_matrix) + { + if (!msl_options.supports_msl_version(3, 1)) + SPIRV_CROSS_THROW("Cooperative matrices require MSL 3.1 or later."); + add_header_line("#include "); + } +} + +// Move the Private and Workgroup global variables to the entry function. +// Non-constant variables cannot have global scope in Metal. +void CompilerMSL::localize_global_variables() +{ + auto &entry_func = get(ir.default_entry_point); + auto iter = global_variables.begin(); + while (iter != global_variables.end()) + { + uint32_t v_id = *iter; + auto &var = get(v_id); + if (var.storage == StorageClassPrivate || var.storage == StorageClassWorkgroup || + var.storage == StorageClassTaskPayloadWorkgroupEXT) + { + if (!variable_is_lut(var)) + entry_func.add_local_variable(v_id); + iter = global_variables.erase(iter); + } + else if (var.storage == StorageClassOutput && is_mesh_shader()) + { + entry_func.add_local_variable(v_id); + iter = global_variables.erase(iter); + } + else + iter++; + } +} + +// For any global variable accessed directly by a function, +// extract that variable and add it as an argument to that function. +void CompilerMSL::extract_global_variables_from_functions() +{ + // Uniforms + unordered_set global_var_ids; + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + // Some builtins resolve directly to a function call which does not need any declared variables. + // Skip these. + if (var.storage == StorageClassInput && has_decoration(var.self, DecorationBuiltIn)) + { + auto bi_type = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + if (bi_type == BuiltInHelperInvocation && !needs_manual_helper_invocation_updates()) + return; + if (bi_type == BuiltInHelperInvocation && needs_manual_helper_invocation_updates()) + { + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.3 on iOS."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.1 on macOS."); + // Make sure this is declared and initialized. + // Force this to have the proper name. + set_name(var.self, builtin_to_glsl(BuiltInHelperInvocation, StorageClassInput)); + auto &entry_func = this->get(ir.default_entry_point); + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + entry_func.fixup_hooks_in.push_back([this, &var]() + { statement(to_name(var.self), " = simd_is_helper_thread();"); }); + } + } + + if (var.storage == StorageClassInput || var.storage == StorageClassOutput || + var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || + var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer) + { + global_var_ids.insert(var.self); + } + }); + + // Local vars that are declared in the main function and accessed directly by a function + auto &entry_func = get(ir.default_entry_point); + for (auto &var : entry_func.local_variables) + if (get(var).storage != StorageClassFunction) + global_var_ids.insert(var); + + std::set added_arg_ids; + unordered_set processed_func_ids; + extract_global_variables_from_function(ir.default_entry_point, added_arg_ids, global_var_ids, processed_func_ids); +} + +// MSL does not support the use of global variables for shader input content. +// For any global variable accessed directly by the specified function, extract that variable, +// add it as an argument to that function, and the arg to the added_arg_ids collection. +void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::set &added_arg_ids, + unordered_set &global_var_ids, + unordered_set &processed_func_ids) +{ + // Avoid processing a function more than once + if (processed_func_ids.find(func_id) != processed_func_ids.end()) + { + // Return function global variables + added_arg_ids = function_global_vars[func_id]; + return; + } + + processed_func_ids.insert(func_id); + + auto &func = get(func_id); + + // Recursively establish global args added to functions on which we depend. + for (auto block : func.blocks) + { + auto &b = get(block); + for (auto &i : b.ops) + { + auto ops = stream(i); + auto op = static_cast(i.op); + + switch (op) + { + case OpLoad: + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + case OpArrayLength: + { + uint32_t base_id = ops[2]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + + // Use Metal's native frame-buffer fetch API for subpass inputs. + auto &type = get(ops[0]); + if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && + (!msl_options.use_framebuffer_fetch_subpasses)) + { + // Implicitly reads gl_FragCoord. + assert(builtin_frag_coord_id != 0); + added_arg_ids.insert(builtin_frag_coord_id); + if (msl_options.multiview) + { + // Implicitly reads gl_ViewIndex. + assert(builtin_view_idx_id != 0); + added_arg_ids.insert(builtin_view_idx_id); + } + else if (msl_options.arrayed_subpass_input) + { + // Implicitly reads gl_Layer. + assert(builtin_layer_id != 0); + added_arg_ids.insert(builtin_layer_id); + } + } + + break; + } + + case OpFunctionCall: + { + // First see if any of the function call args are globals + for (uint32_t arg_idx = 3; arg_idx < i.length; arg_idx++) + { + uint32_t arg_id = ops[arg_idx]; + if (global_var_ids.find(arg_id) != global_var_ids.end()) + added_arg_ids.insert(arg_id); + } + + // Then recurse into the function itself to extract globals used internally in the function + uint32_t inner_func_id = ops[2]; + std::set inner_func_args; + extract_global_variables_from_function(inner_func_id, inner_func_args, global_var_ids, + processed_func_ids); + added_arg_ids.insert(inner_func_args.begin(), inner_func_args.end()); + break; + } + + case OpStore: + { + uint32_t base_id = ops[0]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + { + added_arg_ids.insert(base_id); + + if (msl_options.input_attachment_is_ds_attachment && base_id == builtin_frag_depth_id) + writes_to_depth = true; + } + + uint32_t rvalue_id = ops[1]; + if (global_var_ids.find(rvalue_id) != global_var_ids.end()) + added_arg_ids.insert(rvalue_id); + + if (needs_frag_discard_checks()) + added_arg_ids.insert(builtin_helper_invocation_id); + + break; + } + + case OpSelect: + { + uint32_t base_id = ops[3]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + base_id = ops[4]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + break; + } + + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicStore: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicFAddEXT: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + case OpImageWrite: + { + if (needs_frag_discard_checks()) + added_arg_ids.insert(builtin_helper_invocation_id); + uint32_t ptr = 0; + if (op == OpAtomicStore || op == OpImageWrite) + ptr = ops[0]; + else + ptr = ops[2]; + if (global_var_ids.find(ptr) != global_var_ids.end()) + added_arg_ids.insert(ptr); + break; + } + + // Emulate texture2D atomic operations + case OpImageTexelPointer: + { + // When using the pointer, we need to know which variable it is actually loaded from. + uint32_t base_id = ops[2]; + auto *var = maybe_get_backing_variable(base_id); + if (var) + { + if (atomic_image_vars_emulated.count(var->self) && + !get(var->basetype).array.empty()) + { + SPIRV_CROSS_THROW( + "Cannot emulate array of storage images with atomics. Use MSL 3.1 for native support."); + } + + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + } + break; + } + + case OpExtInst: + { + uint32_t extension_set = ops[2]; + if (get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(ops[3]); + switch (op_450) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + // For these, we really need the stage-in block. It is theoretically possible to pass the + // interpolant object, but a) doing so would require us to create an entirely new variable + // with Interpolant type, and b) if we have a struct or array, handling all the members and + // elements could get unwieldy fast. + added_arg_ids.insert(stage_in_var_id); + break; + } + + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + uint32_t base_id = ops[5]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + break; + } + + default: + break; + } + } + break; + } + + case OpGroupNonUniformInverseBallot: + { + added_arg_ids.insert(builtin_subgroup_invocation_id_id); + break; + } + + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + { + added_arg_ids.insert(builtin_subgroup_size_id); + break; + } + + case OpGroupNonUniformBallotBitCount: + { + auto operation = static_cast(ops[3]); + switch (operation) + { + case GroupOperationReduce: + added_arg_ids.insert(builtin_subgroup_size_id); + break; + case GroupOperationInclusiveScan: + case GroupOperationExclusiveScan: + added_arg_ids.insert(builtin_subgroup_invocation_id_id); + break; + default: + break; + } + break; + } + + case OpGroupNonUniformRotateKHR: + { + // Add the correct invocation ID for calculating clustered rotate case. + if (i.length > 5) + added_arg_ids.insert(static_cast(evaluate_constant_u32(ops[2])) == ScopeSubgroup + ? builtin_subgroup_invocation_id_id : builtin_local_invocation_index_id); + break; + } + + case OpGroupNonUniformFAdd: + case OpGroupNonUniformFMul: + case OpGroupNonUniformFMin: + case OpGroupNonUniformFMax: + case OpGroupNonUniformIAdd: + case OpGroupNonUniformIMul: + case OpGroupNonUniformSMin: + case OpGroupNonUniformSMax: + case OpGroupNonUniformUMin: + case OpGroupNonUniformUMax: + case OpGroupNonUniformBitwiseAnd: + case OpGroupNonUniformBitwiseOr: + case OpGroupNonUniformBitwiseXor: + case OpGroupNonUniformLogicalAnd: + case OpGroupNonUniformLogicalOr: + case OpGroupNonUniformLogicalXor: + if ((get_execution_model() != ExecutionModelFragment || msl_options.supports_msl_version(2, 2)) && + ops[3] == GroupOperationClusteredReduce) + added_arg_ids.insert(builtin_subgroup_invocation_id_id); + break; + + case OpDemoteToHelperInvocation: + if (needs_manual_helper_invocation_updates() && needs_helper_invocation) + added_arg_ids.insert(builtin_helper_invocation_id); + break; + + case OpIsHelperInvocationEXT: + if (needs_manual_helper_invocation_updates()) + added_arg_ids.insert(builtin_helper_invocation_id); + break; + + case OpRayQueryInitializeKHR: + case OpRayQueryProceedKHR: + case OpRayQueryTerminateKHR: + case OpRayQueryGenerateIntersectionKHR: + case OpRayQueryConfirmIntersectionKHR: + { + // Ray query accesses memory directly, need check pass down object if using Private storage class. + uint32_t base_id = ops[0]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + break; + } + + case OpRayQueryGetRayTMinKHR: + case OpRayQueryGetRayFlagsKHR: + case OpRayQueryGetWorldRayOriginKHR: + case OpRayQueryGetWorldRayDirectionKHR: + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: + case OpRayQueryGetIntersectionTypeKHR: + case OpRayQueryGetIntersectionTKHR: + case OpRayQueryGetIntersectionInstanceCustomIndexKHR: + case OpRayQueryGetIntersectionInstanceIdKHR: + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: + case OpRayQueryGetIntersectionGeometryIndexKHR: + case OpRayQueryGetIntersectionPrimitiveIndexKHR: + case OpRayQueryGetIntersectionBarycentricsKHR: + case OpRayQueryGetIntersectionFrontFaceKHR: + case OpRayQueryGetIntersectionObjectRayDirectionKHR: + case OpRayQueryGetIntersectionObjectRayOriginKHR: + case OpRayQueryGetIntersectionObjectToWorldKHR: + case OpRayQueryGetIntersectionWorldToObjectKHR: + { + // Ray query accesses memory directly, need check pass down object if using Private storage class. + uint32_t base_id = ops[2]; + if (global_var_ids.find(base_id) != global_var_ids.end()) + added_arg_ids.insert(base_id); + break; + } + + case OpSetMeshOutputsEXT: + { + if (builtin_local_invocation_index_id != 0) + added_arg_ids.insert(builtin_local_invocation_index_id); + if (builtin_mesh_sizes_id != 0) + added_arg_ids.insert(builtin_mesh_sizes_id); + break; + } + + default: + break; + } + + if (needs_manual_helper_invocation_updates() && b.terminator == SPIRBlock::Kill && + needs_helper_invocation) + added_arg_ids.insert(builtin_helper_invocation_id); + + // TODO: Add all other operations which can affect memory. + // We should consider a more unified system here to reduce boiler-plate. + // This kind of analysis is done in several places ... + } + + if (b.terminator == SPIRBlock::EmitMeshTasks && builtin_task_grid_id != 0) + added_arg_ids.insert(builtin_task_grid_id); + } + + function_global_vars[func_id] = added_arg_ids; + + // Add the global variables as arguments to the function + if (func_id != ir.default_entry_point) + { + bool control_point_added_in = false; + bool control_point_added_out = false; + bool patch_added_in = false; + bool patch_added_out = false; + + for (uint32_t arg_id : added_arg_ids) + { + auto &var = get(arg_id); + uint32_t type_id = var.basetype; + auto *p_type = &get(type_id); + BuiltIn bi_type = BuiltIn(get_decoration(arg_id, DecorationBuiltIn)); + + bool is_patch = has_decoration(arg_id, DecorationPatch) || is_patch_block(*p_type); + bool is_block = has_decoration(p_type->self, DecorationBlock); + bool is_control_point_storage = + !is_patch && ((is_tessellation_shader() && var.storage == StorageClassInput) || + (is_tesc_shader() && var.storage == StorageClassOutput)); + bool is_patch_block_storage = is_patch && is_block && var.storage == StorageClassOutput; + bool is_builtin = is_builtin_variable(var); + bool variable_is_stage_io = + !is_builtin || bi_type == BuiltInPosition || bi_type == BuiltInPointSize || + bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance || + p_type->basetype == SPIRType::Struct; + bool is_redirected_to_global_stage_io = (is_control_point_storage || is_patch_block_storage) && + variable_is_stage_io; + + // If output is masked it is not considered part of the global stage IO interface. + if (is_redirected_to_global_stage_io && var.storage == StorageClassOutput) + is_redirected_to_global_stage_io = !is_stage_output_variable_masked(var); + + if (is_redirected_to_global_stage_io) + { + // Tessellation control shaders see inputs and per-point outputs as arrays. + // Similarly, tessellation evaluation shaders see per-point inputs as arrays. + // We collected them into a structure; we must pass the array of this + // structure to the function. + std::string name; + if (is_patch) + name = var.storage == StorageClassInput ? patch_stage_in_var_name : patch_stage_out_var_name; + else + name = var.storage == StorageClassInput ? "gl_in" : "gl_out"; + + if (var.storage == StorageClassOutput && has_decoration(p_type->self, DecorationBlock)) + { + // If we're redirecting a block, we might still need to access the original block + // variable if we're masking some members. + for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(p_type->member_types.size()); mbr_idx++) + { + if (is_stage_output_block_member_masked(var, mbr_idx, true)) + { + func.add_parameter(var.basetype, var.self, true); + break; + } + } + } + + if (var.storage == StorageClassInput) + { + auto &added_in = is_patch ? patch_added_in : control_point_added_in; + if (added_in) + continue; + arg_id = is_patch ? patch_stage_in_var_id : stage_in_ptr_var_id; + added_in = true; + } + else if (var.storage == StorageClassOutput) + { + auto &added_out = is_patch ? patch_added_out : control_point_added_out; + if (added_out) + continue; + arg_id = is_patch ? patch_stage_out_var_id : stage_out_ptr_var_id; + added_out = true; + } + + type_id = get(arg_id).basetype; + uint32_t next_id = ir.increase_bound_by(1); + func.add_parameter(type_id, next_id, true); + set(next_id, type_id, StorageClassFunction, 0, arg_id); + + set_name(next_id, name); + if (is_tese_shader() && msl_options.raw_buffer_tese_input && var.storage == StorageClassInput) + set_decoration(next_id, DecorationNonWritable); + } + else if (is_builtin && is_mesh_shader()) + { + uint32_t next_id = ir.increase_bound_by(1); + func.add_parameter(type_id, next_id, true); + auto &v = set(next_id, type_id, StorageClassFunction, 0, arg_id); + v.storage = StorageClassWorkgroup; + + // Ensure the existing variable has a valid name and the new variable has all the same meta info + if (ir.meta[arg_id].decoration.builtin) + { + set_name(arg_id, builtin_to_glsl(bi_type, var.storage)); + } + else + { + set_name(arg_id, ensure_valid_name(to_name(arg_id), "v")); + } + ir.meta[next_id] = ir.meta[arg_id]; + } + else if (is_builtin && has_decoration(p_type->self, DecorationBlock)) + { + // Get the pointee type + type_id = get_pointee_type_id(type_id); + p_type = &get(type_id); + + uint32_t mbr_idx = 0; + for (auto &mbr_type_id : p_type->member_types) + { + BuiltIn builtin = BuiltInMax; + 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_ids = ir.increase_bound_by(2); + uint32_t ptr_type_id = next_ids + 0; + uint32_t var_id = next_ids + 1; + + // Make sure we have an actual pointer type, + // so that we will get the appropriate address space when declaring these builtins. + auto &ptr = set(ptr_type_id, get(mbr_type_id)); + ptr.self = mbr_type_id; + ptr.storage = var.storage; + ptr.pointer = true; + ptr.pointer_depth++; + ptr.parent_type = mbr_type_id; + + func.add_parameter(mbr_type_id, var_id, true); + set(var_id, ptr_type_id, StorageClassFunction); + ir.meta[var_id].decoration = ir.meta[type_id].members[mbr_idx]; + } + mbr_idx++; + } + } + else + { + uint32_t next_id = ir.increase_bound_by(1); + func.add_parameter(type_id, next_id, true); + set(next_id, type_id, StorageClassFunction, 0, arg_id); + + // Ensure the new variable has all the same meta info + ir.meta[next_id] = ir.meta[arg_id]; + } + } + } +} + +// For all variables that are some form of non-input-output interface block, mark that all the structs +// that are recursively contained within the type referenced by that variable should be packed tightly. +void CompilerMSL::mark_packable_structs() +{ + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (var.storage != StorageClassFunction && !is_hidden_variable(var)) + { + auto &type = this->get(var.basetype); + if (type.pointer && + (type.storage == StorageClassUniform || type.storage == StorageClassUniformConstant || + type.storage == StorageClassPushConstant || type.storage == StorageClassStorageBuffer) && + (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))) + mark_as_packable(type); + } + + if (var.storage == StorageClassWorkgroup) + { + auto *type = &this->get(var.basetype); + if (type->basetype == SPIRType::Struct) + mark_as_workgroup_struct(*type); + } + }); + + // Physical storage buffer pointers can appear outside of the context of a variable, if the address + // is calculated from a ulong or uvec2 and cast to a pointer, so check if they need to be packed too. + ir.for_each_typed_id([&](uint32_t, SPIRType &type) { + if (type.basetype == SPIRType::Struct && type.pointer && type.storage == StorageClassPhysicalStorageBuffer) + mark_as_packable(type); + }); +} + +// If the specified type is a struct, it and any nested structs +// are marked as packable with the SPIRVCrossDecorationBufferBlockRepacked decoration, +void CompilerMSL::mark_as_packable(SPIRType &type) +{ + // If this is not the base type (eg. it's a pointer or array), tunnel down + if (type.parent_type) + { + mark_as_packable(get(type.parent_type)); + return; + } + + // Handle possible recursion when a struct contains a pointer to its own type nested somewhere. + if (type.basetype == SPIRType::Struct && !has_extended_decoration(type.self, SPIRVCrossDecorationBufferBlockRepacked)) + { + set_extended_decoration(type.self, SPIRVCrossDecorationBufferBlockRepacked); + + // Recurse + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + uint32_t mbr_type_id = type.member_types[mbr_idx]; + auto &mbr_type = get(mbr_type_id); + mark_as_packable(mbr_type); + if (mbr_type.type_alias) + { + auto &mbr_type_alias = get(mbr_type.type_alias); + mark_as_packable(mbr_type_alias); + } + } + } +} + +// If the specified type is a struct, it and any nested structs +// are marked as used with workgroup storage using the SPIRVCrossDecorationWorkgroupStruct decoration. +void CompilerMSL::mark_as_workgroup_struct(SPIRType &type) +{ + // If this is not the base type (eg. it's a pointer or array), tunnel down + if (type.parent_type) + { + mark_as_workgroup_struct(get(type.parent_type)); + return; + } + + // Handle possible recursion when a struct contains a pointer to its own type nested somewhere. + if (type.basetype == SPIRType::Struct && !has_extended_decoration(type.self, SPIRVCrossDecorationWorkgroupStruct)) + { + set_extended_decoration(type.self, SPIRVCrossDecorationWorkgroupStruct); + + // Recurse + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + uint32_t mbr_type_id = type.member_types[mbr_idx]; + auto &mbr_type = get(mbr_type_id); + mark_as_workgroup_struct(mbr_type); + if (mbr_type.type_alias) + { + auto &mbr_type_alias = get(mbr_type.type_alias); + mark_as_workgroup_struct(mbr_type_alias); + } + } + } +} + +// If a shader input exists at the location, it is marked as being used by this shader +void CompilerMSL::mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, + StorageClass storage, bool fallback) +{ + uint32_t count = type_to_location_count(type); + switch (storage) + { + case StorageClassInput: + for (uint32_t i = 0; i < count; i++) + { + location_inputs_in_use.insert(location + i); + if (fallback) + location_inputs_in_use_fallback.insert(location + i); + } + break; + case StorageClassOutput: + for (uint32_t i = 0; i < count; i++) + { + location_outputs_in_use.insert(location + i); + if (fallback) + location_outputs_in_use_fallback.insert(location + i); + } + break; + default: + return; + } +} + +uint32_t CompilerMSL::get_target_components_for_fragment_location(uint32_t location) const +{ + auto itr = fragment_output_components.find(location); + if (itr == end(fragment_output_components)) + return 4; + else + return itr->second; +} + +uint32_t CompilerMSL::build_extended_vector_type(uint32_t type_id, uint32_t components, SPIRType::BaseType basetype) +{ + assert(components > 1); + uint32_t new_type_id = ir.increase_bound_by(1); + const auto *p_old_type = &get(type_id); + const SPIRType *old_ptr_t = nullptr; + const SPIRType *old_array_t = nullptr; + + if (is_pointer(*p_old_type)) + { + old_ptr_t = p_old_type; + p_old_type = &get_pointee_type(*old_ptr_t); + } + + if (is_array(*p_old_type)) + { + old_array_t = p_old_type; + p_old_type = &get_type(old_array_t->parent_type); + } + + auto *type = &set(new_type_id, *p_old_type); + assert(is_scalar(*type) || is_vector(*type)); + type->op = OpTypeVector; + type->vecsize = components; + if (basetype != SPIRType::Unknown) + type->basetype = basetype; + type->self = new_type_id; + // We want parent type to point to the scalar type. + type->parent_type = is_scalar(*p_old_type) ? TypeID(p_old_type->self) : p_old_type->parent_type; + assert(is_scalar(get(type->parent_type))); + type->array.clear(); + type->array_size_literal.clear(); + type->pointer = false; + + if (old_array_t) + { + uint32_t array_type_id = ir.increase_bound_by(1); + type = &set(array_type_id, *type); + type->op = OpTypeArray; + type->parent_type = new_type_id; + type->array = old_array_t->array; + type->array_size_literal = old_array_t->array_size_literal; + new_type_id = array_type_id; + } + + if (old_ptr_t) + { + uint32_t ptr_type_id = ir.increase_bound_by(1); + type = &set(ptr_type_id, *type); + type->op = OpTypePointer; + type->parent_type = new_type_id; + type->storage = old_ptr_t->storage; + type->pointer = true; + type->pointer_depth++; + new_type_id = ptr_type_id; + } + + return new_type_id; +} + +uint32_t CompilerMSL::build_msl_interpolant_type(uint32_t type_id, bool is_noperspective) +{ + uint32_t new_type_id = ir.increase_bound_by(1); + SPIRType &type = set(new_type_id, get(type_id)); + type.basetype = SPIRType::Interpolant; + type.parent_type = type_id; + // In Metal, the pull-model interpolant type encodes perspective-vs-no-perspective in the type itself. + // Add this decoration so we know which argument to pass to the template. + if (is_noperspective) + set_decoration(new_type_id, DecorationNoPerspective); + return new_type_id; +} + +bool CompilerMSL::add_component_variable_to_interface_block(StorageClass storage, const std::string &ib_var_ref, + SPIRVariable &var, + const SPIRType &type, + InterfaceBlockMeta &meta) +{ + // Deal with Component decorations. + const InterfaceBlockMeta::LocationMeta *location_meta = nullptr; + uint32_t location = ~0u; + if (has_decoration(var.self, DecorationLocation)) + { + location = get_decoration(var.self, DecorationLocation); + auto location_meta_itr = meta.location_meta.find(location); + if (location_meta_itr != end(meta.location_meta)) + location_meta = &location_meta_itr->second; + } + + // Check if we need to pad fragment output to match a certain number of components. + if (location_meta) + { + bool pad_fragment_output = has_decoration(var.self, DecorationLocation) && + msl_options.pad_fragment_output_components && + get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput; + + auto &entry_func = get(ir.default_entry_point); + uint32_t start_component = get_decoration(var.self, DecorationComponent); + uint32_t type_components = type.vecsize; + uint32_t num_components = location_meta->num_components; + + if (pad_fragment_output) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + num_components = max(num_components, get_target_components_for_fragment_location(locn)); + } + + // We have already declared an IO block member as m_location_N. + // Just emit an early-declared variable and fixup as needed. + // Arrays need to be unrolled here since each location might need a different number of components. + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + + if (var.storage == StorageClassInput) + { + entry_func.fixup_hooks_in.push_back([=, &type, &var]() { + if (!type.array.empty()) + { + uint32_t array_size = to_array_size_literal(type); + for (uint32_t loc_off = 0; loc_off < array_size; loc_off++) + { + statement(to_name(var.self), "[", loc_off, "]", " = ", ib_var_ref, + ".m_location_", location + loc_off, + vector_swizzle(type_components, start_component), ";"); + } + } + else + { + statement(to_name(var.self), " = ", ib_var_ref, ".m_location_", location, + vector_swizzle(type_components, start_component), ";"); + } + }); + } + else + { + entry_func.fixup_hooks_out.push_back([=, &type, &var]() { + if (!type.array.empty()) + { + uint32_t array_size = to_array_size_literal(type); + for (uint32_t loc_off = 0; loc_off < array_size; loc_off++) + { + statement(ib_var_ref, ".m_location_", location + loc_off, + vector_swizzle(type_components, start_component), " = ", + to_name(var.self), "[", loc_off, "];"); + } + } + else + { + statement(ib_var_ref, ".m_location_", location, + vector_swizzle(type_components, start_component), " = ", to_name(var.self), ";"); + } + }); + } + return true; + } + else + return false; +} + +void CompilerMSL::add_plain_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta) +{ + bool is_builtin = is_builtin_variable(var); + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + bool is_flat = has_decoration(var.self, DecorationFlat); + bool is_noperspective = has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = has_decoration(var.self, DecorationCentroid); + bool is_sample = has_decoration(var.self, DecorationSample); + + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + uint32_t type_id = ensure_correct_builtin_type(var.basetype, builtin); + var.basetype = type_id; + + type_id = get_pointee_type_id(var.basetype); + if (meta.strip_array && is_array(get(type_id))) + type_id = get(type_id).parent_type; + auto &type = get(type_id); + uint32_t target_components = 0; + uint32_t type_components = type.vecsize; + + bool padded_output = false; + bool padded_input = false; + uint32_t start_component = 0; + + auto &entry_func = get(ir.default_entry_point); + + if (add_component_variable_to_interface_block(storage, ib_var_ref, var, type, meta)) + return; + + bool pad_fragment_output = has_decoration(var.self, DecorationLocation) && + msl_options.pad_fragment_output_components && + get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput; + + if (pad_fragment_output) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + target_components = get_target_components_for_fragment_location(locn); + if (type_components < target_components) + { + // Make a new type here. + type_id = build_extended_vector_type(type_id, target_components); + padded_output = true; + } + } + + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(type_id, is_noperspective)); + else + ib_type.member_types.push_back(type_id); + + // Give the member a name + string mbr_name = ensure_valid_name(to_expression(var.self), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // Update the original variable reference to include the structure reference + string qual_var_name = ib_var_ref + "." + mbr_name; + // If using pull-model interpolation, need to add a call to the correct interpolation method. + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + { + if (is_centroid) + qual_var_name += ".interpolate_at_centroid()"; + else if (is_sample) + qual_var_name += join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + qual_var_name += ".interpolate_at_center()"; + } + + if (padded_output || padded_input) + { + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + + if (padded_output) + { + entry_func.fixup_hooks_out.push_back([=, &var]() { + statement(qual_var_name, vector_swizzle(type_components, start_component), " = ", to_name(var.self), + ";"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=, &var]() { + statement(to_name(var.self), " = ", qual_var_name, vector_swizzle(type_components, start_component), + ";"); + }); + } + } + else if (!meta.strip_array) + ir.meta[var.self].decoration.qualified_alias = qual_var_name; + + if (var.storage == StorageClassOutput && var.initializer != ID(0)) + { + if (padded_output || padded_input) + { + entry_func.fixup_hooks_in.push_back( + [=, &var]() { statement(to_name(var.self), " = ", to_expression(var.initializer), ";"); }); + } + else + { + if (meta.strip_array) + { + entry_func.fixup_hooks_in.push_back([=, &var]() { + uint32_t index = get_extended_decoration(var.self, SPIRVCrossDecorationInterfaceMemberIndex); + auto invocation = to_tesc_invocation_id(); + statement(to_expression(stage_out_ptr_var_id), "[", + invocation, "].", + to_member_name(ib_type, index), " = ", to_expression(var.initializer), "[", + invocation, "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=, &var]() { + statement(qual_var_name, " = ", to_expression(var.initializer), ";"); + }); + } + } + } + + // Copy the variable location from the original variable to the member + if (get_decoration_bitset(var.self).get(DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + uint32_t comp = get_decoration(var.self, DecorationComponent); + if (storage == StorageClassInput) + { + type_id = ensure_correct_input_type(var.basetype, locn, comp, 0, meta.strip_array); + var.basetype = type_id; + + type_id = get_pointee_type_id(type_id); + if (meta.strip_array && is_array(get(type_id))) + type_id = get(type_id).parent_type; + if (pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = type_id; + } + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + if (comp) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp); + mark_location_as_used_by_shader(locn, get(type_id), storage); + } + else if (is_builtin && is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, type, storage); + } + else if (is_builtin && capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin)) + { + uint32_t locn = outputs_by_builtin[builtin].location; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, type, storage); + } + + if (get_decoration_bitset(var.self).get(DecorationComponent)) + { + uint32_t component = get_decoration(var.self, DecorationComponent); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, component); + } + + if (get_decoration_bitset(var.self).get(DecorationIndex)) + { + uint32_t index = get_decoration(var.self, DecorationIndex); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index); + } + + // Mark the member as builtin if needed + if (is_builtin) + { + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + if (builtin == BuiltInPosition && storage == StorageClassOutput) + qual_pos_var_name = qual_var_name; + } + + // Copy interpolation decorations if needed + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); +} + +void CompilerMSL::add_composite_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, + InterfaceBlockMeta &meta) +{ + auto &entry_func = get(ir.default_entry_point); + auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + uint32_t elem_cnt = 0; + + if (add_component_variable_to_interface_block(storage, ib_var_ref, var, var_type, meta)) + return; + + if (is_matrix(var_type)) + { + if (is_array(var_type)) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables."); + + elem_cnt = var_type.columns; + } + else if (is_array(var_type)) + { + if (var_type.array.size() != 1) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables."); + + elem_cnt = to_array_size_literal(var_type); + } + + bool is_builtin = is_builtin_variable(var); + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + bool is_flat = has_decoration(var.self, DecorationFlat); + bool is_noperspective = has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = has_decoration(var.self, DecorationCentroid); + bool is_sample = has_decoration(var.self, DecorationSample); + + auto *usable_type = &var_type; + if (usable_type->pointer) + usable_type = &get(usable_type->parent_type); + while (is_array(*usable_type) || is_matrix(*usable_type)) + usable_type = &get(usable_type->parent_type); + + // If a builtin, force it to have the proper name. + if (is_builtin) + set_name(var.self, builtin_to_glsl(builtin, StorageClassFunction)); + + bool flatten_from_ib_var = false; + string flatten_from_ib_mbr_name; + + if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) + { + // Also declare [[clip_distance]] attribute here. + uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(get_variable_data_type_id(var)); + set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + + flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput); + set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name); + + // When we flatten, we flatten directly from the "out" struct, + // not from a function variable. + flatten_from_ib_var = true; + + if (!msl_options.enable_clip_distance_user_varying) + return; + } + else if (!meta.strip_array) + { + // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. + entry_func.add_local_variable(var.self); + // We need to declare the variable early and at entry-point scope. + vars_needing_early_declaration.push_back(var.self); + } + + for (uint32_t i = 0; i < elem_cnt; i++) + { + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + + uint32_t target_components = 0; + bool padded_output = false; + uint32_t type_id = usable_type->self; + + // Check if we need to pad fragment output to match a certain number of components. + if (get_decoration_bitset(var.self).get(DecorationLocation) && msl_options.pad_fragment_output_components && + get_entry_point().model == ExecutionModelFragment && storage == StorageClassOutput) + { + uint32_t locn = get_decoration(var.self, DecorationLocation) + i; + target_components = get_target_components_for_fragment_location(locn); + if (usable_type->vecsize < target_components) + { + // Make a new type here. + type_id = build_extended_vector_type(usable_type->self, target_components); + padded_output = true; + } + } + + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(get_pointee_type_id(type_id), is_noperspective)); + else + ib_type.member_types.push_back(get_pointee_type_id(type_id)); + + // Give the member a name + string mbr_name = ensure_valid_name(join(to_expression(var.self), "_", i), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // There is no qualified alias since we need to flatten the internal array on return. + if (get_decoration_bitset(var.self).get(DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation) + i; + uint32_t comp = get_decoration(var.self, DecorationComponent); + if (storage == StorageClassInput) + { + var.basetype = ensure_correct_input_type(var.basetype, locn, comp, 0, meta.strip_array); + uint32_t mbr_type_id = ensure_correct_input_type(usable_type->self, locn, comp, 0, meta.strip_array); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; + } + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + if (comp) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (is_builtin && is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location + i; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (is_builtin && capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin)) + { + uint32_t locn = outputs_by_builtin[builtin].location + i; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, *usable_type, storage); + } + else if (is_builtin && (builtin == BuiltInClipDistance || builtin == BuiltInCullDistance)) + { + // Declare the Clip/CullDistance as [[user(clip/cullN)]]. + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, i); + } + + if (get_decoration_bitset(var.self).get(DecorationIndex)) + { + uint32_t index = get_decoration(var.self, DecorationIndex); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, index); + } + + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); + + // Only flatten/unflatten IO composites for non-tessellation cases where arrays are not stripped. + if (!meta.strip_array) + { + switch (storage) + { + case StorageClassInput: + entry_func.fixup_hooks_in.push_back([=, &var]() { + if (pull_model_inputs.count(var.self)) + { + string lerp_call; + if (is_centroid) + lerp_call = ".interpolate_at_centroid()"; + else if (is_sample) + lerp_call = join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + lerp_call = ".interpolate_at_center()"; + statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, lerp_call, ";"); + } + else + { + statement(to_name(var.self), "[", i, "] = ", ib_var_ref, ".", mbr_name, ";"); + } + }); + break; + + case StorageClassOutput: + entry_func.fixup_hooks_out.push_back([=, &var]() { + if (padded_output) + { + auto &padded_type = this->get(type_id); + statement( + ib_var_ref, ".", mbr_name, " = ", + remap_swizzle(padded_type, usable_type->vecsize, join(to_name(var.self), "[", i, "]")), + ";"); + } + else if (flatten_from_ib_var) + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i, + "];"); + else + statement(ib_var_ref, ".", mbr_name, " = ", to_name(var.self), "[", i, "];"); + }); + break; + + default: + break; + } + } + } +} + +void CompilerMSL::add_composite_member_variable_to_interface_block(StorageClass storage, + const string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, SPIRType &var_type, + uint32_t mbr_idx, InterfaceBlockMeta &meta, + const string &mbr_name_qual, + const string &var_chain_qual, + uint32_t &location, uint32_t &var_mbr_idx, + const Bitset &interpolation_qual) +{ + auto &entry_func = get(ir.default_entry_point); + + BuiltIn builtin = BuiltInMax; + bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); + bool is_flat = interpolation_qual.get(DecorationFlat) || + has_member_decoration(var_type.self, mbr_idx, DecorationFlat) || + has_decoration(var.self, DecorationFlat); + bool is_noperspective = interpolation_qual.get(DecorationNoPerspective) || + has_member_decoration(var_type.self, mbr_idx, DecorationNoPerspective) || + has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = interpolation_qual.get(DecorationCentroid) || + has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) || + has_decoration(var.self, DecorationCentroid); + bool is_sample = interpolation_qual.get(DecorationSample) || + has_member_decoration(var_type.self, mbr_idx, DecorationSample) || + has_decoration(var.self, DecorationSample); + + Bitset inherited_qual; + if (is_flat) + inherited_qual.set(DecorationFlat); + if (is_noperspective) + inherited_qual.set(DecorationNoPerspective); + if (is_centroid) + inherited_qual.set(DecorationCentroid); + if (is_sample) + inherited_qual.set(DecorationSample); + + uint32_t mbr_type_id = var_type.member_types[mbr_idx]; + auto &mbr_type = get(mbr_type_id); + + bool mbr_is_indexable = false; + uint32_t elem_cnt = 1; + if (is_matrix(mbr_type)) + { + if (is_array(mbr_type)) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables."); + + mbr_is_indexable = true; + elem_cnt = mbr_type.columns; + } + else if (is_array(mbr_type)) + { + if (mbr_type.array.size() != 1) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables."); + + mbr_is_indexable = true; + elem_cnt = to_array_size_literal(mbr_type); + } + + auto *usable_type = &mbr_type; + if (usable_type->pointer) + usable_type = &get(usable_type->parent_type); + while (is_array(*usable_type) || is_matrix(*usable_type)) + usable_type = &get(usable_type->parent_type); + + bool flatten_from_ib_var = false; + string flatten_from_ib_mbr_name; + + if (storage == StorageClassOutput && is_builtin && builtin == BuiltInClipDistance) + { + // Also declare [[clip_distance]] attribute here. + uint32_t clip_array_mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(mbr_type_id); + set_member_decoration(ib_type.self, clip_array_mbr_idx, DecorationBuiltIn, BuiltInClipDistance); + + flatten_from_ib_mbr_name = builtin_to_glsl(BuiltInClipDistance, StorageClassOutput); + set_member_name(ib_type.self, clip_array_mbr_idx, flatten_from_ib_mbr_name); + + // When we flatten, we flatten directly from the "out" struct, + // not from a function variable. + flatten_from_ib_var = true; + + if (!msl_options.enable_clip_distance_user_varying) + return; + } + + // Recursively handle nested structures. + if (mbr_type.basetype == SPIRType::Struct) + { + for (uint32_t i = 0; i < elem_cnt; i++) + { + string mbr_name = append_member_name(mbr_name_qual, var_type, mbr_idx) + (mbr_is_indexable ? join("_", i) : ""); + string var_chain = join(var_chain_qual, ".", to_member_name(var_type, mbr_idx), (mbr_is_indexable ? join("[", i, "]") : "")); + uint32_t sub_mbr_cnt = uint32_t(mbr_type.member_types.size()); + for (uint32_t sub_mbr_idx = 0; sub_mbr_idx < sub_mbr_cnt; sub_mbr_idx++) + { + add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, + var, mbr_type, sub_mbr_idx, + meta, mbr_name, var_chain, + location, var_mbr_idx, inherited_qual); + // FIXME: Recursive structs and tessellation breaks here. + var_mbr_idx++; + } + } + return; + } + + for (uint32_t i = 0; i < elem_cnt; i++) + { + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(usable_type->self, is_noperspective)); + else + ib_type.member_types.push_back(usable_type->self); + + // Give the member a name + string mbr_name = ensure_valid_name(append_member_name(mbr_name_qual, var_type, mbr_idx) + (mbr_is_indexable ? join("_", i) : ""), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // The SPIRV location of interface variable, used to obtain the initial + // MSL location (the location variable) and interface matching + uint32_t ir_location = UINT32_MAX; + bool has_member_loc_decor = has_member_decoration(var_type.self, mbr_idx, DecorationLocation); + bool has_var_loc_decor = has_decoration(var.self, DecorationLocation); + uint32_t orig_vecsize = UINT32_MAX; + + // If we haven't established a location base yet, do so here. + if (location == UINT32_MAX) + { + if (has_member_loc_decor) + ir_location = get_member_decoration(var_type.self, mbr_idx, DecorationLocation); + else if (has_var_loc_decor) + ir_location = get_accumulated_member_location(var, mbr_idx, meta.strip_array); + else if (is_builtin) + { + if (is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) + ir_location = inputs_by_builtin[builtin].location; + else if (capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin)) + ir_location = outputs_by_builtin[builtin].location; + } + } + + // Once we determine the location of the first member within nested structures, + // from a var of the topmost structure, the remaining flattened members of + // the nested structures will have consecutive location values. At this point, + // we've recursively tunnelled into structs, arrays, and matrices, and are + // down to a single location for each member now. + if (location == UINT32_MAX && ir_location != UINT32_MAX) + location = ir_location + i; + + if (storage == StorageClassInput && (has_member_loc_decor || has_var_loc_decor)) + { + uint32_t component = 0; + uint32_t orig_mbr_type_id = usable_type->self; + + if (has_member_loc_decor) + component = get_member_decoration(var_type.self, mbr_idx, DecorationComponent); + + var.basetype = ensure_correct_input_type(var.basetype, location, component, 0, meta.strip_array); + mbr_type_id = ensure_correct_input_type(usable_type->self, location, component, 0, meta.strip_array); + + // For members of the composite interface block, we only change the interface block type + // when interface matching happens. In the meantime, we store the original vector size + // and insert a swizzle when loading from metal interface block (see fixup below) + if (mbr_type_id != orig_mbr_type_id) + orig_vecsize = get(orig_mbr_type_id).vecsize; + + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; + } + + if ((!is_builtin && location != UINT32_MAX) || (is_builtin && ir_location != UINT32_MAX)) + { + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); + mark_location_as_used_by_shader(location, *usable_type, storage); + location++; + } + else if (is_builtin && (builtin == BuiltInClipDistance || builtin == BuiltInCullDistance)) + { + // Declare the Clip/CullDistance as [[user(clip/cullN)]]. + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationIndex, i); + } + + if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent)) + SPIRV_CROSS_THROW("DecorationComponent on matrices and arrays is not supported."); + + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, var_mbr_idx); + + // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate. + if (!meta.strip_array && meta.allow_local_declaration) + { + string var_chain = join(var_chain_qual, ".", to_member_name(var_type, mbr_idx), (mbr_is_indexable ? join("[", i, "]") : "")); + switch (storage) + { + case StorageClassInput: + entry_func.fixup_hooks_in.push_back([=, &var]() { + string lerp_call; + string swizzle; + if (pull_model_inputs.count(var.self)) + { + if (is_centroid) + lerp_call = ".interpolate_at_centroid()"; + else if (is_sample) + lerp_call = join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + lerp_call = ".interpolate_at_center()"; + } + if (orig_vecsize != UINT32_MAX) + swizzle = vector_swizzle(orig_vecsize, 0); + statement(var_chain, " = ", ib_var_ref, ".", mbr_name, lerp_call, swizzle, ";"); + }); + break; + + case StorageClassOutput: + entry_func.fixup_hooks_out.push_back([=]() { + if (flatten_from_ib_var) + statement(ib_var_ref, ".", mbr_name, " = ", ib_var_ref, ".", flatten_from_ib_mbr_name, "[", i, "];"); + else + statement(ib_var_ref, ".", mbr_name, " = ", var_chain, ";"); + }); + break; + + default: + break; + } + } + } +} + +void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass storage, + const string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, SPIRType &var_type, + uint32_t mbr_idx, InterfaceBlockMeta &meta, + const string &mbr_name_qual, + const string &var_chain_qual, + uint32_t &location, uint32_t &var_mbr_idx) +{ + auto &entry_func = get(ir.default_entry_point); + + BuiltIn builtin = BuiltInMax; + bool is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); + bool is_flat = + has_member_decoration(var_type.self, mbr_idx, DecorationFlat) || has_decoration(var.self, DecorationFlat); + bool is_noperspective = has_member_decoration(var_type.self, mbr_idx, DecorationNoPerspective) || + has_decoration(var.self, DecorationNoPerspective); + bool is_centroid = has_member_decoration(var_type.self, mbr_idx, DecorationCentroid) || + has_decoration(var.self, DecorationCentroid); + bool is_sample = + has_member_decoration(var_type.self, mbr_idx, DecorationSample) || has_decoration(var.self, DecorationSample); + + // Add a reference to the member to the interface struct. + uint32_t mbr_type_id = var_type.member_types[mbr_idx]; + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + mbr_type_id = ensure_correct_builtin_type(mbr_type_id, builtin); + var_type.member_types[mbr_idx] = mbr_type_id; + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types.push_back(build_msl_interpolant_type(mbr_type_id, is_noperspective)); + else + ib_type.member_types.push_back(mbr_type_id); + + // Give the member a name + string mbr_name = ensure_valid_name(append_member_name(mbr_name_qual, var_type, mbr_idx), "m"); + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // Update the original variable reference to include the structure reference + string qual_var_name = ib_var_ref + "." + mbr_name; + // If using pull-model interpolation, need to add a call to the correct interpolation method. + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + { + if (is_centroid) + qual_var_name += ".interpolate_at_centroid()"; + else if (is_sample) + qual_var_name += join(".interpolate_at_sample(", to_expression(builtin_sample_id_id), ")"); + else + qual_var_name += ".interpolate_at_center()"; + } + + // The SPIRV location of interface variable, used to obtain the initial + // MSL location (the location variable) and interface matching + uint32_t ir_location = UINT32_MAX; + bool has_member_loc_decor = has_member_decoration(var_type.self, mbr_idx, DecorationLocation); + bool has_var_loc_decor = has_decoration(var.self, DecorationLocation); + uint32_t orig_vecsize = UINT32_MAX; + + if (has_member_loc_decor) + ir_location = get_member_decoration(var_type.self, mbr_idx, DecorationLocation); + else if (has_var_loc_decor) + ir_location = get_accumulated_member_location(var, mbr_idx, meta.strip_array); + else if (is_builtin) + { + if (is_tessellation_shader() && storage == StorageClassInput && inputs_by_builtin.count(builtin)) + ir_location = inputs_by_builtin[builtin].location; + else if (capture_output_to_buffer && storage == StorageClassOutput && outputs_by_builtin.count(builtin)) + ir_location = outputs_by_builtin[builtin].location; + } + + // Once we determine the location of the first member within nested structures, + // from a var of the topmost structure, the remaining flattened members of + // the nested structures will have consecutive location values. At this point, + // we've recursively tunnelled into structs, arrays, and matrices, and are + // down to a single location for each member now. + if (location == UINT32_MAX && ir_location != UINT32_MAX) + location = ir_location; + + if (storage == StorageClassInput && (has_member_loc_decor || has_var_loc_decor)) + { + uint32_t component = 0; + uint32_t orig_mbr_type_id = mbr_type_id; + + if (has_member_loc_decor) + component = get_member_decoration(var_type.self, mbr_idx, DecorationComponent); + + mbr_type_id = ensure_correct_input_type(mbr_type_id, location, component, 0, meta.strip_array); + + // For members of the composite interface block, we only change the interface block type + // when interface matching happens. In the meantime, we store the original vector size + // and insert a swizzle when loading from metal interface block (see fixup below) + if (mbr_type_id != orig_mbr_type_id) + orig_vecsize = get(orig_mbr_type_id).vecsize; + + if (storage == StorageClassInput && pull_model_inputs.count(var.self)) + ib_type.member_types[ib_mbr_idx] = build_msl_interpolant_type(mbr_type_id, is_noperspective); + else + ib_type.member_types[ib_mbr_idx] = mbr_type_id; + } + + bool flatten_stage_out = false; + string var_chain = var_chain_qual + "." + to_member_name(var_type, mbr_idx); + if (is_builtin && !meta.strip_array) + { + // For the builtin gl_PerVertex, we cannot treat it as a block anyways, + // so redirect to qualified name. + set_member_qualified_name(var_type.self, mbr_idx, qual_var_name); + } + else if (!meta.strip_array && meta.allow_local_declaration) + { + // Unflatten or flatten from [[stage_in]] or [[stage_out]] as appropriate. + switch (storage) + { + case StorageClassInput: + entry_func.fixup_hooks_in.push_back([=]() { + string swizzle; + // Insert swizzle for widened interface block vector from interface matching + if (orig_vecsize != UINT32_MAX) + swizzle = vector_swizzle(orig_vecsize, 0); + statement(var_chain, " = ", qual_var_name, swizzle, ";"); + }); + break; + + case StorageClassOutput: + flatten_stage_out = true; + entry_func.fixup_hooks_out.push_back([=]() { + statement(qual_var_name, " = ", var_chain, ";"); + }); + break; + + default: + break; + } + } + + if ((!is_builtin && location != UINT32_MAX) || (is_builtin && ir_location != UINT32_MAX)) + { + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); + mark_location_as_used_by_shader(location, get(mbr_type_id), storage); + location += type_to_location_count(get(mbr_type_id)); + } + + // Copy the component location, if present. + if (has_member_decoration(var_type.self, mbr_idx, DecorationComponent)) + { + uint32_t comp = get_member_decoration(var_type.self, mbr_idx, DecorationComponent); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationComponent, comp); + } + + // Mark the member as builtin if needed + if (is_builtin) + { + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + if (builtin == BuiltInPosition && storage == StorageClassOutput) + qual_pos_var_name = qual_var_name; + } + + const SPIRConstant *c = nullptr; + if (!flatten_stage_out && var.storage == StorageClassOutput && + var.initializer != ID(0) && (c = maybe_get(var.initializer))) + { + if (meta.strip_array) + { + entry_func.fixup_hooks_in.push_back([=, &var]() { + auto &type = this->get(var.basetype); + uint32_t index = get_extended_member_decoration(var.self, mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex); + + auto invocation = to_tesc_invocation_id(); + auto constant_chain = join(to_expression(var.initializer), "[", invocation, "]"); + statement(to_expression(stage_out_ptr_var_id), "[", + invocation, "].", + to_member_name(ib_type, index), " = ", + constant_chain, ".", to_member_name(type, mbr_idx), ";"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + statement(qual_var_name, " = ", constant_expression( + this->get(c->subconstants[mbr_idx])), ";"); + }); + } + } + + if (storage != StorageClassInput || !pull_model_inputs.count(var.self)) + { + // Copy interpolation decorations if needed + if (is_flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (is_noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (is_centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (is_sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceOrigID, var.self); + set_extended_member_decoration(ib_type.self, ib_mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, var_mbr_idx); +} + +// In Metal, the tessellation levels are stored as tightly packed half-precision floating point values. +// But, stage-in attribute offsets and strides must be multiples of four, so we can't pass the levels +// individually. Therefore, we must pass them as vectors. Triangles get a single float4, with the outer +// levels in 'xyz' and the inner level in 'w'. Quads get a float4 containing the outer levels and a +// float2 containing the inner levels. +void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var) +{ + auto &var_type = get_variable_element_type(var); + + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + bool triangles = is_tessellating_triangles(); + string mbr_name; + + // Add a reference to the variable type to the interface struct. + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + + const auto mark_locations = [&](const SPIRType &new_var_type) { + if (get_decoration_bitset(var.self).get(DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, new_var_type, StorageClassInput); + } + else if (inputs_by_builtin.count(builtin)) + { + uint32_t locn = inputs_by_builtin[builtin].location; + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, locn); + mark_location_as_used_by_shader(locn, new_var_type, StorageClassInput); + } + }; + + if (triangles) + { + // Triangles are tricky, because we want only one member in the struct. + mbr_name = "gl_TessLevel"; + + // If we already added the other one, we can skip this step. + if (!added_builtin_tess_level) + { + uint32_t type_id = build_extended_vector_type(var_type.self, 4); + + ib_type.member_types.push_back(type_id); + + // Give the member a name + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + + // We cannot decorate both, but the important part is that + // it's marked as builtin so we can get automatic attribute assignment if needed. + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + + mark_locations(var_type); + added_builtin_tess_level = true; + } + } + else + { + mbr_name = builtin_to_glsl(builtin, StorageClassFunction); + + uint32_t type_id = build_extended_vector_type(var_type.self, builtin == BuiltInTessLevelOuter ? 4 : 2); + + uint32_t ptr_type_id = ir.increase_bound_by(1); + auto &new_var_type = set(ptr_type_id, get(type_id)); + new_var_type.pointer = true; + new_var_type.pointer_depth++; + new_var_type.storage = StorageClassInput; + new_var_type.parent_type = type_id; + + ib_type.member_types.push_back(type_id); + + // Give the member a name + set_member_name(ib_type.self, ib_mbr_idx, mbr_name); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationBuiltIn, builtin); + + mark_locations(new_var_type); + } + + add_tess_level_input(ib_var_ref, mbr_name, var); +} + +void CompilerMSL::add_tess_level_input(const std::string &base_ref, const std::string &mbr_name, SPIRVariable &var) +{ + auto &entry_func = get(ir.default_entry_point); + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + + // Force the variable to have the proper name. + string var_name = builtin_to_glsl(builtin, StorageClassFunction); + set_name(var.self, var_name); + + // We need to declare the variable early and at entry-point scope. + entry_func.add_local_variable(var.self); + vars_needing_early_declaration.push_back(var.self); + bool triangles = is_tessellating_triangles(); + + if (builtin == BuiltInTessLevelOuter) + { + entry_func.fixup_hooks_in.push_back( + [=]() + { + statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[0];"); + statement(var_name, "[1] = ", base_ref, ".", mbr_name, "[1];"); + statement(var_name, "[2] = ", base_ref, ".", mbr_name, "[2];"); + if (!triangles) + statement(var_name, "[3] = ", base_ref, ".", mbr_name, "[3];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + if (triangles) + { + if (msl_options.raw_buffer_tese_input) + statement(var_name, "[0] = ", base_ref, ".", mbr_name, ";"); + else + statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[3];"); + } + else + { + statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[0];"); + statement(var_name, "[1] = ", base_ref, ".", mbr_name, "[1];"); + } + }); + } +} + +bool CompilerMSL::variable_storage_requires_stage_io(StorageClass storage) const +{ + if (storage == StorageClassOutput) + return !capture_output_to_buffer; + else if (storage == StorageClassInput) + return !(is_tesc_shader() && msl_options.multi_patch_workgroup) && + !(is_tese_shader() && msl_options.raw_buffer_tese_input); + else + return false; +} + +string CompilerMSL::to_tesc_invocation_id() +{ + if (msl_options.multi_patch_workgroup) + { + // n.b. builtin_invocation_id_id here is the dispatch global invocation ID, + // not the TC invocation ID. + return join(to_expression(builtin_invocation_id_id), ".x % ", get_entry_point().output_vertices); + } + else + return builtin_to_glsl(BuiltInInvocationId, StorageClassInput); +} + +void CompilerMSL::emit_local_masked_variable(const SPIRVariable &masked_var, bool strip_array) +{ + auto &entry_func = get(ir.default_entry_point); + bool threadgroup_storage = variable_decl_is_remapped_storage(masked_var, StorageClassWorkgroup); + + if (threadgroup_storage && msl_options.multi_patch_workgroup) + { + // We need one threadgroup block per patch, so fake this. + entry_func.fixup_hooks_in.push_back([this, &masked_var]() { + auto &type = get_variable_data_type(masked_var); + add_local_variable_name(masked_var.self); + + const uint32_t max_control_points_per_patch = 32u; + uint32_t max_num_instances = + (max_control_points_per_patch + get_entry_point().output_vertices - 1u) / + get_entry_point().output_vertices; + statement("threadgroup ", type_to_glsl(type), " ", + "spvStorage", to_name(masked_var.self), "[", max_num_instances, "]", + type_to_array_glsl(type, 0), ";"); + + // Assign a threadgroup slice to each PrimitiveID. + // We assume here that workgroup size is rounded to 32, + // since that's the maximum number of control points per patch. + // We cannot size the array based on fixed dispatch parameters, + // since Metal does not allow that. :( + // FIXME: We will likely need an option to support passing down target workgroup size, + // so we can emit appropriate size here. + statement("threadgroup auto ", + "&", to_name(masked_var.self), + " = spvStorage", to_name(masked_var.self), "[", + "(", to_expression(builtin_invocation_id_id), ".x / ", + get_entry_point().output_vertices, ") % ", + max_num_instances, "];"); + }); + } + else + { + entry_func.add_local_variable(masked_var.self); + } + + if (!threadgroup_storage) + { + vars_needing_early_declaration.push_back(masked_var.self); + } + else if (masked_var.initializer) + { + // Cannot directly initialize threadgroup variables. Need fixup hooks. + ID initializer = masked_var.initializer; + if (strip_array) + { + entry_func.fixup_hooks_in.push_back([this, &masked_var, initializer]() { + auto invocation = to_tesc_invocation_id(); + statement(to_expression(masked_var.self), "[", + invocation, "] = ", + to_expression(initializer), "[", + invocation, "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([this, &masked_var, initializer]() { + statement(to_expression(masked_var.self), " = ", to_expression(initializer), ";"); + }); + } + } +} + +void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, InterfaceBlockMeta &meta) +{ + auto &entry_func = get(ir.default_entry_point); + // Tessellation control I/O variables and tessellation evaluation per-point inputs are + // usually declared as arrays. In these cases, we want to add the element type to the + // interface block, since in Metal it's the interface block itself which is arrayed. + auto &var_type = meta.strip_array ? get_variable_element_type(var) : get_variable_data_type(var); + bool is_builtin = is_builtin_variable(var); + auto builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + bool is_block = has_decoration(var_type.self, DecorationBlock); + + // If stage variables are masked out, emit them as plain variables instead. + // For builtins, we query them one by one later. + // IO blocks are not masked here, we need to mask them per-member instead. + if (storage == StorageClassOutput && is_stage_output_variable_masked(var)) + { + // If we ignore an output, we must still emit it, since it might be used by app. + // Instead, just emit it as early declaration. + emit_local_masked_variable(var, meta.strip_array); + return; + } + + // Tesselation stages pass I/O via buffer content which may contain nested structs. + // Ensure the vector sizes of any nested struct members within these input variables match + // the vector sizes of the corresponding output variables from the previous pipeline stage. + // This adjustment is handled here instead of ensure_correct_input_type() in order to + // perform the necessary recursive processing. + if (storage == StorageClassInput && var_type.basetype == SPIRType::Struct && + ((is_tesc_shader() && msl_options.multi_patch_workgroup) || + (is_tese_shader() && msl_options.raw_buffer_tese_input)) && + has_decoration(var.self, DecorationLocation)) + { + uint32_t locn = get_decoration(var.self, DecorationLocation); + ensure_struct_members_valid_vecsizes(get_variable_data_type(var), locn); + } + + if (storage == StorageClassInput && has_decoration(var.self, DecorationPerVertexKHR)) + SPIRV_CROSS_THROW("PerVertexKHR decoration is not supported in MSL."); + + // If variable names alias, they will end up with wrong names in the interface struct, because + // there might be aliases in the member name cache and there would be a mismatch in fixup_in code. + // Make sure to register the variables as unique resource names ahead of time. + // This would normally conflict with the name cache when emitting local variables, + // but this happens in the setup stage, before we hit compilation loops. + // The name cache is cleared before we actually emit code, so this is safe. + add_resource_name(var.self); + + if (var_type.basetype == SPIRType::Struct) + { + bool block_requires_flattening = + variable_storage_requires_stage_io(storage) || (is_block && var_type.array.empty()); + bool needs_local_declaration = !is_builtin && block_requires_flattening && meta.allow_local_declaration; + + if (needs_local_declaration) + { + // For I/O blocks or structs, we will need to pass the block itself around + // to functions if they are used globally in leaf functions. + // Rather than passing down member by member, + // we unflatten I/O blocks while running the shader, + // and pass the actual struct type down to leaf functions. + // We then unflatten inputs, and flatten outputs in the "fixup" stages. + emit_local_masked_variable(var, meta.strip_array); + } + + if (!block_requires_flattening) + { + // In Metal tessellation shaders, the interface block itself is arrayed. This makes things + // very complicated, since stage-in structures in MSL don't support nested structures. + // Luckily, for stage-out when capturing output, we can avoid this and just add + // composite members directly, because the stage-out structure is stored to a buffer, + // not returned. + add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta); + } + else + { + bool masked_block = false; + uint32_t location = UINT32_MAX; + uint32_t var_mbr_idx = 0; + uint32_t elem_cnt = 1; + if (is_matrix(var_type)) + { + if (is_array(var_type)) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-matrices in input and output variables."); + + elem_cnt = var_type.columns; + } + else if (is_array(var_type)) + { + if (var_type.array.size() != 1) + SPIRV_CROSS_THROW("MSL cannot emit arrays-of-arrays in input and output variables."); + + elem_cnt = to_array_size_literal(var_type); + } + + for (uint32_t elem_idx = 0; elem_idx < elem_cnt; elem_idx++) + { + // Flatten the struct members into the interface struct + for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(var_type.member_types.size()); mbr_idx++) + { + builtin = BuiltInMax; + is_builtin = is_member_builtin(var_type, mbr_idx, &builtin); + auto &mbr_type = get(var_type.member_types[mbr_idx]); + + if (storage == StorageClassOutput && is_stage_output_block_member_masked(var, mbr_idx, meta.strip_array)) + { + location = UINT32_MAX; // Skip this member and resolve location again on next var member + + if (is_block) + masked_block = true; + + // Non-builtin block output variables are just ignored, since they will still access + // the block variable as-is. They're just not flattened. + if (is_builtin && !meta.strip_array) + { + // Emit a fake variable instead. + uint32_t ids = ir.increase_bound_by(2); + uint32_t ptr_type_id = ids + 0; + uint32_t var_id = ids + 1; + + auto ptr_type = mbr_type; + ptr_type.pointer = true; + ptr_type.pointer_depth++; + ptr_type.parent_type = var_type.member_types[mbr_idx]; + ptr_type.storage = StorageClassOutput; + + uint32_t initializer = 0; + if (var.initializer) + if (auto *c = maybe_get(var.initializer)) + initializer = c->subconstants[mbr_idx]; + + set(ptr_type_id, ptr_type); + set(var_id, ptr_type_id, StorageClassOutput, initializer); + entry_func.add_local_variable(var_id); + vars_needing_early_declaration.push_back(var_id); + set_name(var_id, builtin_to_glsl(builtin, StorageClassOutput)); + set_decoration(var_id, DecorationBuiltIn, builtin); + } + } + else if (!is_builtin || has_active_builtin(builtin, storage)) + { + bool is_composite_type = is_matrix(mbr_type) || is_array(mbr_type) || mbr_type.basetype == SPIRType::Struct; + bool attribute_load_store = + storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; + bool storage_is_stage_io = variable_storage_requires_stage_io(storage); + + // Clip/CullDistance always need to be declared as user attributes. + if (builtin == BuiltInClipDistance || builtin == BuiltInCullDistance) + is_builtin = false; + + const string var_name = to_name(var.self); + string mbr_name_qual = var_name; + string var_chain_qual = var_name; + if (elem_cnt > 1) + { + mbr_name_qual += join("_", elem_idx); + var_chain_qual += join("[", elem_idx, "]"); + } + + if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) + { + add_composite_member_variable_to_interface_block(storage, ib_var_ref, ib_type, + var, var_type, mbr_idx, meta, + mbr_name_qual, var_chain_qual, + location, var_mbr_idx, {}); + } + else + { + add_plain_member_variable_to_interface_block(storage, ib_var_ref, ib_type, + var, var_type, mbr_idx, meta, + mbr_name_qual, var_chain_qual, + location, var_mbr_idx); + } + } + var_mbr_idx++; + } + } + + // If we're redirecting a block, we might still need to access the original block + // variable if we're masking some members. + if (masked_block && !needs_local_declaration && (!is_builtin_variable(var) || is_tesc_shader())) + { + if (is_builtin_variable(var)) + { + // Ensure correct names for the block members if we're actually going to + // declare gl_PerVertex. + for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(var_type.member_types.size()); mbr_idx++) + { + set_member_name(var_type.self, mbr_idx, builtin_to_glsl( + BuiltIn(get_member_decoration(var_type.self, mbr_idx, DecorationBuiltIn)), + StorageClassOutput)); + } + + set_name(var_type.self, "gl_PerVertex"); + set_name(var.self, "gl_out_masked"); + stage_out_masked_builtin_type_id = var_type.self; + } + emit_local_masked_variable(var, meta.strip_array); + } + } + } + else if (is_tese_shader() && storage == StorageClassInput && !meta.strip_array && is_builtin && + (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner)) + { + add_tess_level_input_to_interface_block(ib_var_ref, ib_type, var); + } + else if (var_type.basetype == SPIRType::Boolean || var_type.basetype == SPIRType::Char || + type_is_integral(var_type) || type_is_floating_point(var_type)) + { + if (!is_builtin || has_active_builtin(builtin, storage)) + { + bool is_composite_type = is_matrix(var_type) || is_array(var_type); + bool storage_is_stage_io = variable_storage_requires_stage_io(storage); + bool attribute_load_store = storage == StorageClassInput && get_execution_model() != ExecutionModelFragment; + + // Clip/CullDistance always needs to be declared as user attributes. + if (builtin == BuiltInClipDistance || builtin == BuiltInCullDistance) + is_builtin = false; + + // MSL does not allow matrices or arrays in input or output variables, so need to handle it specially. + if ((!is_builtin || attribute_load_store) && storage_is_stage_io && is_composite_type) + { + add_composite_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta); + } + else + { + add_plain_variable_to_interface_block(storage, ib_var_ref, ib_type, var, meta); + } + } + } +} + +// Recursively iterate into the input struct type, and adjust the vecsize +// of any nested members, based on location info provided through the API. +// The location parameter is modified recursively. +void CompilerMSL::ensure_struct_members_valid_vecsizes(SPIRType &struct_type, uint32_t &location) +{ + assert(struct_type.basetype == SPIRType::Struct); + + auto mbr_cnt = struct_type.member_types.size(); + for (size_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + auto mbr_type_id = struct_type.member_types[mbr_idx]; + auto &mbr_type = get(mbr_type_id); + + if (mbr_type.basetype == SPIRType::Struct) + ensure_struct_members_valid_vecsizes(mbr_type, location); + else + { + auto p_va = inputs_by_location.find({location, 0}); + if (p_va != end(inputs_by_location) && p_va->second.vecsize > mbr_type.vecsize) + { + // Set a new member type into the struct type, and all its parent types. + auto new_mbr_type_id = build_extended_vector_type(mbr_type_id, p_va->second.vecsize); + for (auto *p_type = &struct_type; p_type; p_type = maybe_get(p_type->parent_type)) + p_type->member_types[mbr_idx] = new_mbr_type_id; + } + + // Calc location of next member + uint32_t loc_cnt = mbr_type.columns; + auto dim_cnt = mbr_type.array.size(); + for (uint32_t i = 0; i < dim_cnt; i++) + loc_cnt *= to_array_size_literal(mbr_type, i); + + location += loc_cnt; + } + } +} + +// Fix up the mapping of variables to interface member indices, which is used to compile access chains +// for per-vertex variables in a tessellation control shader. +void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t ib_type_id) +{ + // Only needed for tessellation shaders and pull-model interpolants. + // Need to redirect interface indices back to variables themselves. + // For structs, each member of the struct need a separate instance. + if (!is_tesc_shader() && !(is_tese_shader() && storage == StorageClassInput) && + !(get_execution_model() == ExecutionModelFragment && storage == StorageClassInput && + !pull_model_inputs.empty())) + return; + + auto mbr_cnt = uint32_t(ir.meta[ib_type_id].members.size()); + for (uint32_t i = 0; i < mbr_cnt; i++) + { + uint32_t var_id = get_extended_member_decoration(ib_type_id, i, SPIRVCrossDecorationInterfaceOrigID); + if (!var_id) + continue; + auto &var = get(var_id); + + auto &type = get_variable_element_type(var); + + bool flatten_composites = variable_storage_requires_stage_io(var.storage); + bool is_block = has_decoration(type.self, DecorationBlock); + + uint32_t mbr_idx = uint32_t(-1); + if (type.basetype == SPIRType::Struct && (flatten_composites || is_block)) + mbr_idx = get_extended_member_decoration(ib_type_id, i, SPIRVCrossDecorationInterfaceMemberIndex); + + if (mbr_idx != uint32_t(-1)) + { + // Only set the lowest InterfaceMemberIndex for each variable member. + // IB struct members will be emitted in-order w.r.t. interface member index. + if (!has_extended_member_decoration(var_id, mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex)) + set_extended_member_decoration(var_id, mbr_idx, SPIRVCrossDecorationInterfaceMemberIndex, i); + } + else + { + // Only set the lowest InterfaceMemberIndex for each variable. + // IB struct members will be emitted in-order w.r.t. interface member index. + if (!has_extended_decoration(var_id, SPIRVCrossDecorationInterfaceMemberIndex)) + set_extended_decoration(var_id, SPIRVCrossDecorationInterfaceMemberIndex, i); + } + } +} + +// Add an interface structure for the type of storage, which is either StorageClassInput or StorageClassOutput. +// Returns the ID of the newly added variable, or zero if no variable was added. +uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch) +{ + // Accumulate the variables that should appear in the interface struct. + SmallVector vars; + bool incl_builtins = storage == StorageClassOutput || is_tessellation_shader(); + bool has_seen_barycentric = false; + + InterfaceBlockMeta meta; + + // Varying interfaces between stages which use "user()" attribute can be dealt with + // without explicit packing and unpacking of components. For any variables which link against the runtime + // in some way (vertex attributes, fragment output, etc), we'll need to deal with it somehow. + bool pack_components = + (storage == StorageClassInput && get_execution_model() == ExecutionModelVertex) || + (storage == StorageClassOutput && get_execution_model() == ExecutionModelFragment) || + (storage == StorageClassOutput && get_execution_model() == ExecutionModelVertex && capture_output_to_buffer); + + ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { + if (var.storage != storage) + return; + + auto &type = this->get(var.basetype); + + bool is_builtin = is_builtin_variable(var); + bool is_block = has_decoration(type.self, DecorationBlock); + + auto bi_type = BuiltInMax; + bool builtin_is_gl_in_out = false; + if (is_builtin && !is_block) + { + bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn)); + builtin_is_gl_in_out = bi_type == BuiltInPosition || bi_type == BuiltInPointSize || + bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance; + } + + if (is_builtin && is_block) + builtin_is_gl_in_out = true; + + uint32_t location = get_decoration(var_id, DecorationLocation); + + bool builtin_is_stage_in_out = builtin_is_gl_in_out || + bi_type == BuiltInLayer || bi_type == BuiltInViewportIndex || + bi_type == BuiltInBaryCoordKHR || bi_type == BuiltInBaryCoordNoPerspKHR || + bi_type == BuiltInFragDepth || + bi_type == BuiltInFragStencilRefEXT || bi_type == BuiltInSampleMask; + + // These builtins are part of the stage in/out structs. + bool is_interface_block_builtin = + builtin_is_stage_in_out || (is_tese_shader() && !msl_options.raw_buffer_tese_input && + (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner)); + + bool is_active = interface_variable_exists_in_entry_point(var.self); + if (is_builtin && is_active) + { + // Only emit the builtin if it's active in this entry point. Interface variable list might lie. + if (is_block) + { + // If any builtin is active, the block is active. + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t i = 0; !is_active && i < mbr_cnt; i++) + is_active = has_active_builtin(BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn)), storage); + } + else + { + is_active = has_active_builtin(bi_type, storage); + } + } + + bool filter_patch_decoration = (has_decoration(var_id, DecorationPatch) || is_patch_block(type)) == patch; + + bool hidden = is_hidden_variable(var, incl_builtins); + + // ClipDistance is never hidden, we need to emulate it when used as an input. + if (bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance) + hidden = false; + + // It's not enough to simply avoid marking fragment outputs if the pipeline won't + // accept them. We can't put them in the struct at all, or otherwise the compiler + // complains that the outputs weren't explicitly marked. + // Frag depth and stencil outputs are incompatible with explicit early fragment tests. + // In GLSL, depth and stencil outputs are just ignored when explicit early fragment tests are required. + // In Metal, it's a compilation error, so we need to exclude them from the output struct. + if (get_execution_model() == ExecutionModelFragment && storage == StorageClassOutput && !patch && + ((is_builtin && ((bi_type == BuiltInFragDepth && (!msl_options.enable_frag_depth_builtin || uses_explicit_early_fragment_test())) || + (bi_type == BuiltInFragStencilRefEXT && (!msl_options.enable_frag_stencil_ref_builtin || uses_explicit_early_fragment_test())))) || + (!is_builtin && !(msl_options.enable_frag_output_mask & (1 << location))))) + { + hidden = true; + disabled_frag_outputs.push_back(var_id); + // If a builtin, force it to have the proper name, and mark it as not part of the output struct. + if (is_builtin) + { + set_name(var_id, builtin_to_glsl(bi_type, StorageClassFunction)); + mask_stage_output_by_builtin(bi_type); + } + } + + // Barycentric inputs must be emitted in stage-in, because they can have interpolation arguments. + if (is_active && (bi_type == BuiltInBaryCoordKHR || bi_type == BuiltInBaryCoordNoPerspKHR)) + { + if (has_seen_barycentric) + SPIRV_CROSS_THROW("Cannot declare both BaryCoordNV and BaryCoordNoPerspNV in same shader in MSL."); + has_seen_barycentric = true; + hidden = false; + } + + if (is_active && !hidden && type.pointer && filter_patch_decoration && + (!is_builtin || is_interface_block_builtin)) + { + vars.push_back(&var); + + if (!is_builtin) + { + // Need to deal specially with DecorationComponent. + // Multiple variables can alias the same Location, and try to make sure each location is declared only once. + // We will swizzle data in and out to make this work. + // This is only relevant for vertex inputs and fragment outputs. + // Technically tessellation as well, but it is too complicated to support. + uint32_t component = get_decoration(var_id, DecorationComponent); + if (component != 0) + { + if (is_tessellation_shader()) + SPIRV_CROSS_THROW("Component decoration is not supported in tessellation shaders."); + else if (pack_components) + { + uint32_t array_size = 1; + if (!type.array.empty()) + array_size = to_array_size_literal(type); + + for (uint32_t location_offset = 0; location_offset < array_size; location_offset++) + { + auto &location_meta = meta.location_meta[location + location_offset]; + location_meta.num_components = max(location_meta.num_components, component + type.vecsize); + + // For variables sharing location, decorations and base type must match. + location_meta.base_type_id = type.self; + location_meta.flat = has_decoration(var.self, DecorationFlat); + location_meta.noperspective = has_decoration(var.self, DecorationNoPerspective); + location_meta.centroid = has_decoration(var.self, DecorationCentroid); + location_meta.sample = has_decoration(var.self, DecorationSample); + } + } + } + } + } + + if (is_tese_shader() && msl_options.raw_buffer_tese_input && patch && storage == StorageClassInput && + (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner)) + { + // In this case, we won't add the builtin to the interface struct, + // but we still need the hook to run to populate the arrays. + string base_ref = join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), "]"); + const char *mbr_name = + bi_type == BuiltInTessLevelOuter ? "edgeTessellationFactor" : "insideTessellationFactor"; + add_tess_level_input(base_ref, mbr_name, var); + if (inputs_by_builtin.count(bi_type)) + { + uint32_t locn = inputs_by_builtin[bi_type].location; + mark_location_as_used_by_shader(locn, type, StorageClassInput); + } + } + }); + + // If no variables qualify, leave. + // For patch input in a tessellation evaluation shader, the per-vertex stage inputs + // are included in a special patch control point array. + if (vars.empty() && + !(!msl_options.raw_buffer_tese_input && storage == StorageClassInput && patch && stage_in_var_id)) + return 0; + + // Add a new typed variable for this interface structure. + // The initializer expression is allocated here, but populated when the function + // declaraion is emitted, because it is cleared after each compilation pass. + uint32_t next_id = ir.increase_bound_by(3); + uint32_t ib_type_id = next_id++; + auto &ib_type = set(ib_type_id, OpTypeStruct); + ib_type.basetype = SPIRType::Struct; + ib_type.storage = storage; + set_decoration(ib_type_id, DecorationBlock); + + uint32_t ib_var_id = next_id++; + auto &var = set(ib_var_id, ib_type_id, storage, 0); + var.initializer = next_id++; + + string ib_var_ref; + auto &entry_func = get(ir.default_entry_point); + switch (storage) + { + case StorageClassInput: + ib_var_ref = patch ? patch_stage_in_var_name : stage_in_var_name; + switch (get_execution_model()) + { + case ExecutionModelTessellationControl: + // Add a hook to populate the shared workgroup memory containing the gl_in array. + entry_func.fixup_hooks_in.push_back([=]() { + // Can't use PatchVertices, PrimitiveId, or InvocationId yet; the hooks for those may not have run yet. + if (msl_options.multi_patch_workgroup) + { + // n.b. builtin_invocation_id_id here is the dispatch global invocation ID, + // not the TC invocation ID. + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_in = &", + input_buffer_var_name, "[min(", to_expression(builtin_invocation_id_id), ".x / ", + get_entry_point().output_vertices, + ", spvIndirectParams[1] - 1) * spvIndirectParams[0]];"); + } + else + { + // It's safe to use InvocationId here because it's directly mapped to a + // Metal builtin, and therefore doesn't need a hook. + statement("if (", to_expression(builtin_invocation_id_id), " < spvIndirectParams[0])"); + statement(" ", input_wg_var_name, "[", to_expression(builtin_invocation_id_id), + "] = ", ib_var_ref, ";"); + statement("threadgroup_barrier(mem_flags::mem_threadgroup);"); + statement("if (", to_expression(builtin_invocation_id_id), + " >= ", get_entry_point().output_vertices, ")"); + statement(" return;"); + } + }); + break; + case ExecutionModelTessellationEvaluation: + if (!msl_options.raw_buffer_tese_input) + break; + if (patch) + { + entry_func.fixup_hooks_in.push_back( + [=]() + { + statement("const device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", patch_input_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back( + [=]() + { + statement("const device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_in = &", + input_buffer_var_name, "[", to_expression(builtin_primitive_id_id), " * ", + get_entry_point().output_vertices, "];"); + }); + } + break; + default: + break; + } + break; + + case StorageClassOutput: + { + ib_var_ref = patch ? patch_stage_out_var_name : stage_out_var_name; + + // Add the output interface struct as a local variable to the entry function. + // If the entry point should return the output struct, set the entry function + // to return the output interface struct, otherwise to return nothing. + // Watch out for the rare case where the terminator of the last entry point block is a + // Kill or Unreachable, instead of a Return. Based on SPIR-V's block-domination rules, + // we assume that any block that has a Kill will also have a terminating Return, except + // the last block. + // Indicate the output var requires early initialization. + bool ep_should_return_output = !get_is_rasterization_disabled(); + uint32_t rtn_id = ep_should_return_output ? ib_var_id : 0; + if (!capture_output_to_buffer) + { + entry_func.add_local_variable(ib_var_id); + for (auto &blk_id : entry_func.blocks) + { + auto &blk = get(blk_id); + auto last_blk_return = blk.terminator == SPIRBlock::Kill || blk.terminator == SPIRBlock::Unreachable; + if (blk.terminator == SPIRBlock::Return || (last_blk_return && blk_id == entry_func.blocks.back())) + blk.return_value = rtn_id; + } + vars_needing_early_declaration.push_back(ib_var_id); + } + else + { + switch (get_execution_model()) + { + case ExecutionModelVertex: + case ExecutionModelTessellationEvaluation: + // Instead of declaring a struct variable to hold the output and then + // copying that to the output buffer, we'll declare the output variable + // as a reference to the final output element in the buffer. Then we can + // avoid the extra copy. + entry_func.fixup_hooks_in.push_back([=]() { + if (stage_out_var_id) + { + // The first member of the indirect buffer is always the number of vertices + // to draw. + // We zero-base the InstanceID & VertexID variables for HLSL emulation elsewhere, so don't do it twice + if (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation) + { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", output_buffer_var_name, "[", to_expression(builtin_invocation_id_id), + ".y * ", to_expression(builtin_stage_input_size_id), ".x + ", + to_expression(builtin_invocation_id_id), ".x];"); + } + else if (msl_options.enable_base_index_zero) + { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", output_buffer_var_name, "[", to_expression(builtin_instance_idx_id), + " * spvIndirectParams[0] + ", to_expression(builtin_vertex_idx_id), "];"); + } + else + { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", output_buffer_var_name, "[(", to_expression(builtin_instance_idx_id), + " - ", to_expression(builtin_base_instance_id), ") * spvIndirectParams[0] + ", + to_expression(builtin_vertex_idx_id), " - ", + to_expression(builtin_base_vertex_id), "];"); + } + } + }); + break; + case ExecutionModelTessellationControl: + if (msl_options.multi_patch_workgroup) + { + // We cannot use PrimitiveId here, because the hook may not have run yet. + if (patch) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", patch_output_buffer_var_name, "[", to_expression(builtin_invocation_id_id), + ".x / ", get_entry_point().output_vertices, "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_out = &", + output_buffer_var_name, "[", to_expression(builtin_invocation_id_id), ".x - ", + to_expression(builtin_invocation_id_id), ".x % ", + get_entry_point().output_vertices, "];"); + }); + } + } + else + { + if (patch) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref, + " = ", patch_output_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "];"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + statement("device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_out = &", + output_buffer_var_name, "[", to_expression(builtin_primitive_id_id), " * ", + get_entry_point().output_vertices, "];"); + }); + } + } + break; + default: + break; + } + } + break; + } + + default: + break; + } + + set_name(ib_type_id, to_name(ir.default_entry_point) + "_" + ib_var_ref); + set_name(ib_var_id, ib_var_ref); + + for (auto *p_var : vars) + { + bool strip_array = (is_tesc_shader() || (is_tese_shader() && storage == StorageClassInput)) && !patch; + + // Fixing up flattened stores in TESC is impossible since the memory is group shared either via + // device (not masked) or threadgroup (masked) storage classes and it's race condition city. + meta.strip_array = strip_array; + meta.allow_local_declaration = !strip_array && !(is_tesc_shader() && storage == StorageClassOutput); + add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, meta); + } + + if (((is_tesc_shader() && msl_options.multi_patch_workgroup) || + (is_tese_shader() && msl_options.raw_buffer_tese_input)) && + storage == StorageClassInput) + { + // For tessellation inputs, add all outputs from the previous stage to ensure + // the struct containing them is the correct size and layout. + for (auto &input : inputs_by_location) + { + if (location_inputs_in_use.count(input.first.location) != 0) + continue; + + if (patch != (input.second.rate == MSL_SHADER_VARIABLE_RATE_PER_PATCH)) + continue; + + // Tessellation levels have their own struct, so there's no need to add them here. + if (input.second.builtin == BuiltInTessLevelOuter || input.second.builtin == BuiltInTessLevelInner) + continue; + + // Create a fake variable to put at the location. + uint32_t offset = ir.increase_bound_by(5); + uint32_t type_id = offset; + uint32_t vec_type_id = offset + 1; + uint32_t array_type_id = offset + 2; + uint32_t ptr_type_id = offset + 3; + uint32_t var_id = offset + 4; + + SPIRType type { OpTypeInt }; + switch (input.second.format) + { + case MSL_SHADER_VARIABLE_FORMAT_UINT16: + case MSL_SHADER_VARIABLE_FORMAT_ANY16: + type.basetype = SPIRType::UShort; + type.width = 16; + break; + case MSL_SHADER_VARIABLE_FORMAT_ANY32: + default: + type.basetype = SPIRType::UInt; + type.width = 32; + break; + } + set(type_id, type); + if (input.second.vecsize > 1) + { + type.op = OpTypeVector; + type.vecsize = input.second.vecsize; + set(vec_type_id, type); + type_id = vec_type_id; + } + + type.op = OpTypeArray; + type.array.push_back(0); + type.array_size_literal.push_back(true); + type.parent_type = type_id; + set(array_type_id, type); + type.self = type_id; + + type.op = OpTypePointer; + type.pointer = true; + type.pointer_depth++; + type.parent_type = array_type_id; + type.storage = storage; + auto &ptr_type = set(ptr_type_id, type); + ptr_type.self = array_type_id; + + auto &fake_var = set(var_id, ptr_type_id, storage); + set_decoration(var_id, DecorationLocation, input.first.location); + if (input.first.component) + set_decoration(var_id, DecorationComponent, input.first.component); + + meta.strip_array = true; + meta.allow_local_declaration = false; + add_variable_to_interface_block(storage, ib_var_ref, ib_type, fake_var, meta); + } + } + + if (capture_output_to_buffer && storage == StorageClassOutput) + { + // For captured output, add all inputs from the next stage to ensure + // the struct containing them is the correct size and layout. This is + // necessary for certain implicit builtins that may nonetheless be read, + // even when they aren't written. + for (auto &output : outputs_by_location) + { + if (location_outputs_in_use.count(output.first.location) != 0) + continue; + + // Create a fake variable to put at the location. + uint32_t offset = ir.increase_bound_by(5); + uint32_t type_id = offset; + uint32_t vec_type_id = offset + 1; + uint32_t array_type_id = offset + 2; + uint32_t ptr_type_id = offset + 3; + uint32_t var_id = offset + 4; + + SPIRType type { OpTypeInt }; + switch (output.second.format) + { + case MSL_SHADER_VARIABLE_FORMAT_UINT16: + case MSL_SHADER_VARIABLE_FORMAT_ANY16: + type.basetype = SPIRType::UShort; + type.width = 16; + break; + case MSL_SHADER_VARIABLE_FORMAT_ANY32: + default: + type.basetype = SPIRType::UInt; + type.width = 32; + break; + } + set(type_id, type); + if (output.second.vecsize > 1) + { + type.op = OpTypeVector; + type.vecsize = output.second.vecsize; + set(vec_type_id, type); + type_id = vec_type_id; + } + + if (is_tesc_shader()) + { + type.op = OpTypeArray; + type.array.push_back(0); + type.array_size_literal.push_back(true); + type.parent_type = type_id; + set(array_type_id, type); + } + + type.op = OpTypePointer; + type.pointer = true; + type.pointer_depth++; + type.parent_type = is_tesc_shader() ? array_type_id : type_id; + type.storage = storage; + auto &ptr_type = set(ptr_type_id, type); + ptr_type.self = type.parent_type; + + auto &fake_var = set(var_id, ptr_type_id, storage); + set_decoration(var_id, DecorationLocation, output.first.location); + if (output.first.component) + set_decoration(var_id, DecorationComponent, output.first.component); + + meta.strip_array = true; + meta.allow_local_declaration = false; + add_variable_to_interface_block(storage, ib_var_ref, ib_type, fake_var, meta); + } + } + + // When multiple variables need to access same location, + // unroll locations one by one and we will flatten output or input as necessary. + for (auto &loc : meta.location_meta) + { + uint32_t location = loc.first; + auto &location_meta = loc.second; + + uint32_t ib_mbr_idx = uint32_t(ib_type.member_types.size()); + uint32_t type_id = build_extended_vector_type(location_meta.base_type_id, location_meta.num_components); + ib_type.member_types.push_back(type_id); + + set_member_name(ib_type.self, ib_mbr_idx, join("m_location_", location)); + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationLocation, location); + mark_location_as_used_by_shader(location, get(type_id), storage); + + if (location_meta.flat) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationFlat); + if (location_meta.noperspective) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationNoPerspective); + if (location_meta.centroid) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationCentroid); + if (location_meta.sample) + set_member_decoration(ib_type.self, ib_mbr_idx, DecorationSample); + } + + // Sort the members of the structure by their locations. + MemberSorter member_sorter(ib_type, ir.meta[ib_type_id], MemberSorter::LocationThenBuiltInType); + member_sorter.sort(); + + // The member indices were saved to the original variables, but after the members + // were sorted, those indices are now likely incorrect. Fix those up now. + fix_up_interface_member_indices(storage, ib_type_id); + + // For patch inputs, add one more member, holding the array of control point data. + if (is_tese_shader() && !msl_options.raw_buffer_tese_input && storage == StorageClassInput && patch && + stage_in_var_id) + { + uint32_t pcp_type_id = ir.increase_bound_by(1); + auto &pcp_type = set(pcp_type_id, ib_type); + pcp_type.basetype = SPIRType::ControlPointArray; + pcp_type.parent_type = pcp_type.type_alias = get_stage_in_struct_type().self; + pcp_type.storage = storage; + ir.meta[pcp_type_id] = ir.meta[ib_type.self]; + uint32_t mbr_idx = uint32_t(ib_type.member_types.size()); + ib_type.member_types.push_back(pcp_type_id); + set_member_name(ib_type.self, mbr_idx, "gl_in"); + } + + if (storage == StorageClassInput) + set_decoration(ib_var_id, DecorationNonWritable); + + return ib_var_id; +} + +uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageClass storage) +{ + if (!ib_var_id) + return 0; + + uint32_t ib_ptr_var_id; + uint32_t next_id = ir.increase_bound_by(3); + auto &ib_type = expression_type(ib_var_id); + if (is_tesc_shader() || (is_tese_shader() && msl_options.raw_buffer_tese_input)) + { + // Tessellation control per-vertex I/O is presented as an array, so we must + // do the same with our struct here. + uint32_t ib_ptr_type_id = next_id++; + auto &ib_ptr_type = set(ib_ptr_type_id, ib_type); + ib_ptr_type.op = OpTypePointer; + ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self; + ib_ptr_type.pointer = true; + ib_ptr_type.pointer_depth++; + ib_ptr_type.storage = storage == StorageClassInput ? + ((is_tesc_shader() && msl_options.multi_patch_workgroup) || + (is_tese_shader() && msl_options.raw_buffer_tese_input) ? + StorageClassStorageBuffer : + StorageClassWorkgroup) : + StorageClassStorageBuffer; + ir.meta[ib_ptr_type_id] = ir.meta[ib_type.self]; + // To ensure that get_variable_data_type() doesn't strip off the pointer, + // which we need, use another pointer. + uint32_t ib_ptr_ptr_type_id = next_id++; + auto &ib_ptr_ptr_type = set(ib_ptr_ptr_type_id, ib_ptr_type); + ib_ptr_ptr_type.parent_type = ib_ptr_type_id; + ib_ptr_ptr_type.type_alias = ib_type.self; + ib_ptr_ptr_type.storage = StorageClassFunction; + ir.meta[ib_ptr_ptr_type_id] = ir.meta[ib_type.self]; + + ib_ptr_var_id = next_id; + set(ib_ptr_var_id, ib_ptr_ptr_type_id, StorageClassFunction, 0); + set_name(ib_ptr_var_id, storage == StorageClassInput ? "gl_in" : "gl_out"); + if (storage == StorageClassInput) + set_decoration(ib_ptr_var_id, DecorationNonWritable); + } + else + { + // Tessellation evaluation per-vertex inputs are also presented as arrays. + // But, in Metal, this array uses a very special type, 'patch_control_point', + // which is a container that can be used to access the control point data. + // To represent this, a special 'ControlPointArray' type has been added to the + // SPIRV-Cross type system. It should only be generated by and seen in the MSL + // backend (i.e. this one). + uint32_t pcp_type_id = next_id++; + auto &pcp_type = set(pcp_type_id, ib_type); + pcp_type.basetype = SPIRType::ControlPointArray; + pcp_type.parent_type = pcp_type.type_alias = ib_type.self; + pcp_type.storage = storage; + ir.meta[pcp_type_id] = ir.meta[ib_type.self]; + + ib_ptr_var_id = next_id; + set(ib_ptr_var_id, pcp_type_id, storage, 0); + set_name(ib_ptr_var_id, "gl_in"); + ir.meta[ib_ptr_var_id].decoration.qualified_alias = join(patch_stage_in_var_name, ".gl_in"); + } + return ib_ptr_var_id; +} + +uint32_t CompilerMSL::add_meshlet_block(bool per_primitive) +{ + // Accumulate the variables that should appear in the interface struct. + SmallVector vars; + + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (var.storage != StorageClassOutput || var.self == builtin_mesh_primitive_indices_id) + return; + if (is_per_primitive_variable(var) != per_primitive) + return; + vars.push_back(&var); + }); + + if (vars.empty()) + return 0; + + uint32_t next_id = ir.increase_bound_by(1); + auto &type = set(next_id, SPIRType(OpTypeStruct)); + type.basetype = SPIRType::Struct; + + InterfaceBlockMeta meta; + for (auto *p_var : vars) + { + meta.strip_array = true; + meta.allow_local_declaration = false; + add_variable_to_interface_block(StorageClassOutput, "", type, *p_var, meta); + } + + if (per_primitive) + set_name(type.self, "spvPerPrimitive"); + else + set_name(type.self, "spvPerVertex"); + + return next_id; +} + +// Ensure that the type is compatible with the builtin. +// If it is, simply return the given type ID. +// Otherwise, create a new type, and return it's ID. +uint32_t CompilerMSL::ensure_correct_builtin_type(uint32_t type_id, BuiltIn builtin) +{ + auto &type = get(type_id); + auto &pointee_type = get_pointee_type(type); + + if ((builtin == BuiltInSampleMask && is_array(pointee_type)) || + ((builtin == BuiltInLayer || builtin == BuiltInViewportIndex || builtin == BuiltInFragStencilRefEXT) && + pointee_type.basetype != SPIRType::UInt)) + { + uint32_t next_id = ir.increase_bound_by(is_pointer(type) ? 2 : 1); + uint32_t base_type_id = next_id++; + auto &base_type = set(base_type_id, OpTypeInt); + base_type.basetype = SPIRType::UInt; + base_type.width = 32; + + if (!is_pointer(type)) + return base_type_id; + + uint32_t ptr_type_id = next_id++; + auto &ptr_type = set(ptr_type_id, base_type); + ptr_type.op = OpTypePointer; + ptr_type.pointer = true; + ptr_type.pointer_depth++; + ptr_type.storage = type.storage; + ptr_type.parent_type = base_type_id; + return ptr_type_id; + } + + return type_id; +} + +// Ensure that the type is compatible with the shader input. +// If it is, simply return the given type ID. +// Otherwise, create a new type, and return its ID. +uint32_t CompilerMSL::ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t component, uint32_t num_components, bool strip_array) +{ + auto &type = get(type_id); + + uint32_t max_array_dimensions = strip_array ? 1 : 0; + + // Struct and array types must match exactly. + if (type.basetype == SPIRType::Struct || type.array.size() > max_array_dimensions) + return type_id; + + auto p_va = inputs_by_location.find({location, component}); + if (p_va == end(inputs_by_location)) + { + if (num_components > type.vecsize) + return build_extended_vector_type(type_id, num_components); + else + return type_id; + } + + if (num_components == 0) + num_components = p_va->second.vecsize; + + switch (p_va->second.format) + { + case MSL_SHADER_VARIABLE_FORMAT_UINT8: + { + switch (type.basetype) + { + case SPIRType::UByte: + case SPIRType::UShort: + case SPIRType::UInt: + if (num_components > type.vecsize) + return build_extended_vector_type(type_id, num_components); + else + return type_id; + + case SPIRType::Short: + return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize, + SPIRType::UShort); + case SPIRType::Int: + return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize, + SPIRType::UInt); + + default: + SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader"); + } + } + + case MSL_SHADER_VARIABLE_FORMAT_UINT16: + { + switch (type.basetype) + { + case SPIRType::UShort: + case SPIRType::UInt: + if (num_components > type.vecsize) + return build_extended_vector_type(type_id, num_components); + else + return type_id; + + case SPIRType::Int: + return build_extended_vector_type(type_id, num_components > type.vecsize ? num_components : type.vecsize, + SPIRType::UInt); + + default: + SPIRV_CROSS_THROW("Vertex attribute type mismatch between host and shader"); + } + } + + default: + if (num_components > type.vecsize) + type_id = build_extended_vector_type(type_id, num_components); + break; + } + + return type_id; +} + +void CompilerMSL::mark_struct_members_packed(const SPIRType &type) +{ + // Handle possible recursion when a struct contains a pointer to its own type nested somewhere. + if (has_extended_decoration(type.self, SPIRVCrossDecorationPhysicalTypePacked)) + return; + + set_extended_decoration(type.self, SPIRVCrossDecorationPhysicalTypePacked); + + // Problem case! Struct needs to be placed at an awkward alignment. + // Mark every member of the child struct as packed. + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < mbr_cnt; i++) + { + auto &mbr_type = get(type.member_types[i]); + if (mbr_type.basetype == SPIRType::Struct) + { + // Recursively mark structs as packed. + auto *struct_type = &mbr_type; + while (!struct_type->array.empty()) + struct_type = &get(struct_type->parent_type); + mark_struct_members_packed(*struct_type); + } + else if (!is_scalar(mbr_type)) + set_extended_member_decoration(type.self, i, SPIRVCrossDecorationPhysicalTypePacked); + } +} + +void CompilerMSL::mark_scalar_layout_structs(const SPIRType &type) +{ + uint32_t mbr_cnt = uint32_t(type.member_types.size()); + for (uint32_t i = 0; i < mbr_cnt; i++) + { + // Handle possible recursion when a struct contains a pointer to its own type nested somewhere. + auto &mbr_type = get(type.member_types[i]); + if (mbr_type.basetype == SPIRType::Struct && !(mbr_type.pointer && mbr_type.storage == StorageClassPhysicalStorageBuffer)) + { + auto *struct_type = &mbr_type; + while (!struct_type->array.empty()) + struct_type = &get(struct_type->parent_type); + + if (has_extended_decoration(struct_type->self, SPIRVCrossDecorationPhysicalTypePacked)) + continue; + + uint32_t msl_alignment = get_declared_struct_member_alignment_msl(type, i); + uint32_t msl_size = get_declared_struct_member_size_msl(type, i); + uint32_t spirv_offset = type_struct_member_offset(type, i); + uint32_t spirv_offset_next; + if (i + 1 < mbr_cnt) + spirv_offset_next = type_struct_member_offset(type, i + 1); + else + spirv_offset_next = spirv_offset + msl_size; + + // Both are complicated cases. In scalar layout, a struct of float3 might just consume 12 bytes, + // and the next member will be placed at offset 12. + bool struct_is_misaligned = (spirv_offset % msl_alignment) != 0; + bool struct_is_too_large = spirv_offset + msl_size > spirv_offset_next; + uint32_t array_stride = 0; + bool struct_needs_explicit_padding = false; + + // Verify that if a struct is used as an array that ArrayStride matches the effective size of the struct. + if (!mbr_type.array.empty()) + { + array_stride = type_struct_member_array_stride(type, i); + uint32_t dimensions = uint32_t(mbr_type.array.size() - 1); + for (uint32_t dim = 0; dim < dimensions; dim++) + { + uint32_t array_size = to_array_size_literal(mbr_type, dim); + array_stride /= max(array_size, 1u); + } + + // Set expected struct size based on ArrayStride. + struct_needs_explicit_padding = true; + + // If struct size is larger than array stride, we might be able to fit, if we tightly pack. + if (get_declared_struct_size_msl(*struct_type) > array_stride) + struct_is_too_large = true; + } + + if (struct_is_misaligned || struct_is_too_large) + mark_struct_members_packed(*struct_type); + mark_scalar_layout_structs(*struct_type); + + if (struct_needs_explicit_padding) + { + msl_size = get_declared_struct_size_msl(*struct_type); + + if (array_stride > msl_size) + { + set_decoration(struct_type->self, DecorationArrayStride, msl_size); + add_spv_func_and_recompile(SPVFuncImplPaddedArrayElement); + } + + if (array_stride < msl_size) + SPIRV_CROSS_THROW("Cannot express an array stride smaller than size of struct type."); + } + } + } +} + +// Sort the members of the struct type by offset, and pack and then pad members where needed +// to align MSL members with SPIR-V offsets. The struct members are iterated twice. Packing +// occurs first, followed by padding, because packing a member reduces both its size and its +// natural alignment, possibly requiring a padding member to be added ahead of it. +void CompilerMSL::align_struct(SPIRType &ib_type, unordered_set &aligned_structs) +{ + // We align structs recursively, so stop any redundant work. + ID &ib_type_id = ib_type.self; + if (aligned_structs.count(ib_type_id)) + return; + aligned_structs.insert(ib_type_id); + + // Sort the members of the interface structure by their offset. + // They should already be sorted per SPIR-V spec anyway. + MemberSorter member_sorter(ib_type, ir.meta[ib_type_id], MemberSorter::Offset); + member_sorter.sort(); + + auto mbr_cnt = uint32_t(ib_type.member_types.size()); + + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + // Pack any dependent struct types before we pack a parent struct. + auto &mbr_type = get(ib_type.member_types[mbr_idx]); + if (mbr_type.basetype == SPIRType::Struct) + align_struct(mbr_type, aligned_structs); + } + + // Test the alignment of each member, and if a member should be closer to the previous + // member than the default spacing expects, it is likely that the previous member is in + // a packed format. If so, and the previous member is packable, pack it. + // For example ... this applies to any 3-element vector that is followed by a scalar. + uint32_t msl_offset = 0; + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + // This checks the member in isolation, if the member needs some kind of type remapping to conform to SPIR-V + // offsets, array strides and matrix strides. + ensure_member_packing_rules_msl(ib_type, mbr_idx); + + // Align current offset to the current member's default alignment. If the member was packed, it will observe + // the updated alignment here. + uint32_t msl_align_mask = get_declared_struct_member_alignment_msl(ib_type, mbr_idx) - 1; + uint32_t aligned_msl_offset = (msl_offset + msl_align_mask) & ~msl_align_mask; + + // Fetch the member offset as declared in the SPIRV. + uint32_t spirv_mbr_offset = get_member_decoration(ib_type_id, mbr_idx, DecorationOffset); + if (spirv_mbr_offset > aligned_msl_offset) + { + // Since MSL and SPIR-V have slightly different struct member alignment and + // size rules, we'll pad to standard C-packing rules with a char[] array. If the member is farther + // away than C-packing, expects, add an inert padding member before the the member. + uint32_t padding_bytes = spirv_mbr_offset - aligned_msl_offset; + set_extended_member_decoration(ib_type_id, mbr_idx, SPIRVCrossDecorationPaddingTarget, padding_bytes); + + // Re-align as a sanity check that aligning post-padding matches up. + msl_offset += padding_bytes; + aligned_msl_offset = (msl_offset + msl_align_mask) & ~msl_align_mask; + } + else if (spirv_mbr_offset < aligned_msl_offset) + { + // This should not happen, but deal with unexpected scenarios. + // It *might* happen if a sub-struct has a larger alignment requirement in MSL than SPIR-V. + SPIRV_CROSS_THROW("Cannot represent buffer block correctly in MSL."); + } + + assert(aligned_msl_offset == spirv_mbr_offset); + + // Increment the current offset to be positioned immediately after the current member. + // Don't do this for the last member since it can be unsized, and it is not relevant for padding purposes here. + if (mbr_idx + 1 < mbr_cnt) + msl_offset = aligned_msl_offset + get_declared_struct_member_size_msl(ib_type, mbr_idx); + } +} + +bool CompilerMSL::validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const +{ + auto &mbr_type = get(type.member_types[index]); + uint32_t spirv_offset = get_member_decoration(type.self, index, DecorationOffset); + + if (index + 1 < type.member_types.size()) + { + // First, we will check offsets. If SPIR-V offset + MSL size > SPIR-V offset of next member, + // we *must* perform some kind of remapping, no way getting around it. + // We can always pad after this member if necessary, so that case is fine. + uint32_t spirv_offset_next = get_member_decoration(type.self, index + 1, DecorationOffset); + assert(spirv_offset_next >= spirv_offset); + uint32_t maximum_size = spirv_offset_next - spirv_offset; + uint32_t msl_mbr_size = get_declared_struct_member_size_msl(type, index); + if (msl_mbr_size > maximum_size) + return false; + } + + if (is_array(mbr_type)) + { + // If we have an array type, array stride must match exactly with SPIR-V. + + // An exception to this requirement is if we have one array element. + // This comes from DX scalar layout workaround. + // If app tries to be cheeky and access the member out of bounds, this will not work, but this is the best we can do. + // In OpAccessChain with logical memory models, access chains must be in-bounds in SPIR-V specification. + bool relax_array_stride = mbr_type.array.back() == 1 && mbr_type.array_size_literal.back(); + bool is_plain_struct = !mbr_type.pointer && mbr_type.basetype == SPIRType::Struct; + + // Array of struct is padded on-demand. + if (!relax_array_stride && !is_plain_struct) + { + uint32_t spirv_array_stride = type_struct_member_array_stride(type, index); + uint32_t msl_array_stride = get_declared_struct_member_array_stride_msl(type, index); + if (spirv_array_stride != msl_array_stride) + return false; + } + } + + if (is_matrix(mbr_type)) + { + // Need to check MatrixStride as well. + uint32_t spirv_matrix_stride = type_struct_member_matrix_stride(type, index); + uint32_t msl_matrix_stride = get_declared_struct_member_matrix_stride_msl(type, index); + if (spirv_matrix_stride != msl_matrix_stride) + return false; + } + + // Now, we check alignment. + uint32_t msl_alignment = get_declared_struct_member_alignment_msl(type, index); + if ((spirv_offset % msl_alignment) != 0) + return false; + + // We're in the clear. + return true; +} + +// Here we need to verify that the member type we declare conforms to Offset, ArrayStride or MatrixStride restrictions. +// If there is a mismatch, we need to emit remapped types, either normal types, or "packed_X" types. +// In odd cases we need to emit packed and remapped types, for e.g. weird matrices or arrays with weird array strides. +void CompilerMSL::ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index) +{ + if (validate_member_packing_rules_msl(ib_type, index)) + return; + + // We failed validation. + // This case will be nightmare-ish to deal with. This could possibly happen if struct alignment does not quite + // match up with what we want. Scalar block layout comes to mind here where we might have to work around the rule + // that struct alignment == max alignment of all members and struct size depends on this alignment. + // Can't repack structs, but can repack pointers to structs. + auto &mbr_type = get(ib_type.member_types[index]); + bool is_buff_ptr = mbr_type.pointer && mbr_type.storage == StorageClassPhysicalStorageBuffer; + if (mbr_type.basetype == SPIRType::Struct && !is_buff_ptr) + SPIRV_CROSS_THROW("Cannot perform any repacking for structs when it is used as a member of another struct."); + + // Perform remapping here. + // There is nothing to be gained by using packed scalars, so don't attempt it. + if (!is_scalar(ib_type)) + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + + // Try validating again, now with packed. + if (validate_member_packing_rules_msl(ib_type, index)) + return; + + // We're in deep trouble, and we need to create a new PhysicalType which matches up with what we expect. + // A lot of work goes here ... + // We will need remapping on Load and Store to translate the types between Logical and Physical. + + // First, we check if we have small vector std140 array. + // We detect this if we have an array of vectors, and array stride is greater than number of elements. + if (!mbr_type.array.empty() && !is_matrix(mbr_type)) + { + uint32_t array_stride = type_struct_member_array_stride(ib_type, index); + + // Hack off array-of-arrays until we find the array stride per element we must have to make it work. + uint32_t dimensions = uint32_t(mbr_type.array.size() - 1); + for (uint32_t dim = 0; dim < dimensions; dim++) + array_stride /= max(to_array_size_literal(mbr_type, dim), 1u); + + // Pointers are 8 bytes + uint32_t mbr_width_in_bytes = is_buff_ptr ? 8 : (mbr_type.width / 8); + uint32_t elems_per_stride = array_stride / mbr_width_in_bytes; + + if (elems_per_stride == 3) + SPIRV_CROSS_THROW("Cannot use ArrayStride of 3 elements in remapping scenarios."); + else if (elems_per_stride > 4 && elems_per_stride != 8) + SPIRV_CROSS_THROW("Cannot represent vectors with more than 4 elements in MSL."); + + if (elems_per_stride == 8) + { + if (mbr_type.width == 16) + add_spv_func_and_recompile(SPVFuncImplPaddedStd140); + else + SPIRV_CROSS_THROW("Unexpected type in std140 wide array resolve."); + } + + auto physical_type = mbr_type; + physical_type.vecsize = elems_per_stride; + physical_type.parent_type = 0; + + // If this is a physical buffer pointer, replace type with a ulongn vector. + if (is_buff_ptr) + { + physical_type.width = 64; + physical_type.basetype = to_unsigned_basetype(physical_type.width); + physical_type.pointer = false; + physical_type.pointer_depth = false; + physical_type.forward_pointer = false; + } + + uint32_t type_id = ir.increase_bound_by(1); + set(type_id, physical_type); + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID, type_id); + set_decoration(type_id, DecorationArrayStride, array_stride); + + // Remove packed_ for vectors of size 1, 2 and 4. + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + } + else if (is_matrix(mbr_type)) + { + // MatrixStride might be std140-esque. + uint32_t matrix_stride = type_struct_member_matrix_stride(ib_type, index); + + uint32_t elems_per_stride = matrix_stride / (mbr_type.width / 8); + + if (elems_per_stride == 3) + SPIRV_CROSS_THROW("Cannot use ArrayStride of 3 elements in remapping scenarios."); + else if (elems_per_stride > 4 && elems_per_stride != 8) + SPIRV_CROSS_THROW("Cannot represent vectors with more than 4 elements in MSL."); + + if (elems_per_stride == 8) + { + if (mbr_type.basetype != SPIRType::Half) + SPIRV_CROSS_THROW("Unexpected type in std140 wide matrix stride resolve."); + add_spv_func_and_recompile(SPVFuncImplPaddedStd140); + } + + bool row_major = has_member_decoration(ib_type.self, index, DecorationRowMajor); + auto physical_type = mbr_type; + physical_type.parent_type = 0; + + if (row_major) + physical_type.columns = elems_per_stride; + else + physical_type.vecsize = elems_per_stride; + uint32_t type_id = ir.increase_bound_by(1); + set(type_id, physical_type); + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID, type_id); + + // Remove packed_ for vectors of size 1, 2 and 4. + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + } + else + SPIRV_CROSS_THROW("Found a buffer packing case which we cannot represent in MSL."); + + // Try validating again, now with physical type remapping. + if (validate_member_packing_rules_msl(ib_type, index)) + return; + + // We might have a particular odd scalar layout case where the last element of an array + // does not take up as much space as the ArrayStride or MatrixStride. This can happen with DX cbuffers. + // The "proper" workaround for this is extremely painful and essentially impossible in the edge case of float3[], + // so we hack around it by declaring the offending array or matrix with one less array size/col/row, + // and rely on padding to get the correct value. We will technically access arrays out of bounds into the padding region, + // but it should spill over gracefully without too much trouble. We rely on behavior like this for unsized arrays anyways. + + // E.g. we might observe a physical layout of: + // { float2 a[2]; float b; } in cbuffer layout where ArrayStride of a is 16, but offset of b is 24, packed right after a[1] ... + uint32_t type_id = get_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID); + auto &type = get(type_id); + + // Modify the physical type in-place. This is safe since each physical type workaround is a copy. + if (is_array(type)) + { + if (type.array.back() > 1) + { + if (!type.array_size_literal.back()) + SPIRV_CROSS_THROW("Cannot apply scalar layout workaround with spec constant array size."); + type.array.back() -= 1; + } + else + { + // We have an array of size 1, so we cannot decrement that. Our only option now is to + // force a packed layout instead, and drop the physical type remap since ArrayStride is meaningless now. + unset_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypeID); + set_extended_member_decoration(ib_type.self, index, SPIRVCrossDecorationPhysicalTypePacked); + } + } + else if (is_matrix(type)) + { + bool row_major = has_member_decoration(ib_type.self, index, DecorationRowMajor); + if (!row_major) + { + // Slice off one column. If we only have 2 columns, this might turn the matrix into a vector with one array element instead. + if (type.columns > 2) + { + type.columns--; + } + else if (type.columns == 2) + { + type.columns = 1; + assert(type.array.empty()); + type.op = OpTypeArray; + type.array.push_back(1); + type.array_size_literal.push_back(true); + } + } + else + { + // Slice off one row. If we only have 2 rows, this might turn the matrix into a vector with one array element instead. + if (type.vecsize > 2) + { + type.vecsize--; + } + else if (type.vecsize == 2) + { + type.vecsize = type.columns; + type.columns = 1; + assert(type.array.empty()); + type.op = OpTypeArray; + type.array.push_back(1); + type.array_size_literal.push_back(true); + } + } + } + + // This better validate now, or we must fail gracefully. + if (!validate_member_packing_rules_msl(ib_type, index)) + SPIRV_CROSS_THROW("Found a buffer packing case which we cannot represent in MSL."); +} + +void CompilerMSL::emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) +{ + auto &type = expression_type(rhs_expression); + + bool lhs_remapped_type = has_extended_decoration(lhs_expression, SPIRVCrossDecorationPhysicalTypeID); + bool lhs_packed_type = has_extended_decoration(lhs_expression, SPIRVCrossDecorationPhysicalTypePacked); + auto *lhs_e = maybe_get(lhs_expression); + auto *rhs_e = maybe_get(rhs_expression); + + bool transpose = lhs_e && lhs_e->need_transpose; + + if (has_decoration(lhs_expression, DecorationBuiltIn) && + BuiltIn(get_decoration(lhs_expression, DecorationBuiltIn)) == BuiltInSampleMask && + is_array(type)) + { + // Storing an array to SampleMask, have to remove the array-ness before storing. + statement(to_expression(lhs_expression), " = ", to_enclosed_unpacked_expression(rhs_expression), "[0];"); + register_write(lhs_expression); + } + else if (!lhs_remapped_type && !lhs_packed_type) + { + // No physical type remapping, and no packed type, so can just emit a store directly. + + // We might not be dealing with remapped physical types or packed types, + // but we might be doing a clean store to a row-major matrix. + // In this case, we just flip transpose states, and emit the store, a transpose must be in the RHS expression, if any. + if (is_matrix(type) && lhs_e && lhs_e->need_transpose) + { + lhs_e->need_transpose = false; + + if (rhs_e && rhs_e->need_transpose) + { + // Direct copy, but might need to unpack RHS. + // Skip the transpose, as we will transpose when writing to LHS and transpose(transpose(T)) == T. + rhs_e->need_transpose = false; + statement(to_expression(lhs_expression), " = ", to_unpacked_row_major_matrix_expression(rhs_expression), + ";"); + rhs_e->need_transpose = true; + } + else + statement(to_expression(lhs_expression), " = transpose(", to_unpacked_expression(rhs_expression), ");"); + + lhs_e->need_transpose = true; + register_write(lhs_expression); + } + else if (lhs_e && lhs_e->need_transpose) + { + lhs_e->need_transpose = false; + + // Storing a column to a row-major matrix. Unroll the write. + for (uint32_t c = 0; c < type.vecsize; c++) + { + auto lhs_expr = to_dereferenced_expression(lhs_expression); + auto column_index = lhs_expr.find_last_of('['); + if (column_index != string::npos) + { + statement(lhs_expr.insert(column_index, join('[', c, ']')), " = ", + to_extract_component_expression(rhs_expression, c), ";"); + } + } + lhs_e->need_transpose = true; + register_write(lhs_expression); + } + else + CompilerGLSL::emit_store_statement(lhs_expression, rhs_expression); + } + else if (!lhs_remapped_type && !is_matrix(type) && !transpose) + { + // Even if the target type is packed, we can directly store to it. We cannot store to packed matrices directly, + // since they are declared as array of vectors instead, and we need the fallback path below. + CompilerGLSL::emit_store_statement(lhs_expression, rhs_expression); + } + else + { + // Special handling when storing to a remapped physical type. + // This is mostly to deal with std140 padded matrices or vectors. + + TypeID physical_type_id = lhs_remapped_type ? + ID(get_extended_decoration(lhs_expression, SPIRVCrossDecorationPhysicalTypeID)) : + type.self; + + auto &physical_type = get(physical_type_id); + + string cast_addr_space = "thread"; + auto *p_var_lhs = maybe_get_backing_variable(lhs_expression); + if (p_var_lhs) + cast_addr_space = get_type_address_space(get(p_var_lhs->basetype), lhs_expression); + + if (is_matrix(type)) + { + const char *packed_pfx = lhs_packed_type ? "packed_" : ""; + + // Packed matrices are stored as arrays of packed vectors, so we need + // to assign the vectors one at a time. + // For row-major matrices, we need to transpose the *right-hand* side, + // not the left-hand side. + + // Lots of cases to cover here ... + + bool rhs_transpose = rhs_e && rhs_e->need_transpose; + SPIRType write_type = type; + string cast_expr; + + // We're dealing with transpose manually. + if (rhs_transpose) + rhs_e->need_transpose = false; + + if (transpose) + { + // We're dealing with transpose manually. + lhs_e->need_transpose = false; + write_type.vecsize = type.columns; + write_type.columns = 1; + + if (physical_type.columns != type.columns) + cast_expr = join("(", cast_addr_space, " ", packed_pfx, type_to_glsl(write_type), "&)"); + + if (rhs_transpose) + { + // If RHS is also transposed, we can just copy row by row. + for (uint32_t i = 0; i < type.vecsize; i++) + { + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", + to_unpacked_row_major_matrix_expression(rhs_expression), "[", i, "];"); + } + } + else + { + auto vector_type = expression_type(rhs_expression); + vector_type.vecsize = vector_type.columns; + vector_type.columns = 1; + + // Transpose on the fly. Emitting a lot of full transpose() ops and extracting lanes seems very bad, + // so pick out individual components instead. + for (uint32_t i = 0; i < type.vecsize; i++) + { + string rhs_row = type_to_glsl_constructor(vector_type) + "("; + for (uint32_t j = 0; j < vector_type.vecsize; j++) + { + rhs_row += join(to_enclosed_unpacked_expression(rhs_expression), "[", j, "][", i, "]"); + if (j + 1 < vector_type.vecsize) + rhs_row += ", "; + } + rhs_row += ")"; + + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", rhs_row, ";"); + } + } + + // We're dealing with transpose manually. + lhs_e->need_transpose = true; + } + else + { + write_type.columns = 1; + + if (physical_type.vecsize != type.vecsize) + cast_expr = join("(", cast_addr_space, " ", packed_pfx, type_to_glsl(write_type), "&)"); + + if (rhs_transpose) + { + auto vector_type = expression_type(rhs_expression); + vector_type.columns = 1; + + // Transpose on the fly. Emitting a lot of full transpose() ops and extracting lanes seems very bad, + // so pick out individual components instead. + for (uint32_t i = 0; i < type.columns; i++) + { + string rhs_row = type_to_glsl_constructor(vector_type) + "("; + for (uint32_t j = 0; j < vector_type.vecsize; j++) + { + // Need to explicitly unpack expression since we've mucked with transpose state. + auto unpacked_expr = to_unpacked_row_major_matrix_expression(rhs_expression); + rhs_row += join(unpacked_expr, "[", j, "][", i, "]"); + if (j + 1 < vector_type.vecsize) + rhs_row += ", "; + } + rhs_row += ")"; + + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", rhs_row, ";"); + } + } + else + { + // Copy column-by-column. + for (uint32_t i = 0; i < type.columns; i++) + { + statement(cast_expr, to_enclosed_expression(lhs_expression), "[", i, "]", " = ", + to_enclosed_unpacked_expression(rhs_expression), "[", i, "];"); + } + } + } + + // We're dealing with transpose manually. + if (rhs_transpose) + rhs_e->need_transpose = true; + } + else if (transpose) + { + lhs_e->need_transpose = false; + + SPIRType write_type = type; + write_type.vecsize = 1; + write_type.columns = 1; + + // Storing a column to a row-major matrix. Unroll the write. + for (uint32_t c = 0; c < type.vecsize; c++) + { + auto lhs_expr = to_enclosed_expression(lhs_expression); + auto column_index = lhs_expr.find_last_of('['); + + // Get rid of any ".data" half8 handling here, we're casting to scalar anyway. + auto end_column_index = lhs_expr.find_last_of(']'); + auto end_dot_index = lhs_expr.find_last_of('.'); + if (end_dot_index != string::npos && end_dot_index > end_column_index) + lhs_expr.resize(end_dot_index); + + if (column_index != string::npos) + { + statement("((", cast_addr_space, " ", type_to_glsl(write_type), "*)&", + lhs_expr.insert(column_index, join('[', c, ']', ")")), " = ", + to_extract_component_expression(rhs_expression, c), ";"); + } + } + + lhs_e->need_transpose = true; + } + else if ((is_matrix(physical_type) || is_array(physical_type)) && + physical_type.vecsize <= 4 && + physical_type.vecsize > type.vecsize) + { + assert(type.vecsize >= 1 && type.vecsize <= 3); + + // If we have packed types, we cannot use swizzled stores. + // We could technically unroll the store for each element if needed. + // When remapping to a std140 physical type, we always get float4, + // and the packed decoration should always be removed. + assert(!lhs_packed_type); + + string lhs = to_dereferenced_expression(lhs_expression); + string rhs = to_pointer_expression(rhs_expression); + + // Unpack the expression so we can store to it with a float or float2. + // It's still an l-value, so it's fine. Most other unpacking of expressions turn them into r-values instead. + lhs = join("(", cast_addr_space, " ", type_to_glsl(type), "&)", enclose_expression(lhs)); + if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + } + else if (!is_matrix(type)) + { + string lhs = to_dereferenced_expression(lhs_expression); + string rhs = to_pointer_expression(rhs_expression); + if (!optimize_read_modify_write(expression_type(rhs_expression), lhs, rhs)) + statement(lhs, " = ", rhs, ";"); + } + + register_write(lhs_expression); + } +} + +static bool expression_ends_with(const string &expr_str, const std::string &ending) +{ + if (expr_str.length() >= ending.length()) + return (expr_str.compare(expr_str.length() - ending.length(), ending.length(), ending) == 0); + else + return false; +} + +// Converts the format of the current expression from packed to unpacked, +// by wrapping the expression in a constructor of the appropriate type. +// Also, handle special physical ID remapping scenarios, similar to emit_store_statement(). +string CompilerMSL::unpack_expression_type(string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool packed, bool row_major) +{ + // Trivial case, nothing to do. + if (physical_type_id == 0 && !packed) + return expr_str; + + const SPIRType *physical_type = nullptr; + if (physical_type_id) + physical_type = &get(physical_type_id); + + static const char *swizzle_lut[] = { + ".x", + ".xy", + ".xyz", + "", + }; + + // TODO: Move everything to the template wrapper? + bool uses_std140_wrapper = physical_type && physical_type->vecsize > 4; + + if (physical_type && is_vector(*physical_type) && is_array(*physical_type) && + !uses_std140_wrapper && + physical_type->vecsize > type.vecsize && !expression_ends_with(expr_str, swizzle_lut[type.vecsize - 1])) + { + // std140 array cases for vectors. + assert(type.vecsize >= 1 && type.vecsize <= 3); + return enclose_expression(expr_str) + swizzle_lut[type.vecsize - 1]; + } + else if (physical_type && is_matrix(*physical_type) && is_vector(type) && + !uses_std140_wrapper && + physical_type->vecsize > type.vecsize) + { + // Extract column from padded matrix. + assert(type.vecsize >= 1 && type.vecsize <= 4); + return enclose_expression(expr_str) + swizzle_lut[type.vecsize - 1]; + } + else if (is_matrix(type)) + { + // Packed matrices are stored as arrays of packed vectors. Unfortunately, + // we can't just pass the array straight to the matrix constructor. We have to + // pass each vector individually, so that they can be unpacked to normal vectors. + if (!physical_type) + physical_type = &type; + + uint32_t vecsize = type.vecsize; + uint32_t columns = type.columns; + if (row_major) + swap(vecsize, columns); + + uint32_t physical_vecsize = row_major ? physical_type->columns : physical_type->vecsize; + + const char *base_type = type.width == 16 ? "half" : "float"; + string unpack_expr = join(base_type, columns, "x", vecsize, "("); + + const char *load_swiz = ""; + const char *data_swiz = physical_vecsize > 4 ? ".data" : ""; + + if (physical_vecsize != vecsize) + load_swiz = swizzle_lut[vecsize - 1]; + + for (uint32_t i = 0; i < columns; i++) + { + if (i > 0) + unpack_expr += ", "; + + if (packed) + unpack_expr += join(base_type, physical_vecsize, "(", expr_str, "[", i, "]", ")", load_swiz); + else + unpack_expr += join(expr_str, "[", i, "]", data_swiz, load_swiz); + } + + unpack_expr += ")"; + return unpack_expr; + } + else + { + return join(type_to_glsl(type), "(", expr_str, ")"); + } +} + +// Emits the file header info +void CompilerMSL::emit_header() +{ + // This particular line can be overridden during compilation, so make it a flag and not a pragma line. + if (suppress_missing_prototypes) + add_pragma_line("#pragma clang diagnostic ignored \"-Wmissing-prototypes\"", false); + if (suppress_incompatible_pointer_types_discard_qualifiers) + add_pragma_line("#pragma clang diagnostic ignored \"-Wincompatible-pointer-types-discards-qualifiers\"", false); + + // Disable warning about "sometimes unitialized" when zero-initializing simple threadgroup variables + if (suppress_sometimes_unitialized) + add_pragma_line("#pragma clang diagnostic ignored \"-Wsometimes-uninitialized\"", false); + + // Disable warning about missing braces for array template to make arrays a value type + if (spv_function_implementations.count(SPVFuncImplUnsafeArray) != 0) + add_pragma_line("#pragma clang diagnostic ignored \"-Wmissing-braces\"", false); + + // Floating point fast math compile declarations + if (msl_options.use_fast_math_pragmas && msl_options.supports_msl_version(3, 2)) + { + uint32_t contract_mask = FPFastMathModeAllowContractMask; + uint32_t relax_mask = (FPFastMathModeNSZMask | FPFastMathModeAllowRecipMask | FPFastMathModeAllowReassocMask); + uint32_t fast_mask = (relax_mask | FPFastMathModeNotNaNMask | FPFastMathModeNotInfMask); + + // FP math mode + uint32_t fp_flags = get_fp_fast_math_flags(true); + const char *math_mode = "safe"; + if ((fp_flags & fast_mask) == fast_mask) // Must have all flags + math_mode = "fast"; + else if ((fp_flags & relax_mask) == relax_mask) // Must have all flags + math_mode = "relaxed"; + + add_pragma_line(join("#pragma metal fp math_mode(", math_mode, ")"), false); + + // FP contraction + const char *contract_mode = ((fp_flags & contract_mask) == contract_mask) ? "fast" : "off"; + add_pragma_line(join("#pragma metal fp contract(", contract_mode, ")"), false); + } + + for (auto &pragma : pragma_lines) + statement(pragma); + + if (!pragma_lines.empty()) + statement(""); + + statement("#include "); + statement("#include "); + + for (auto &header : header_lines) + statement(header); + + statement(""); + statement("using namespace metal;"); + statement(""); + + for (auto &td : typedef_lines) + statement(td); + + if (!typedef_lines.empty()) + statement(""); +} + +void CompilerMSL::add_pragma_line(const string &line, bool recompile_on_unique) +{ + if (std::find(pragma_lines.begin(), pragma_lines.end(), line) == pragma_lines.end()) + { + pragma_lines.push_back(line); + if (recompile_on_unique) + force_recompile(); + } +} + +void CompilerMSL::add_typedef_line(const string &line) +{ + if (std::find(typedef_lines.begin(), typedef_lines.end(), line) == typedef_lines.end()) + { + typedef_lines.push_back(line); + force_recompile(); + } +} + +// Template struct like spvUnsafeArray<> need to be declared *before* any resources are declared +void CompilerMSL::emit_custom_templates() +{ + static const char * const address_spaces[] = { + "thread", "constant", "device", "threadgroup", "threadgroup_imageblock", "ray_data", "object_data" + }; + + for (const auto &spv_func : spv_function_implementations) + { + switch (spv_func) + { + case SPVFuncImplUnsafeArray: + statement("template"); + statement("struct spvUnsafeArray"); + begin_scope(); + statement("T elements[Num ? Num : 1];"); + statement(""); + statement("thread T& operator [] (size_t pos) thread"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement("constexpr const thread T& operator [] (size_t pos) const thread"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement(""); + statement("device T& operator [] (size_t pos) device"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement("constexpr const device T& operator [] (size_t pos) const device"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement(""); + statement("constexpr const constant T& operator [] (size_t pos) const constant"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement(""); + statement("threadgroup T& operator [] (size_t pos) threadgroup"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement("constexpr const threadgroup T& operator [] (size_t pos) const threadgroup"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + if (get_execution_model() == ExecutionModelMeshEXT || + get_execution_model() == ExecutionModelTaskEXT) + { + statement(""); + statement("object_data T& operator [] (size_t pos) object_data"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + statement("constexpr const object_data T& operator [] (size_t pos) const object_data"); + begin_scope(); + statement("return elements[pos];"); + end_scope(); + } + end_scope_decl(); + statement(""); + break; + + case SPVFuncImplStorageMatrix: + statement("template"); + statement("struct spvStorageMatrix"); + begin_scope(); + statement("vec columns[Cols];"); + statement(""); + for (size_t method_idx = 0; method_idx < sizeof(address_spaces) / sizeof(address_spaces[0]); ++method_idx) + { + // Some address spaces require particular features. + if (method_idx == 4) // threadgroup_imageblock + statement("#ifdef __HAVE_IMAGEBLOCKS__"); + else if (method_idx == 5) // ray_data + statement("#ifdef __HAVE_RAYTRACING__"); + else if (method_idx == 6) // object_data + statement("#ifdef __HAVE_MESH__"); + const string &method_as = address_spaces[method_idx]; + statement("spvStorageMatrix() ", method_as, " = default;"); + if (method_idx != 1) // constant + { + statement(method_as, " spvStorageMatrix& operator=(initializer_list> cols) ", + method_as); + begin_scope(); + statement("size_t i;"); + statement("thread vec* col;"); + statement("for (i = 0, col = cols.begin(); i < Cols; ++i, ++col)"); + statement(" columns[i] = *col;"); + statement("return *this;"); + end_scope(); + } + statement(""); + for (size_t param_idx = 0; param_idx < sizeof(address_spaces) / sizeof(address_spaces[0]); ++param_idx) + { + if (param_idx != method_idx) + { + if (param_idx == 4) // threadgroup_imageblock + statement("#ifdef __HAVE_IMAGEBLOCKS__"); + else if (param_idx == 5) // ray_data + statement("#ifdef __HAVE_RAYTRACING__"); + else if (param_idx == 6) // object_data + statement("#ifdef __HAVE_MESH__"); + } + const string ¶m_as = address_spaces[param_idx]; + statement("spvStorageMatrix(const ", param_as, " matrix& m) ", method_as); + begin_scope(); + statement("for (size_t i = 0; i < Cols; ++i)"); + statement(" columns[i] = m.columns[i];"); + end_scope(); + statement("spvStorageMatrix(const ", param_as, " spvStorageMatrix& m) ", method_as, " = default;"); + if (method_idx != 1) // constant + { + statement(method_as, " spvStorageMatrix& operator=(const ", param_as, + " matrix& m) ", method_as); + begin_scope(); + statement("for (size_t i = 0; i < Cols; ++i)"); + statement(" columns[i] = m.columns[i];"); + statement("return *this;"); + end_scope(); + statement(method_as, " spvStorageMatrix& operator=(const ", param_as, " spvStorageMatrix& m) ", + method_as, " = default;"); + } + if (param_idx != method_idx && param_idx >= 4) + statement("#endif"); + statement(""); + } + statement("operator matrix() const ", method_as); + begin_scope(); + statement("matrix m;"); + statement("for (int i = 0; i < Cols; ++i)"); + statement(" m.columns[i] = columns[i];"); + statement("return m;"); + end_scope(); + statement(""); + statement("vec operator[](size_t idx) const ", method_as); + begin_scope(); + statement("return columns[idx];"); + end_scope(); + if (method_idx != 1) // constant + { + statement(method_as, " vec& operator[](size_t idx) ", method_as); + begin_scope(); + statement("return columns[idx];"); + end_scope(); + } + if (method_idx >= 4) + statement("#endif"); + statement(""); + } + end_scope_decl(); + statement(""); + statement("template"); + statement("matrix transpose(spvStorageMatrix m)"); + begin_scope(); + statement("return transpose(matrix(m));"); + end_scope(); + statement(""); + statement("typedef spvStorageMatrix spvStorage_half2x2;"); + statement("typedef spvStorageMatrix spvStorage_half2x3;"); + statement("typedef spvStorageMatrix spvStorage_half2x4;"); + statement("typedef spvStorageMatrix spvStorage_half3x2;"); + statement("typedef spvStorageMatrix spvStorage_half3x3;"); + statement("typedef spvStorageMatrix spvStorage_half3x4;"); + statement("typedef spvStorageMatrix spvStorage_half4x2;"); + statement("typedef spvStorageMatrix spvStorage_half4x3;"); + statement("typedef spvStorageMatrix spvStorage_half4x4;"); + statement("typedef spvStorageMatrix spvStorage_float2x2;"); + statement("typedef spvStorageMatrix spvStorage_float2x3;"); + statement("typedef spvStorageMatrix spvStorage_float2x4;"); + statement("typedef spvStorageMatrix spvStorage_float3x2;"); + statement("typedef spvStorageMatrix spvStorage_float3x3;"); + statement("typedef spvStorageMatrix spvStorage_float3x4;"); + statement("typedef spvStorageMatrix spvStorage_float4x2;"); + statement("typedef spvStorageMatrix spvStorage_float4x3;"); + statement("typedef spvStorageMatrix spvStorage_float4x4;"); + statement(""); + break; + + default: + break; + } + } +} + +// Emits any needed custom function bodies. +// Metal helper functions must be static force-inline, i.e. static inline __attribute__((always_inline)) +// otherwise they will cause problems when linked together in a single Metallib. +void CompilerMSL::emit_custom_functions() +{ + // Use when outputting overloaded functions to cover different address spaces. + static const char *texture_addr_spaces[] = { "device", "constant", "thread" }; + static uint32_t texture_addr_space_count = sizeof(texture_addr_spaces) / sizeof(char*); + + if (spv_function_implementations.count(SPVFuncImplArrayCopyMultidim)) + spv_function_implementations.insert(SPVFuncImplArrayCopy); + + if (spv_function_implementations.count(SPVFuncImplDynamicImageSampler)) + { + // Unfortunately, this one needs a lot of the other functions to compile OK. + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW( + "spvDynamicImageSampler requires default-constructible texture objects, which require MSL 2.0."); + spv_function_implementations.insert(SPVFuncImplTextureSwizzle); + if (msl_options.swizzle_texture_samples) + spv_function_implementations.insert(SPVFuncImplGatherSwizzle); + for (uint32_t i = SPVFuncImplChromaReconstructNearest2Plane; + i <= SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane; i++) + spv_function_implementations.insert(static_cast(i)); + spv_function_implementations.insert(SPVFuncImplExpandITUFullRange); + spv_function_implementations.insert(SPVFuncImplExpandITUNarrowRange); + spv_function_implementations.insert(SPVFuncImplConvertYCbCrBT709); + spv_function_implementations.insert(SPVFuncImplConvertYCbCrBT601); + spv_function_implementations.insert(SPVFuncImplConvertYCbCrBT2020); + } + + if (spv_function_implementations.count(SPVFuncImplGatherSwizzle) || + spv_function_implementations.count(SPVFuncImplGatherConstOffsets)) + { + spv_function_implementations.insert(SPVFuncImplGatherReturn); + } + + if (spv_function_implementations.count(SPVFuncImplGatherCompareSwizzle) || + spv_function_implementations.count(SPVFuncImplGatherCompareConstOffsets)) + { + spv_function_implementations.insert(SPVFuncImplGatherCompareReturn); + } + + if (spv_function_implementations.count(SPVFuncImplTextureSwizzle) || + spv_function_implementations.count(SPVFuncImplGatherSwizzle) || + spv_function_implementations.count(SPVFuncImplGatherCompareSwizzle)) + { + spv_function_implementations.insert(SPVFuncImplGetSwizzle); + } + + for (const auto &spv_func : spv_function_implementations) + { + switch (spv_func) + { + case SPVFuncImplSMod: + statement("// Implementation of signed integer mod accurate to SPIR-V specification"); + statement("template"); + statement("inline Tx spvSMod(Tx x, Ty y)"); + begin_scope(); + statement("Tx remainder = x - y * (x / y);"); + statement("return select(Tx(remainder + y), remainder, remainder == 0 || (x >= 0) == (y >= 0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplMod: + statement("// Implementation of the GLSL mod() function, which is slightly different than Metal fmod()"); + statement("template"); + statement("inline Tx mod(Tx x, Ty y)"); + begin_scope(); + statement("return x - y * floor(x / y);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplRadians: + statement("// Implementation of the GLSL radians() function"); + statement("template"); + statement("inline T radians(T d)"); + begin_scope(); + statement("return d * T(0.01745329251);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplDegrees: + statement("// Implementation of the GLSL degrees() function"); + statement("template"); + statement("inline T degrees(T r)"); + begin_scope(); + statement("return r * T(57.2957795131);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplFindILsb: + statement("// Implementation of the GLSL findLSB() function"); + statement("template"); + statement("inline T spvFindLSB(T x)"); + begin_scope(); + statement("return select(ctz(x), T(-1), x == T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplFindUMsb: + statement("// Implementation of the unsigned GLSL findMSB() function"); + statement("template"); + statement("inline T spvFindUMSB(T x)"); + begin_scope(); + statement("return select(clz(T(0)) - (clz(x) + T(1)), T(-1), x == T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplFindSMsb: + statement("// Implementation of the signed GLSL findMSB() function"); + statement("template"); + statement("inline T spvFindSMSB(T x)"); + begin_scope(); + statement("T v = select(x, T(-1) - x, x < T(0));"); + statement("return select(clz(T(0)) - (clz(v) + T(1)), T(-1), v == T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSSign: + statement("// Implementation of the GLSL sign() function for integer types"); + statement("template::value>::type>"); + statement("inline T sign(T x)"); + begin_scope(); + statement("return select(select(select(x, T(0), x == T(0)), T(1), x > T(0)), T(-1), x < T(0));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplArrayCopy: + case SPVFuncImplArrayCopyMultidim: + { + // Unfortunately we cannot template on the address space, so combinatorial explosion it is. + static const char *function_name_tags[] = { + "FromConstantToStack", "FromConstantToThreadGroup", "FromStackToStack", + "FromStackToThreadGroup", "FromThreadGroupToStack", "FromThreadGroupToThreadGroup", + "FromDeviceToDevice", "FromConstantToDevice", "FromStackToDevice", + "FromThreadGroupToDevice", "FromDeviceToStack", "FromDeviceToThreadGroup", + }; + + static const char *src_address_space[] = { + "constant", "constant", "thread const", "thread const", + "threadgroup const", "threadgroup const", "device const", "constant", + "thread const", "threadgroup const", "device const", "device const", + }; + + static const char *dst_address_space[] = { + "thread", "threadgroup", "thread", "threadgroup", "thread", "threadgroup", + "device", "device", "device", "device", "thread", "threadgroup", + }; + + static const bool src_is_physical_with_mismatch[] = { + true, true, false, + false, false, false, + false, false, false, + false, true, true, + }; + + static const bool dst_is_physical_with_mismatch[] = { + false, false, false, + false, false, false, + false, false, true, + true, false, false, + }; + + for (uint32_t variant = 0; variant < 12; variant++) + { + assert(!src_is_physical_with_mismatch[variant] || !dst_is_physical_with_mismatch[variant]); + bool is_multidim = spv_func == SPVFuncImplArrayCopyMultidim; + const char *dim = is_multidim ? "[N][M]" : "[N]"; + + // Simple base case. + statement("template" : ">"); + statement("inline void spvArrayCopy", function_name_tags[variant], "(", + dst_address_space[variant], " T (&dst)", dim, ", ", + src_address_space[variant], " T (&src)", dim, ")"); + begin_scope(); + statement("for (uint i = 0; i < N; i++)"); + begin_scope(); + if (is_multidim) + statement("spvArrayCopy", function_name_tags[variant], "(dst[i], src[i]);"); + else + statement("dst[i] = src[i];"); + end_scope(); + end_scope(); + + if (spv_function_implementations.count(SPVFuncImplArrayCopyExtendedSrc) && + src_is_physical_with_mismatch[variant]) + { + // 1st overload, src can be magic vector where dst is a scalar. + // Need reinterpret casts to be memory model correct. LLVM vectors are broken otherwise. + statement("template" : ">"); + statement("inline void spvArrayCopy", function_name_tags[variant], "(", + dst_address_space[variant], " T (&dst)", dim, ", ", + src_address_space[variant], " vec (&src)", dim, ")"); + begin_scope(); + statement("for (uint i = 0; i < N; i++)"); + begin_scope(); + if (is_multidim) + statement("spvArrayCopy", function_name_tags[variant], "(dst[i], src[i]);"); + else + statement("dst[i] = reinterpret_cast<", src_address_space[variant], " T &>(src[i]);"); + end_scope(); + end_scope(); + + statement(""); + + // 2nd overload, both are vectors, but need SFINAE magic to avoid ambiguous case. + statement("template" : ">"); + statement("inline enable_if_t spvArrayCopy", function_name_tags[variant], "(", + dst_address_space[variant], " vec (&dst)", dim, ", ", + src_address_space[variant], " vec (&src)", dim, ")"); + begin_scope(); + statement("for (uint i = 0; i < N; i++)"); + begin_scope(); + if (is_multidim) + statement("spvArrayCopy", function_name_tags[variant], "(dst[i], src[i]);"); + else + statement("dst[i] = reinterpret_cast<", src_address_space[variant], " vec &>(src[i]);"); + end_scope(); + end_scope(); + } + + if (spv_function_implementations.count(SPVFuncImplArrayCopyExtendedDst) && + dst_is_physical_with_mismatch[variant]) + { + // 1st overload, src can be magic vector where dst is a scalar. + // Need reinterpret casts to be memory model correct. LLVM vectors are broken otherwise. + statement("template" : ">"); + statement("inline void spvArrayCopy", function_name_tags[variant], "(", + dst_address_space[variant], " vec (&dst)", dim, ", ", + src_address_space[variant], " T (&src)", dim, ")"); + begin_scope(); + statement("for (uint i = 0; i < N; i++)"); + begin_scope(); + if (is_multidim) + statement("spvArrayCopy", function_name_tags[variant], "(dst[i], src[i]);"); + else + statement("reinterpret_cast<", dst_address_space[variant], " T &>(dst[i]) = src[i];"); + end_scope(); + end_scope(); + + statement(""); + + // 2nd overload, both are vectors, but need SFINAE magic to avoid ambiguous case. + statement("template" : ">"); + statement("inline enable_if_t spvArrayCopy", function_name_tags[variant], "(", + dst_address_space[variant], " vec (&dst)", dim, ", ", + src_address_space[variant], " vec (&src)", dim, ")"); + begin_scope(); + statement("for (uint i = 0; i < N; i++)"); + begin_scope(); + if (is_multidim) + statement("spvArrayCopy", function_name_tags[variant], "(dst[i], src[i]);"); + else + statement("reinterpret_cast<", dst_address_space[variant], " vec &>(dst[i]) = src[i];"); + end_scope(); + end_scope(); + } + + statement(""); + } + break; + } + + // Support for Metal 2.1's new texture_buffer type. + case SPVFuncImplTexelBufferCoords: + { + if (msl_options.texel_buffer_texture_width > 0) + { + 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(force_inline); + statement("uint2 spvTexelBufferCoord(uint tc)"); + begin_scope(); + statement(join("return uint2(tc % ", tex_width_str, ", tc / ", tex_width_str, ");")); + end_scope(); + statement(""); + } + else + { + statement("// Returns 2D texture coords corresponding to 1D texel buffer coords"); + statement( + "#define spvTexelBufferCoord(tc, tex) uint2((tc) % (tex).get_width(), (tc) / (tex).get_width())"); + statement(""); + } + break; + } + + // Emulate texture2D atomic operations + case SPVFuncImplImage2DAtomicCoords: + { + if (msl_options.supports_msl_version(1, 2)) + { + statement("// The required alignment of a linear texture of R32Uint format."); + statement("constant uint spvLinearTextureAlignmentOverride [[function_constant(", + msl_options.r32ui_alignment_constant_id, ")]];"); + statement("constant uint spvLinearTextureAlignment = ", + "is_function_constant_defined(spvLinearTextureAlignmentOverride) ? ", + "spvLinearTextureAlignmentOverride : ", msl_options.r32ui_linear_texture_alignment, ";"); + } + else + { + statement("// The required alignment of a linear texture of R32Uint format."); + statement("constant uint spvLinearTextureAlignment = ", msl_options.r32ui_linear_texture_alignment, + ";"); + } + statement("// Returns buffer coords corresponding to 2D texture coords for emulating 2D texture atomics"); + statement("#define spvImage2DAtomicCoord(tc, tex) (((((tex).get_width() + ", + " spvLinearTextureAlignment / 4 - 1) & ~(", + " spvLinearTextureAlignment / 4 - 1)) * (tc).y) + (tc).x)"); + statement(""); + break; + } + + // Fix up gradient vectors when sampling a cube texture for Apple Silicon. + // h/t Alexey Knyazev (https://github.com/KhronosGroup/MoltenVK/issues/2068#issuecomment-1817799067) for the code. + case SPVFuncImplGradientCube: + statement("static inline gradientcube spvGradientCube(float3 P, float3 dPdx, float3 dPdy)"); + begin_scope(); + statement("// Major axis selection"); + statement("float3 absP = abs(P);"); + statement("bool xMajor = absP.x >= max(absP.y, absP.z);"); + statement("bool yMajor = absP.y >= absP.z;"); + statement("float3 Q = xMajor ? P.yzx : (yMajor ? P.xzy : P);"); + statement("float3 dQdx = xMajor ? dPdx.yzx : (yMajor ? dPdx.xzy : dPdx);"); + statement("float3 dQdy = xMajor ? dPdy.yzx : (yMajor ? dPdy.xzy : dPdy);"); + statement_no_indent(""); + statement("// Skip a couple of operations compared to usual projection"); + statement("float4 d = float4(dQdx.xy, dQdy.xy) - (Q.xy / Q.z).xyxy * float4(dQdx.zz, dQdy.zz);"); + statement_no_indent(""); + statement("// Final swizzle to put the intermediate values into non-ignored components"); + statement("// X major: X and Z"); + statement("// Y major: X and Y"); + statement("// Z major: Y and Z"); + statement("return gradientcube(xMajor ? d.xxy : d.xyx, xMajor ? d.zzw : d.zwz);"); + end_scope(); + statement(""); + break; + + // "fadd" intrinsic support + case SPVFuncImplFAdd: + statement("template"); + statement("[[clang::optnone]] T spvFAdd(T l, T r)"); + begin_scope(); + statement("return fma(T(1), l, r);"); + end_scope(); + statement(""); + break; + + // "fsub" intrinsic support + case SPVFuncImplFSub: + statement("template"); + statement("[[clang::optnone]] T spvFSub(T l, T r)"); + begin_scope(); + statement("return fma(T(-1), r, l);"); + end_scope(); + statement(""); + break; + + // "fmul' intrinsic support + case SPVFuncImplFMul: + statement("template"); + statement("[[clang::optnone]] T spvFMul(T l, T r)"); + begin_scope(); + statement("return fma(l, r, T(0));"); + end_scope(); + statement(""); + + statement("template"); + statement("[[clang::optnone]] vec spvFMulVectorMatrix(vec v, matrix m)"); + begin_scope(); + statement("vec res = vec(0);"); + statement("for (uint i = Rows; i > 0; --i)"); + begin_scope(); + statement("vec tmp(0);"); + statement("for (uint j = 0; j < Cols; ++j)"); + begin_scope(); + statement("tmp[j] = m[j][i - 1];"); + end_scope(); + statement("res = fma(tmp, vec(v[i - 1]), res);"); + end_scope(); + statement("return res;"); + end_scope(); + statement(""); + + statement("template"); + statement("[[clang::optnone]] vec spvFMulMatrixVector(matrix m, vec v)"); + begin_scope(); + statement("vec res = vec(0);"); + statement("for (uint i = Cols; i > 0; --i)"); + begin_scope(); + statement("res = fma(m[i - 1], vec(v[i - 1]), res);"); + end_scope(); + statement("return res;"); + end_scope(); + statement(""); + + statement("template"); + statement("[[clang::optnone]] matrix spvFMulMatrixMatrix(matrix l, matrix r)"); + begin_scope(); + statement("matrix res;"); + statement("for (uint i = 0; i < RCols; i++)"); + begin_scope(); + statement("vec tmp(0);"); + statement("for (uint j = 0; j < LCols; j++)"); + begin_scope(); + statement("tmp = fma(vec(r[i][j]), l[j], tmp);"); + end_scope(); + statement("res[i] = tmp;"); + end_scope(); + statement("return res;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplQuantizeToF16: + // Ensure fast-math is disabled to match Vulkan results. + // SpvHalfTypeSelector is used to match the half* template type to the float* template type. + // Depending on GPU, MSL does not always flush converted subnormal halfs to zero, + // as required by OpQuantizeToF16, so check for subnormals and flush them to zero. + statement("template struct SpvHalfTypeSelector;"); + statement("template <> struct SpvHalfTypeSelector { public: using H = half; };"); + statement("template struct SpvHalfTypeSelector> { using H = vec; };"); + statement("template::H>"); + statement("[[clang::optnone]] F spvQuantizeToF16(F fval)"); + begin_scope(); + statement("H hval = H(fval);"); + statement("hval = select(copysign(H(0), hval), hval, isnormal(hval) || isinf(hval) || isnan(hval));"); + statement("return F(hval);"); + end_scope(); + statement(""); + break; + + // Emulate texturecube_array with texture2d_array for iOS where this type is not available + case SPVFuncImplCubemapTo2DArrayFace: + statement(force_inline); + statement("float3 spvCubemapTo2DArrayFace(float3 P)"); + begin_scope(); + statement("float3 Coords = abs(P.xyz);"); + statement("float CubeFace = 0;"); + statement("float ProjectionAxis = 0;"); + statement("float u = 0;"); + statement("float v = 0;"); + statement("if (Coords.x >= Coords.y && Coords.x >= Coords.z)"); + begin_scope(); + statement("CubeFace = P.x >= 0 ? 0 : 1;"); + statement("ProjectionAxis = Coords.x;"); + statement("u = P.x >= 0 ? -P.z : P.z;"); + statement("v = -P.y;"); + end_scope(); + statement("else if (Coords.y >= Coords.x && Coords.y >= Coords.z)"); + begin_scope(); + statement("CubeFace = P.y >= 0 ? 2 : 3;"); + statement("ProjectionAxis = Coords.y;"); + statement("u = P.x;"); + statement("v = P.y >= 0 ? P.z : -P.z;"); + end_scope(); + statement("else"); + begin_scope(); + statement("CubeFace = P.z >= 0 ? 4 : 5;"); + statement("ProjectionAxis = Coords.z;"); + statement("u = P.z >= 0 ? P.x : -P.x;"); + statement("v = -P.y;"); + end_scope(); + statement("u = 0.5 * (u/ProjectionAxis + 1);"); + statement("v = 0.5 * (v/ProjectionAxis + 1);"); + statement("return float3(u, v, CubeFace);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplInverse4x4: + statement("// Returns the determinant of a 2x2 matrix."); + statement(force_inline); + statement("float spvDet2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement(""); + + statement("// Returns the determinant of a 3x3 matrix."); + statement(force_inline); + statement("float spvDet3x3(float a1, float a2, float a3, float b1, float b2, float b3, float c1, " + "float c2, float c3)"); + begin_scope(); + statement("return a1 * spvDet2x2(b2, b3, c2, c3) - b1 * spvDet2x2(a2, a3, c2, c3) + c1 * spvDet2x2(a2, a3, " + "b2, b3);"); + end_scope(); + statement(""); + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement(force_inline); + statement("float4x4 spvInverse4x4(float4x4 m)"); + begin_scope(); + statement("float4x4 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = spvDet3x3(m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement("adj[0][1] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[2][1], m[2][2], m[2][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement("adj[0][2] = spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[3][1], m[3][2], " + "m[3][3]);"); + statement("adj[0][3] = -spvDet3x3(m[0][1], m[0][2], m[0][3], m[1][1], m[1][2], m[1][3], m[2][1], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement("adj[1][0] = -spvDet3x3(m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement("adj[1][1] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[2][0], m[2][2], m[2][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement("adj[1][2] = -spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[3][0], m[3][2], " + "m[3][3]);"); + statement("adj[1][3] = spvDet3x3(m[0][0], m[0][2], m[0][3], m[1][0], m[1][2], m[1][3], m[2][0], m[2][2], " + "m[2][3]);"); + statement_no_indent(""); + statement("adj[2][0] = spvDet3x3(m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement("adj[2][1] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[2][0], m[2][1], m[2][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement("adj[2][2] = spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[3][0], m[3][1], " + "m[3][3]);"); + statement("adj[2][3] = -spvDet3x3(m[0][0], m[0][1], m[0][3], m[1][0], m[1][1], m[1][3], m[2][0], m[2][1], " + "m[2][3]);"); + statement_no_indent(""); + statement("adj[3][0] = -spvDet3x3(m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement("adj[3][1] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[2][0], m[2][1], m[2][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement("adj[3][2] = -spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[3][0], m[3][1], " + "m[3][2]);"); + statement("adj[3][3] = spvDet3x3(m[0][0], m[0][1], m[0][2], m[1][0], m[1][1], m[1][2], m[2][0], m[2][1], " + "m[2][2]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]) + (adj[0][3] " + "* m[3][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplInverse3x3: + if (spv_function_implementations.count(SPVFuncImplInverse4x4) == 0) + { + statement("// Returns the determinant of a 2x2 matrix."); + statement(force_inline); + statement("float spvDet2x2(float a1, float a2, float b1, float b2)"); + begin_scope(); + statement("return a1 * b2 - b1 * a2;"); + end_scope(); + statement(""); + } + + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement(force_inline); + statement("float3x3 spvInverse3x3(float3x3 m)"); + begin_scope(); + statement("float3x3 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = spvDet2x2(m[1][1], m[1][2], m[2][1], m[2][2]);"); + statement("adj[0][1] = -spvDet2x2(m[0][1], m[0][2], m[2][1], m[2][2]);"); + statement("adj[0][2] = spvDet2x2(m[0][1], m[0][2], m[1][1], m[1][2]);"); + statement_no_indent(""); + statement("adj[1][0] = -spvDet2x2(m[1][0], m[1][2], m[2][0], m[2][2]);"); + statement("adj[1][1] = spvDet2x2(m[0][0], m[0][2], m[2][0], m[2][2]);"); + statement("adj[1][2] = -spvDet2x2(m[0][0], m[0][2], m[1][0], m[1][2]);"); + statement_no_indent(""); + statement("adj[2][0] = spvDet2x2(m[1][0], m[1][1], m[2][0], m[2][1]);"); + statement("adj[2][1] = -spvDet2x2(m[0][0], m[0][1], m[2][0], m[2][1]);"); + statement("adj[2][2] = spvDet2x2(m[0][0], m[0][1], m[1][0], m[1][1]);"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]) + (adj[0][2] * m[2][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplInverse2x2: + statement("// Returns the inverse of a matrix, by using the algorithm of calculating the classical"); + statement("// adjoint and dividing by the determinant. The contents of the matrix are changed."); + statement(force_inline); + statement("float2x2 spvInverse2x2(float2x2 m)"); + begin_scope(); + statement("float2x2 adj; // The adjoint matrix (inverse after dividing by determinant)"); + statement_no_indent(""); + statement("// Create the transpose of the cofactors, as the classical adjoint of the matrix."); + statement("adj[0][0] = m[1][1];"); + statement("adj[0][1] = -m[0][1];"); + statement_no_indent(""); + statement("adj[1][0] = -m[1][0];"); + statement("adj[1][1] = m[0][0];"); + statement_no_indent(""); + statement("// Calculate the determinant as a combination of the cofactors of the first row."); + statement("float det = (adj[0][0] * m[0][0]) + (adj[0][1] * m[1][0]);"); + statement_no_indent(""); + statement("// Divide the classical adjoint matrix by the determinant."); + statement("// If determinant is zero, matrix is not invertable, so leave it unchanged."); + statement("return (det != 0.0f) ? (adj * (1.0f / det)) : m;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplGetSwizzle: + statement("enum class spvSwizzle : uint"); + begin_scope(); + statement("none = 0,"); + statement("zero,"); + statement("one,"); + statement("red,"); + statement("green,"); + statement("blue,"); + statement("alpha"); + end_scope_decl(); + statement(""); + statement("template"); + statement("inline T spvGetSwizzle(vec x, T c, spvSwizzle s)"); + begin_scope(); + statement("switch (s)"); + begin_scope(); + statement("case spvSwizzle::none:"); + statement(" return c;"); + statement("case spvSwizzle::zero:"); + statement(" return 0;"); + statement("case spvSwizzle::one:"); + statement(" return 1;"); + statement("case spvSwizzle::red:"); + statement(" return x.r;"); + statement("case spvSwizzle::green:"); + statement(" return x.g;"); + statement("case spvSwizzle::blue:"); + statement(" return x.b;"); + statement("case spvSwizzle::alpha:"); + statement(" return x.a;"); + end_scope(); + end_scope(); + statement(""); + break; + + case SPVFuncImplTextureSwizzle: + statement("// Wrapper function that swizzles texture samples and fetches."); + statement("template"); + statement("inline vec spvTextureSwizzle(vec x, uint s)"); + begin_scope(); + statement("if (!s)"); + statement(" return x;"); + statement("return vec(spvGetSwizzle(x, x.r, spvSwizzle((s >> 0) & 0xFF)), " + "spvGetSwizzle(x, x.g, spvSwizzle((s >> 8) & 0xFF)), spvGetSwizzle(x, x.b, spvSwizzle((s >> 16) " + "& 0xFF)), " + "spvGetSwizzle(x, x.a, spvSwizzle((s >> 24) & 0xFF)));"); + end_scope(); + statement(""); + statement("template"); + statement("inline T spvTextureSwizzle(T x, uint s)"); + begin_scope(); + statement("return spvTextureSwizzle(vec(x, 0, 0, 1), s).x;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplGatherReturn: + statement("template"); + statement("using spvGatherReturn = decltype(declval().gather(declval(), declval()...));"); + statement(""); + break; + + case SPVFuncImplGatherCompareReturn: + statement("template"); + statement("using spvGatherCompareReturn = decltype(declval().gather_compare(declval(), declval()...));"); + statement(""); + break; + + case SPVFuncImplGatherSwizzle: + statement("// Wrapper function that swizzles texture gathers."); + statement("template"); + statement("inline spvGatherReturn spvGatherSwizzle(const thread Tex& t, sampler s, " + "uint sw, component c, Ts... params) METAL_CONST_ARG(c)"); + begin_scope(); + statement("if (sw)"); + begin_scope(); + statement("switch (spvSwizzle((sw >> (uint(c) * 8)) & 0xFF))"); + begin_scope(); + statement("case spvSwizzle::none:"); + statement(" break;"); + statement("case spvSwizzle::zero:"); + statement(" return spvGatherReturn(0, 0, 0, 0);"); + statement("case spvSwizzle::one:"); + statement(" return spvGatherReturn(1, 1, 1, 1);"); + statement("case spvSwizzle::red:"); + statement(" return t.gather(s, params..., component::x);"); + statement("case spvSwizzle::green:"); + statement(" return t.gather(s, params..., component::y);"); + statement("case spvSwizzle::blue:"); + statement(" return t.gather(s, params..., component::z);"); + statement("case spvSwizzle::alpha:"); + statement(" return t.gather(s, params..., component::w);"); + end_scope(); + end_scope(); + // texture::gather insists on its component parameter being a constant + // expression, so we need this silly workaround just to compile the shader. + statement("switch (c)"); + begin_scope(); + statement("case component::x:"); + statement(" return t.gather(s, params..., component::x);"); + statement("case component::y:"); + statement(" return t.gather(s, params..., component::y);"); + statement("case component::z:"); + statement(" return t.gather(s, params..., component::z);"); + statement("case component::w:"); + statement(" return t.gather(s, params..., component::w);"); + end_scope(); + end_scope(); + statement(""); + break; + + case SPVFuncImplGatherCompareSwizzle: + statement("// Wrapper function that swizzles depth texture gathers."); + statement("template"); + statement("inline spvGatherCompareReturn spvGatherCompareSwizzle(const thread Tex& t, sampler s, uint sw, Ts... params)"); + begin_scope(); + statement("if (sw)"); + begin_scope(); + statement("switch (spvSwizzle(sw & 0xFF))"); + begin_scope(); + statement("case spvSwizzle::none:"); + statement("case spvSwizzle::red:"); + statement(" break;"); + statement("case spvSwizzle::zero:"); + statement("case spvSwizzle::green:"); + statement("case spvSwizzle::blue:"); + statement("case spvSwizzle::alpha:"); + statement(" return spvGatherCompareReturn(0, 0, 0, 0);"); + statement("case spvSwizzle::one:"); + statement(" return spvGatherCompareReturn(1, 1, 1, 1);"); + end_scope(); + end_scope(); + statement("return t.gather_compare(s, params...);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplGatherConstOffsets: + // Because we are passing a texture reference, we have to output an overloaded version of this function for each address space. + for (uint32_t i = 0; i < texture_addr_space_count; i++) + { + statement("// Wrapper function that processes a ", texture_addr_spaces[i], " texture gather with a constant offset array."); + statement("template"); + statement("inline spvGatherReturn spvGatherConstOffsets(const ", texture_addr_spaces[i], " Tex& t, sampler s, " + "Toff coffsets, component c, Tp... params) METAL_CONST_ARG(c)"); + begin_scope(); + statement("spvGatherReturn rslts[4];"); + statement("for (uint i = 0; i < 4; i++)"); + begin_scope(); + statement("switch (c)"); + begin_scope(); + // Work around texture::gather() requiring its component parameter to be a constant expression + statement("case component::x:"); + statement(" rslts[i] = t.gather(s, params..., coffsets[i], component::x);"); + statement(" break;"); + statement("case component::y:"); + statement(" rslts[i] = t.gather(s, params..., coffsets[i], component::y);"); + statement(" break;"); + statement("case component::z:"); + statement(" rslts[i] = t.gather(s, params..., coffsets[i], component::z);"); + statement(" break;"); + statement("case component::w:"); + statement(" rslts[i] = t.gather(s, params..., coffsets[i], component::w);"); + statement(" break;"); + end_scope(); + end_scope(); + // Pull all values from the i0j0 component of each gather footprint + statement("return spvGatherReturn(rslts[0].w, rslts[1].w, rslts[2].w, rslts[3].w);"); + end_scope(); + statement(""); + } + break; + + case SPVFuncImplGatherCompareConstOffsets: + // Because we are passing a texture reference, we have to output an overloaded version of this function for each address space. + for (uint32_t i = 0; i < texture_addr_space_count; i++) + { + statement("// Wrapper function that processes a ", texture_addr_spaces[i], " texture gather with a constant offset array."); + statement("template"); + statement("inline spvGatherCompareReturn spvGatherCompareConstOffsets(const ", texture_addr_spaces[i], " Tex& t, sampler s, " + "Toff coffsets, Tp... params)"); + begin_scope(); + statement("spvGatherCompareReturn rslts[4];"); + statement("for (uint i = 0; i < 4; i++)"); + begin_scope(); + statement(" rslts[i] = t.gather_compare(s, params..., coffsets[i]);"); + end_scope(); + // Pull all values from the i0j0 component of each gather footprint + statement("return spvGatherCompareReturn(rslts[0].w, rslts[1].w, rslts[2].w, rslts[3].w);"); + end_scope(); + statement(""); + } + break; + + case SPVFuncImplSubgroupBroadcast: + // Metal doesn't allow broadcasting boolean values directly, but we can work around that by broadcasting + // them as integers. + statement("template"); + statement("inline T spvSubgroupBroadcast(T value, ushort lane)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_broadcast(value, lane);"); + else + statement("return simd_broadcast(value, lane);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupBroadcast(bool value, ushort lane)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return !!quad_broadcast((ushort)value, lane);"); + else + statement("return !!simd_broadcast((ushort)value, lane);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupBroadcast(vec value, ushort lane)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return (vec)quad_broadcast((vec)value, lane);"); + else + statement("return (vec)simd_broadcast((vec)value, lane);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBroadcastFirst: + statement("template"); + statement("inline T spvSubgroupBroadcastFirst(T value)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_broadcast_first(value);"); + else + statement("return simd_broadcast_first(value);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupBroadcastFirst(bool value)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return !!quad_broadcast_first((ushort)value);"); + else + statement("return !!simd_broadcast_first((ushort)value);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupBroadcastFirst(vec value)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return (vec)quad_broadcast_first((vec)value);"); + else + statement("return (vec)simd_broadcast_first((vec)value);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallot: + statement("inline uint4 spvSubgroupBallot(bool value)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + { + statement("return uint4((quad_vote::vote_t)quad_ballot(value), 0, 0, 0);"); + } + else if (msl_options.is_ios()) + { + // The current simd_vote on iOS uses a 32-bit integer-like object. + statement("return uint4((simd_vote::vote_t)simd_ballot(value), 0, 0, 0);"); + } + else + { + statement("simd_vote vote = simd_ballot(value);"); + statement("// simd_ballot() returns a 64-bit integer-like object, but"); + statement("// SPIR-V callers expect a uint4. We must convert."); + statement("// FIXME: This won't include higher bits if Apple ever supports"); + statement("// 128 lanes in an SIMD-group."); + statement("return uint4(as_type((simd_vote::vote_t)vote), 0, 0);"); + } + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotBitExtract: + statement("inline bool spvSubgroupBallotBitExtract(uint4 ballot, uint bit)"); + begin_scope(); + statement("return !!extract_bits(ballot[bit / 32], bit % 32, 1);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotFindLSB: + statement("inline uint spvSubgroupBallotFindLSB(uint4 ballot, uint gl_SubgroupSize)"); + begin_scope(); + if (msl_options.is_ios()) + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, gl_SubgroupSize), uint3(0));"); + } + else + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0));"); + } + statement("ballot &= mask;"); + statement("return select(ctz(ballot.x), select(32 + ctz(ballot.y), select(64 + ctz(ballot.z), select(96 + " + "ctz(ballot.w), uint(-1), ballot.w == 0), ballot.z == 0), ballot.y == 0), ballot.x == 0);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotFindMSB: + statement("inline uint spvSubgroupBallotFindMSB(uint4 ballot, uint gl_SubgroupSize)"); + begin_scope(); + if (msl_options.is_ios()) + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, gl_SubgroupSize), uint3(0));"); + } + else + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0));"); + } + statement("ballot &= mask;"); + statement("return select(128 - (clz(ballot.w) + 1), select(96 - (clz(ballot.z) + 1), select(64 - " + "(clz(ballot.y) + 1), select(32 - (clz(ballot.x) + 1), uint(-1), ballot.x == 0), ballot.y == 0), " + "ballot.z == 0), ballot.w == 0);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupBallotBitCount: + statement("inline uint spvPopCount4(uint4 ballot)"); + begin_scope(); + statement("return popcount(ballot.x) + popcount(ballot.y) + popcount(ballot.z) + popcount(ballot.w);"); + end_scope(); + statement(""); + statement("inline uint spvSubgroupBallotBitCount(uint4 ballot, uint gl_SubgroupSize)"); + begin_scope(); + if (msl_options.is_ios()) + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, gl_SubgroupSize), uint3(0));"); + } + else + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupSize, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupSize - 32, 0)), uint2(0));"); + } + statement("return spvPopCount4(ballot & mask);"); + end_scope(); + statement(""); + statement("inline uint spvSubgroupBallotInclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID)"); + begin_scope(); + if (msl_options.is_ios()) + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, gl_SubgroupInvocationID + 1), uint3(0));"); + } + else + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID + 1, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID + 1 - 32, 0)), " + "uint2(0));"); + } + statement("return spvPopCount4(ballot & mask);"); + end_scope(); + statement(""); + statement("inline uint spvSubgroupBallotExclusiveBitCount(uint4 ballot, uint gl_SubgroupInvocationID)"); + begin_scope(); + if (msl_options.is_ios()) + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, gl_SubgroupInvocationID), uint2(0));"); + } + else + { + statement("uint4 mask = uint4(extract_bits(0xFFFFFFFF, 0, min(gl_SubgroupInvocationID, 32u)), " + "extract_bits(0xFFFFFFFF, 0, (uint)max((int)gl_SubgroupInvocationID - 32, 0)), uint2(0));"); + } + statement("return spvPopCount4(ballot & mask);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupAllEqual: + // Metal doesn't provide a function to evaluate this directly. But, we can + // implement this by comparing every thread's value to one thread's value + // (in this case, the value of the first active thread). Then, by the transitive + // property of equality, if all comparisons return true, then they are all equal. + statement("template"); + statement("inline bool spvSubgroupAllEqual(T value)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_all(all(value == quad_broadcast_first(value)));"); + else + statement("return simd_all(all(value == simd_broadcast_first(value)));"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupAllEqual(bool value)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_all(value) || !quad_any(value);"); + else + statement("return simd_all(value) || !simd_any(value);"); + end_scope(); + statement(""); + statement("template"); + statement("inline bool spvSubgroupAllEqual(vec value)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_all(all(value == (vec)quad_broadcast_first((vec)value)));"); + else + statement("return simd_all(all(value == (vec)simd_broadcast_first((vec)value)));"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupShuffle: + statement("template"); + statement("inline T spvSubgroupShuffle(T value, ushort lane)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_shuffle(value, lane);"); + else + statement("return simd_shuffle(value, lane);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffle(bool value, ushort lane)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return !!quad_shuffle((ushort)value, lane);"); + else + statement("return !!simd_shuffle((ushort)value, lane);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffle(vec value, ushort lane)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return (vec)quad_shuffle((vec)value, lane);"); + else + statement("return (vec)simd_shuffle((vec)value, lane);"); + end_scope(); + statement(""); + + if (msl_options.supports_msl_version(2, 2)) + { + // Despite being a template in MSL, it does not support 64-bit shuffles. + // Unsure if there's a cleaner way to statically unroll based on vec<> template, but this will do. + statement("template<>"); + statement("inline ulong spvSubgroupShuffle(ulong value, ushort lane)"); + begin_scope(); + statement("return as_type(spvSubgroupShuffle(as_type(value), lane));"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline ulong2 spvSubgroupShuffle(ulong2 value, ushort lane)"); + begin_scope(); + statement("return ulong2(spvSubgroupShuffle(value.x, lane), spvSubgroupShuffle(value.y, lane));"); + end_scope(); + statement(""); + statement("inline ulong3 spvSubgroupShuffle(ulong3 value, ushort lane)"); + begin_scope(); + statement("return ulong3(spvSubgroupShuffle(value.xy, lane), spvSubgroupShuffle(value.z, lane));"); + end_scope(); + statement(""); + statement("inline ulong4 spvSubgroupShuffle(ulong4 value, ushort lane)"); + begin_scope(); + statement("return ulong4(spvSubgroupShuffle(value.xy, lane), spvSubgroupShuffle(value.zw, lane));"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffle(vec value, ushort lane)"); + begin_scope(); + statement("return vec(spvSubgroupShuffle(vec(value), lane));"); + end_scope(); + statement(""); + } + break; + + case SPVFuncImplSubgroupShuffleXor: + statement("template"); + statement("inline T spvSubgroupShuffleXor(T value, ushort mask)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_shuffle_xor(value, mask);"); + else + statement("return simd_shuffle_xor(value, mask);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffleXor(bool value, ushort mask)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return !!quad_shuffle_xor((ushort)value, mask);"); + else + statement("return !!simd_shuffle_xor((ushort)value, mask);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffleXor(vec value, ushort mask)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return (vec)quad_shuffle_xor((vec)value, mask);"); + else + statement("return (vec)simd_shuffle_xor((vec)value, mask);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupShuffleUp: + statement("template"); + statement("inline T spvSubgroupShuffleUp(T value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_shuffle_up(value, delta);"); + else + statement("return simd_shuffle_up(value, delta);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffleUp(bool value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return !!quad_shuffle_up((ushort)value, delta);"); + else + statement("return !!simd_shuffle_up((ushort)value, delta);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffleUp(vec value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return (vec)quad_shuffle_up((vec)value, delta);"); + else + statement("return (vec)simd_shuffle_up((vec)value, delta);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupShuffleDown: + statement("template"); + statement("inline T spvSubgroupShuffleDown(T value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_shuffle_down(value, delta);"); + else + statement("return simd_shuffle_down(value, delta);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupShuffleDown(bool value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return !!quad_shuffle_down((ushort)value, delta);"); + else + statement("return !!simd_shuffle_down((ushort)value, delta);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupShuffleDown(vec value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return (vec)quad_shuffle_down((vec)value, delta);"); + else + statement("return (vec)simd_shuffle_down((vec)value, delta);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSubgroupRotate: + statement("template"); + statement("inline T spvSubgroupRotate(T value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return quad_shuffle_rotate_down(value, delta);"); + else + statement("return simd_shuffle_rotate_down(value, delta);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvSubgroupRotate(bool value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return !!quad_shuffle_rotate_down((ushort)value, delta);"); + else + statement("return !!simd_shuffle_rotate_down((ushort)value, delta);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvSubgroupRotate(vec value, ushort delta)"); + begin_scope(); + if (msl_options.use_quadgroup_operation()) + statement("return (vec)quad_shuffle_rotate_down((vec)value, delta);"); + else + statement("return (vec)simd_shuffle_rotate_down((vec)value, delta);"); + end_scope(); + statement(""); + break; + + // C++ disallows partial specializations of function templates, + // hence the use of a struct. + // clang-format off +#define FUNC_SUBGROUP_CLUSTERED(spv, msl, combine, op, ident) \ + case SPVFuncImplSubgroupClustered##spv: \ + statement("template"); \ + statement("struct spvClustered" #spv "Detail;"); \ + statement(""); \ + statement("// Base cases"); \ + statement("template<>"); \ + statement("struct spvClustered" #spv "Detail<1, 0>"); \ + begin_scope(); \ + statement("template"); \ + statement("static T op(T value, uint)"); \ + begin_scope(); \ + statement("return value;"); \ + end_scope(); \ + end_scope_decl(); \ + statement(""); \ + statement("template"); \ + statement("struct spvClustered" #spv "Detail<1, offset>"); \ + begin_scope(); \ + statement("template"); \ + statement("static T op(T value, uint lid)"); \ + begin_scope(); \ + statement("// If the target lane is inactive, then return identity."); \ + if (msl_options.use_quadgroup_operation()) \ + statement("if (!extract_bits((quad_vote::vote_t)quad_active_threads_mask(), (lid ^ offset), 1))"); \ + else \ + statement("if (!extract_bits(as_type((simd_vote::vote_t)simd_active_threads_mask())[(lid ^ offset) / 32], (lid ^ offset) % 32, 1))"); \ + statement(" return " #ident ";"); \ + if (msl_options.use_quadgroup_operation()) \ + statement("return quad_shuffle_xor(value, offset);"); \ + else \ + statement("return simd_shuffle_xor(value, offset);"); \ + end_scope(); \ + end_scope_decl(); \ + statement(""); \ + statement("template<>"); \ + statement("struct spvClustered" #spv "Detail<4, 0>"); \ + begin_scope(); \ + statement("template"); \ + statement("static T op(T value, uint)"); \ + begin_scope(); \ + statement("return quad_" #msl "(value);"); \ + end_scope(); \ + end_scope_decl(); \ + statement(""); \ + statement("template"); \ + statement("struct spvClustered" #spv "Detail<4, offset>"); \ + begin_scope(); \ + statement("template"); \ + statement("static T op(T value, uint lid)"); \ + begin_scope(); \ + statement("// Here, we care if any of the lanes in the quad are active."); \ + statement("uint quad_mask = extract_bits(as_type((simd_vote::vote_t)simd_active_threads_mask())[(lid ^ offset) / 32], ((lid ^ offset) % 32) & ~3, 4);"); \ + statement("if (!quad_mask)"); \ + statement(" return " #ident ";"); \ + statement("// But we need to make sure we shuffle from an active lane."); \ + if (msl_options.use_quadgroup_operation()) \ + SPIRV_CROSS_THROW("Subgroup size with quadgroup operation cannot exceed 4."); \ + else \ + statement("return simd_shuffle(quad_" #msl "(value), ((lid ^ offset) & ~3) | ctz(quad_mask));"); \ + end_scope(); \ + end_scope_decl(); \ + statement(""); \ + statement("// General case"); \ + statement("template"); \ + statement("struct spvClustered" #spv "Detail"); \ + begin_scope(); \ + statement("template"); \ + statement("static T op(T value, uint lid)"); \ + begin_scope(); \ + statement("return " combine(msl, op, "spvClustered" #spv "Detail::op(value, lid)", "spvClustered" #spv "Detail::op(value, lid)") ";"); \ + end_scope(); \ + end_scope_decl(); \ + statement(""); \ + statement("template"); \ + statement("T spvClustered_" #msl "(T value, uint lid)"); \ + begin_scope(); \ + statement("return spvClustered" #spv "Detail::op(value, lid);"); \ + end_scope(); \ + statement(""); \ + break +#define BINOP(msl, op, l, r) l " " #op " " r +#define BINFUNC(msl, op, l, r) #msl "(" l ", " r ")" + + FUNC_SUBGROUP_CLUSTERED(Add, sum, BINOP, +, 0); + FUNC_SUBGROUP_CLUSTERED(Mul, product, BINOP, *, 1); + FUNC_SUBGROUP_CLUSTERED(Min, min, BINFUNC, , numeric_limits::max()); + FUNC_SUBGROUP_CLUSTERED(Max, max, BINFUNC, , numeric_limits::min()); + FUNC_SUBGROUP_CLUSTERED(And, and, BINOP, &, ~T(0)); + FUNC_SUBGROUP_CLUSTERED(Or, or, BINOP, |, 0); + FUNC_SUBGROUP_CLUSTERED(Xor, xor, BINOP, ^, 0); + // clang-format on +#undef FUNC_SUBGROUP_CLUSTERED +#undef BINOP +#undef BINFUNC + + case SPVFuncImplQuadBroadcast: + statement("template"); + statement("inline T spvQuadBroadcast(T value, uint lane)"); + begin_scope(); + statement("return quad_broadcast(value, lane);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvQuadBroadcast(bool value, uint lane)"); + begin_scope(); + statement("return !!quad_broadcast((ushort)value, lane);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvQuadBroadcast(vec value, uint lane)"); + begin_scope(); + statement("return (vec)quad_broadcast((vec)value, lane);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplQuadSwap: + // We can implement this easily based on the following table giving + // the target lane ID from the direction and current lane ID: + // Direction + // | 0 | 1 | 2 | + // ---+---+---+---+ + // L 0 | 1 2 3 + // a 1 | 0 3 2 + // n 2 | 3 0 1 + // e 3 | 2 1 0 + // Notice that target = source ^ (direction + 1). + statement("template"); + statement("inline T spvQuadSwap(T value, uint dir)"); + begin_scope(); + statement("return quad_shuffle_xor(value, dir + 1);"); + end_scope(); + statement(""); + statement("template<>"); + statement("inline bool spvQuadSwap(bool value, uint dir)"); + begin_scope(); + statement("return !!quad_shuffle_xor((ushort)value, dir + 1);"); + end_scope(); + statement(""); + statement("template"); + statement("inline vec spvQuadSwap(vec value, uint dir)"); + begin_scope(); + statement("return (vec)quad_shuffle_xor((vec)value, dir + 1);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplReflectScalar: + // Metal does not support scalar versions of these functions. + // Ensure fast-math is disabled to match Vulkan results. + statement("template"); + statement("[[clang::optnone]] T spvReflect(T i, T n)"); + begin_scope(); + statement("return i - T(2) * i * n * n;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplRefractScalar: + // Metal does not support scalar versions of these functions. + statement("template"); + statement("inline T spvRefract(T i, T n, T eta)"); + begin_scope(); + statement("T NoI = n * i;"); + statement("T NoI2 = NoI * NoI;"); + statement("T k = T(1) - eta * eta * (T(1) - NoI2);"); + statement("if (k < T(0))"); + begin_scope(); + statement("return T(0);"); + end_scope(); + statement("else"); + begin_scope(); + statement("return eta * i - (eta * NoI + sqrt(k)) * n;"); + end_scope(); + end_scope(); + statement(""); + break; + + case SPVFuncImplFaceForwardScalar: + // Metal does not support scalar versions of these functions. + statement("template"); + statement("inline T spvFaceForward(T n, T i, T nref)"); + begin_scope(); + statement("return i * nref < T(0) ? n : -n;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructNearest2Plane: + statement("template"); + statement("inline vec spvChromaReconstructNearest(texture2d plane0, texture2d plane1, sampler " + "samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("ycbcr.br = plane1.sample(samp, coord, options...).rg;"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructNearest3Plane: + statement("template"); + statement("inline vec spvChromaReconstructNearest(texture2d plane0, texture2d plane1, " + "texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("ycbcr.b = plane1.sample(samp, coord, options...).r;"); + statement("ycbcr.r = plane2.sample(samp, coord, options...).r;"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422CositedEven2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422CositedEven(texture2d plane0, texture2d " + "plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("if (fract(coord.x * plane1.get_width()) != 0.0)"); + begin_scope(); + statement("ycbcr.br = vec(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), 0.5).rg);"); + end_scope(); + statement("else"); + begin_scope(); + statement("ycbcr.br = plane1.sample(samp, coord, options...).rg;"); + end_scope(); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422CositedEven3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422CositedEven(texture2d plane0, texture2d " + "plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("if (fract(coord.x * plane1.get_width()) != 0.0)"); + begin_scope(); + statement("ycbcr.b = T(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), 0.5).r);"); + statement("ycbcr.r = T(mix(plane2.sample(samp, coord, options...), " + "plane2.sample(samp, coord, options..., int2(1, 0)), 0.5).r);"); + end_scope(); + statement("else"); + begin_scope(); + statement("ycbcr.b = plane1.sample(samp, coord, options...).r;"); + statement("ycbcr.r = plane2.sample(samp, coord, options...).r;"); + end_scope(); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422Midpoint2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422Midpoint(texture2d plane0, texture2d " + "plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("int2 offs = int2(fract(coord.x * plane1.get_width()) != 0.0 ? 1 : -1, 0);"); + statement("ycbcr.br = vec(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., offs), 0.25).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear422Midpoint3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear422Midpoint(texture2d plane0, texture2d " + "plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("int2 offs = int2(fract(coord.x * plane1.get_width()) != 0.0 ? 1 : -1, 0);"); + statement("ycbcr.b = T(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., offs), 0.25).r);"); + statement("ycbcr.r = T(mix(plane2.sample(samp, coord, options...), " + "plane2.sample(samp, coord, options..., offs), 0.25).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYCositedEven(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract(round(coord * float2(plane0.get_width(), plane0.get_height())) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYCositedEven(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract(round(coord * float2(plane0.get_width(), plane0.get_height())) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, options...), " + "plane2.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, options..., int2(0, 1)), " + "plane2.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYCositedEven(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0)) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYCositedEven(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0)) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, options...), " + "plane2.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, options..., int2(0, 1)), " + "plane2.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYMidpoint(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0, " + "0.5)) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XCositedEvenYMidpoint(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0, " + "0.5)) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, options...), " + "plane2.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, options..., int2(0, 1)), " + "plane2.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYMidpoint(texture2d plane0, " + "texture2d plane1, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0.5)) * 0.5);"); + statement("ycbcr.br = vec(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).rg);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane: + statement("template"); + statement("inline vec spvChromaReconstructLinear420XMidpointYMidpoint(texture2d plane0, " + "texture2d plane1, texture2d plane2, sampler samp, float2 coord, LodOptions... options)"); + begin_scope(); + statement("vec ycbcr = vec(0, 0, 0, 1);"); + statement("ycbcr.g = plane0.sample(samp, coord, options...).r;"); + statement("float2 ab = fract((round(coord * float2(plane0.get_width(), plane0.get_height())) - float2(0.5, " + "0.5)) * 0.5);"); + statement("ycbcr.b = T(mix(mix(plane1.sample(samp, coord, options...), " + "plane1.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane1.sample(samp, coord, options..., int2(0, 1)), " + "plane1.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("ycbcr.r = T(mix(mix(plane2.sample(samp, coord, options...), " + "plane2.sample(samp, coord, options..., int2(1, 0)), ab.x), " + "mix(plane2.sample(samp, coord, options..., int2(0, 1)), " + "plane2.sample(samp, coord, options..., int2(1, 1)), ab.x), ab.y).r);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplExpandITUFullRange: + statement("template"); + statement("inline vec spvExpandITUFullRange(vec ycbcr, int n)"); + begin_scope(); + statement("ycbcr.br -= exp2(T(n-1))/(exp2(T(n))-1);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplExpandITUNarrowRange: + statement("template"); + statement("inline vec spvExpandITUNarrowRange(vec ycbcr, int n)"); + begin_scope(); + statement("ycbcr.g = (ycbcr.g * (exp2(T(n)) - 1) - ldexp(T(16), n - 8))/ldexp(T(219), n - 8);"); + statement("ycbcr.br = (ycbcr.br * (exp2(T(n)) - 1) - ldexp(T(128), n - 8))/ldexp(T(224), n - 8);"); + statement("return ycbcr;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplConvertYCbCrBT709: + statement("// cf. Khronos Data Format Specification, section 15.1.1"); + statement("constant float3x3 spvBT709Factors = {{1, 1, 1}, {0, -0.13397432/0.7152, 1.8556}, {1.5748, " + "-0.33480248/0.7152, 0}};"); + statement(""); + statement("template"); + statement("inline vec spvConvertYCbCrBT709(vec ycbcr)"); + begin_scope(); + statement("vec rgba;"); + statement("rgba.rgb = vec(spvBT709Factors * ycbcr.gbr);"); + statement("rgba.a = ycbcr.a;"); + statement("return rgba;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplConvertYCbCrBT601: + statement("// cf. Khronos Data Format Specification, section 15.1.2"); + statement("constant float3x3 spvBT601Factors = {{1, 1, 1}, {0, -0.202008/0.587, 1.772}, {1.402, " + "-0.419198/0.587, 0}};"); + statement(""); + statement("template"); + statement("inline vec spvConvertYCbCrBT601(vec ycbcr)"); + begin_scope(); + statement("vec rgba;"); + statement("rgba.rgb = vec(spvBT601Factors * ycbcr.gbr);"); + statement("rgba.a = ycbcr.a;"); + statement("return rgba;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplConvertYCbCrBT2020: + statement("// cf. Khronos Data Format Specification, section 15.1.3"); + statement("constant float3x3 spvBT2020Factors = {{1, 1, 1}, {0, -0.11156702/0.6780, 1.8814}, {1.4746, " + "-0.38737742/0.6780, 0}};"); + statement(""); + statement("template"); + statement("inline vec spvConvertYCbCrBT2020(vec ycbcr)"); + begin_scope(); + statement("vec rgba;"); + statement("rgba.rgb = vec(spvBT2020Factors * ycbcr.gbr);"); + statement("rgba.a = ycbcr.a;"); + statement("return rgba;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplDynamicImageSampler: + statement("enum class spvFormatResolution"); + begin_scope(); + statement("_444 = 0,"); + statement("_422,"); + statement("_420"); + end_scope_decl(); + statement(""); + statement("enum class spvChromaFilter"); + begin_scope(); + statement("nearest = 0,"); + statement("linear"); + end_scope_decl(); + statement(""); + statement("enum class spvXChromaLocation"); + begin_scope(); + statement("cosited_even = 0,"); + statement("midpoint"); + end_scope_decl(); + statement(""); + statement("enum class spvYChromaLocation"); + begin_scope(); + statement("cosited_even = 0,"); + statement("midpoint"); + end_scope_decl(); + statement(""); + statement("enum class spvYCbCrModelConversion"); + begin_scope(); + statement("rgb_identity = 0,"); + statement("ycbcr_identity,"); + statement("ycbcr_bt_709,"); + statement("ycbcr_bt_601,"); + statement("ycbcr_bt_2020"); + end_scope_decl(); + statement(""); + statement("enum class spvYCbCrRange"); + begin_scope(); + statement("itu_full = 0,"); + statement("itu_narrow"); + end_scope_decl(); + statement(""); + statement("struct spvComponentBits"); + begin_scope(); + statement("constexpr explicit spvComponentBits(int v) thread : value(v) {}"); + statement("uchar value : 6;"); + end_scope_decl(); + statement("// A class corresponding to metal::sampler which holds sampler"); + statement("// Y'CbCr conversion info."); + statement("struct spvYCbCrSampler"); + begin_scope(); + statement("constexpr spvYCbCrSampler() thread : val(build()) {}"); + statement("template"); + statement("constexpr spvYCbCrSampler(Ts... t) thread : val(build(t...)) {}"); + statement("constexpr spvYCbCrSampler(const thread spvYCbCrSampler& s) thread = default;"); + statement(""); + statement("spvFormatResolution get_resolution() const thread"); + begin_scope(); + statement("return spvFormatResolution((val & resolution_mask) >> resolution_base);"); + end_scope(); + statement("spvChromaFilter get_chroma_filter() const thread"); + begin_scope(); + statement("return spvChromaFilter((val & chroma_filter_mask) >> chroma_filter_base);"); + end_scope(); + statement("spvXChromaLocation get_x_chroma_offset() const thread"); + begin_scope(); + statement("return spvXChromaLocation((val & x_chroma_off_mask) >> x_chroma_off_base);"); + end_scope(); + statement("spvYChromaLocation get_y_chroma_offset() const thread"); + begin_scope(); + statement("return spvYChromaLocation((val & y_chroma_off_mask) >> y_chroma_off_base);"); + end_scope(); + statement("spvYCbCrModelConversion get_ycbcr_model() const thread"); + begin_scope(); + statement("return spvYCbCrModelConversion((val & ycbcr_model_mask) >> ycbcr_model_base);"); + end_scope(); + statement("spvYCbCrRange get_ycbcr_range() const thread"); + begin_scope(); + statement("return spvYCbCrRange((val & ycbcr_range_mask) >> ycbcr_range_base);"); + end_scope(); + statement("int get_bpc() const thread { return (val & bpc_mask) >> bpc_base; }"); + statement(""); + statement("private:"); + statement("ushort val;"); + statement(""); + statement("constexpr static constant ushort resolution_bits = 2;"); + statement("constexpr static constant ushort chroma_filter_bits = 2;"); + statement("constexpr static constant ushort x_chroma_off_bit = 1;"); + statement("constexpr static constant ushort y_chroma_off_bit = 1;"); + statement("constexpr static constant ushort ycbcr_model_bits = 3;"); + statement("constexpr static constant ushort ycbcr_range_bit = 1;"); + statement("constexpr static constant ushort bpc_bits = 6;"); + statement(""); + statement("constexpr static constant ushort resolution_base = 0;"); + statement("constexpr static constant ushort chroma_filter_base = 2;"); + statement("constexpr static constant ushort x_chroma_off_base = 4;"); + statement("constexpr static constant ushort y_chroma_off_base = 5;"); + statement("constexpr static constant ushort ycbcr_model_base = 6;"); + statement("constexpr static constant ushort ycbcr_range_base = 9;"); + statement("constexpr static constant ushort bpc_base = 10;"); + statement(""); + statement( + "constexpr static constant ushort resolution_mask = ((1 << resolution_bits) - 1) << resolution_base;"); + statement("constexpr static constant ushort chroma_filter_mask = ((1 << chroma_filter_bits) - 1) << " + "chroma_filter_base;"); + statement("constexpr static constant ushort x_chroma_off_mask = ((1 << x_chroma_off_bit) - 1) << " + "x_chroma_off_base;"); + statement("constexpr static constant ushort y_chroma_off_mask = ((1 << y_chroma_off_bit) - 1) << " + "y_chroma_off_base;"); + statement("constexpr static constant ushort ycbcr_model_mask = ((1 << ycbcr_model_bits) - 1) << " + "ycbcr_model_base;"); + statement("constexpr static constant ushort ycbcr_range_mask = ((1 << ycbcr_range_bit) - 1) << " + "ycbcr_range_base;"); + statement("constexpr static constant ushort bpc_mask = ((1 << bpc_bits) - 1) << bpc_base;"); + statement(""); + statement("static constexpr ushort build()"); + begin_scope(); + statement("return 0;"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvFormatResolution res, Ts... t)"); + begin_scope(); + statement("return (ushort(res) << resolution_base) | (build(t...) & ~resolution_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvChromaFilter filt, Ts... t)"); + begin_scope(); + statement("return (ushort(filt) << chroma_filter_base) | (build(t...) & ~chroma_filter_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvXChromaLocation loc, Ts... t)"); + begin_scope(); + statement("return (ushort(loc) << x_chroma_off_base) | (build(t...) & ~x_chroma_off_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvYChromaLocation loc, Ts... t)"); + begin_scope(); + statement("return (ushort(loc) << y_chroma_off_base) | (build(t...) & ~y_chroma_off_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvYCbCrModelConversion model, Ts... t)"); + begin_scope(); + statement("return (ushort(model) << ycbcr_model_base) | (build(t...) & ~ycbcr_model_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvYCbCrRange range, Ts... t)"); + begin_scope(); + statement("return (ushort(range) << ycbcr_range_base) | (build(t...) & ~ycbcr_range_mask);"); + end_scope(); + statement(""); + statement("template"); + statement("static constexpr ushort build(spvComponentBits bpc, Ts... t)"); + begin_scope(); + statement("return (ushort(bpc.value) << bpc_base) | (build(t...) & ~bpc_mask);"); + end_scope(); + end_scope_decl(); + statement(""); + statement("// A class which can hold up to three textures and a sampler, including"); + statement("// Y'CbCr conversion info, used to pass combined image-samplers"); + statement("// dynamically to functions."); + statement("template"); + statement("struct spvDynamicImageSampler"); + begin_scope(); + statement("texture2d plane0;"); + statement("texture2d plane1;"); + statement("texture2d plane2;"); + statement("sampler samp;"); + statement("spvYCbCrSampler ycbcr_samp;"); + statement("uint swizzle = 0;"); + statement(""); + if (msl_options.swizzle_texture_samples) + { + statement("constexpr spvDynamicImageSampler(texture2d tex, sampler samp, uint sw) thread :"); + statement(" plane0(tex), samp(samp), swizzle(sw) {}"); + } + else + { + statement("constexpr spvDynamicImageSampler(texture2d tex, sampler samp) thread :"); + statement(" plane0(tex), samp(samp) {}"); + } + statement("constexpr spvDynamicImageSampler(texture2d tex, sampler samp, spvYCbCrSampler ycbcr_samp, " + "uint sw) thread :"); + statement(" plane0(tex), samp(samp), ycbcr_samp(ycbcr_samp), swizzle(sw) {}"); + statement("constexpr spvDynamicImageSampler(texture2d plane0, texture2d plane1,"); + statement(" sampler samp, spvYCbCrSampler ycbcr_samp, uint sw) thread :"); + statement(" plane0(plane0), plane1(plane1), samp(samp), ycbcr_samp(ycbcr_samp), swizzle(sw) {}"); + statement( + "constexpr spvDynamicImageSampler(texture2d plane0, texture2d plane1, texture2d plane2,"); + statement(" sampler samp, spvYCbCrSampler ycbcr_samp, uint sw) thread :"); + statement(" plane0(plane0), plane1(plane1), plane2(plane2), samp(samp), ycbcr_samp(ycbcr_samp), " + "swizzle(sw) {}"); + statement(""); + // XXX This is really hard to follow... I've left comments to make it a bit easier. + statement("template"); + statement("vec do_sample(float2 coord, LodOptions... options) const thread"); + begin_scope(); + statement("if (!is_null_texture(plane1))"); + begin_scope(); + statement("if (ycbcr_samp.get_resolution() == spvFormatResolution::_444 ||"); + statement(" ycbcr_samp.get_chroma_filter() == spvChromaFilter::nearest)"); + begin_scope(); + statement("if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructNearest(plane0, plane1, plane2, samp, coord, options...);"); + statement("return spvChromaReconstructNearest(plane0, plane1, samp, coord, options...);"); + end_scope(); // if (resolution == 422 || chroma_filter == nearest) + statement("switch (ycbcr_samp.get_resolution())"); + begin_scope(); + statement("case spvFormatResolution::_444: break;"); + statement("case spvFormatResolution::_422:"); + begin_scope(); + statement("switch (ycbcr_samp.get_x_chroma_offset())"); + begin_scope(); + statement("case spvXChromaLocation::cosited_even:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear422CositedEven("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, options...);"); + statement(" return spvChromaReconstructLinear422CositedEven("); + statement(" plane0, plane1, samp, coord,"); + statement(" options...);"); + statement("case spvXChromaLocation::midpoint:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear422Midpoint("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, options...);"); + statement(" return spvChromaReconstructLinear422Midpoint("); + statement(" plane0, plane1, samp, coord,"); + statement(" options...);"); + end_scope(); // switch (x_chroma_offset) + end_scope(); // case 422: + statement("case spvFormatResolution::_420:"); + begin_scope(); + statement("switch (ycbcr_samp.get_x_chroma_offset())"); + begin_scope(); + statement("case spvXChromaLocation::cosited_even:"); + begin_scope(); + statement("switch (ycbcr_samp.get_y_chroma_offset())"); + begin_scope(); + statement("case spvYChromaLocation::cosited_even:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XCositedEvenYCositedEven("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, options...);"); + statement(" return spvChromaReconstructLinear420XCositedEvenYCositedEven("); + statement(" plane0, plane1, samp, coord,"); + statement(" options...);"); + statement("case spvYChromaLocation::midpoint:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XCositedEvenYMidpoint("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, options...);"); + statement(" return spvChromaReconstructLinear420XCositedEvenYMidpoint("); + statement(" plane0, plane1, samp, coord,"); + statement(" options...);"); + end_scope(); // switch (y_chroma_offset) + end_scope(); // case x::cosited_even: + statement("case spvXChromaLocation::midpoint:"); + begin_scope(); + statement("switch (ycbcr_samp.get_y_chroma_offset())"); + begin_scope(); + statement("case spvYChromaLocation::cosited_even:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XMidpointYCositedEven("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, options...);"); + statement(" return spvChromaReconstructLinear420XMidpointYCositedEven("); + statement(" plane0, plane1, samp, coord,"); + statement(" options...);"); + statement("case spvYChromaLocation::midpoint:"); + statement(" if (!is_null_texture(plane2))"); + statement(" return spvChromaReconstructLinear420XMidpointYMidpoint("); + statement(" plane0, plane1, plane2, samp,"); + statement(" coord, options...);"); + statement(" return spvChromaReconstructLinear420XMidpointYMidpoint("); + statement(" plane0, plane1, samp, coord,"); + statement(" options...);"); + end_scope(); // switch (y_chroma_offset) + end_scope(); // case x::midpoint + end_scope(); // switch (x_chroma_offset) + end_scope(); // case 420: + end_scope(); // switch (resolution) + end_scope(); // if (multiplanar) + statement("return plane0.sample(samp, coord, options...);"); + end_scope(); // do_sample() + statement("template "); + statement("vec sample(float2 coord, LodOptions... options) const thread"); + begin_scope(); + statement("vec s = spvTextureSwizzle(do_sample(coord, options...), swizzle);"); + statement("if (ycbcr_samp.get_ycbcr_model() == spvYCbCrModelConversion::rgb_identity)"); + statement(" return s;"); + statement(""); + statement("switch (ycbcr_samp.get_ycbcr_range())"); + begin_scope(); + statement("case spvYCbCrRange::itu_full:"); + statement(" s = spvExpandITUFullRange(s, ycbcr_samp.get_bpc());"); + statement(" break;"); + statement("case spvYCbCrRange::itu_narrow:"); + statement(" s = spvExpandITUNarrowRange(s, ycbcr_samp.get_bpc());"); + statement(" break;"); + end_scope(); + statement(""); + statement("switch (ycbcr_samp.get_ycbcr_model())"); + begin_scope(); + statement("case spvYCbCrModelConversion::rgb_identity:"); // Silence Clang warning + statement("case spvYCbCrModelConversion::ycbcr_identity:"); + statement(" return s;"); + statement("case spvYCbCrModelConversion::ycbcr_bt_709:"); + statement(" return spvConvertYCbCrBT709(s);"); + statement("case spvYCbCrModelConversion::ycbcr_bt_601:"); + statement(" return spvConvertYCbCrBT601(s);"); + statement("case spvYCbCrModelConversion::ycbcr_bt_2020:"); + statement(" return spvConvertYCbCrBT2020(s);"); + end_scope(); + end_scope(); + statement(""); + // Sampler Y'CbCr conversion forbids offsets. + statement("vec sample(float2 coord, int2 offset) const thread"); + begin_scope(); + if (msl_options.swizzle_texture_samples) + statement("return spvTextureSwizzle(plane0.sample(samp, coord, offset), swizzle);"); + else + statement("return plane0.sample(samp, coord, offset);"); + end_scope(); + statement("template"); + statement("vec sample(float2 coord, lod_options options, int2 offset) const thread"); + begin_scope(); + if (msl_options.swizzle_texture_samples) + statement("return spvTextureSwizzle(plane0.sample(samp, coord, options, offset), swizzle);"); + else + statement("return plane0.sample(samp, coord, options, offset);"); + end_scope(); + statement("#if __HAVE_MIN_LOD_CLAMP__"); + statement("vec sample(float2 coord, bias b, min_lod_clamp min_lod, int2 offset) const thread"); + begin_scope(); + statement("return plane0.sample(samp, coord, b, min_lod, offset);"); + end_scope(); + statement( + "vec sample(float2 coord, gradient2d grad, min_lod_clamp min_lod, int2 offset) const thread"); + begin_scope(); + statement("return plane0.sample(samp, coord, grad, min_lod, offset);"); + end_scope(); + statement("#endif"); + statement(""); + // Y'CbCr conversion forbids all operations but sampling. + statement("vec read(uint2 coord, uint lod = 0) const thread"); + begin_scope(); + statement("return plane0.read(coord, lod);"); + end_scope(); + statement(""); + statement("vec gather(float2 coord, int2 offset = int2(0), component c = component::x) const thread"); + begin_scope(); + if (msl_options.swizzle_texture_samples) + statement("return spvGatherSwizzle(plane0, samp, swizzle, c, coord, offset);"); + else + statement("return plane0.gather(samp, coord, offset, c);"); + end_scope(); + end_scope_decl(); + statement(""); + break; + + case SPVFuncImplRayQueryIntersectionParams: + statement("intersection_params spvMakeIntersectionParams(uint flags)"); + begin_scope(); + statement("intersection_params ip;"); + statement("if ((flags & ", RayFlagsOpaqueKHRMask, ") != 0)"); + statement(" ip.force_opacity(forced_opacity::opaque);"); + statement("if ((flags & ", RayFlagsNoOpaqueKHRMask, ") != 0)"); + statement(" ip.force_opacity(forced_opacity::non_opaque);"); + statement("if ((flags & ", RayFlagsTerminateOnFirstHitKHRMask, ") != 0)"); + statement(" ip.accept_any_intersection(true);"); + // RayFlagsSkipClosestHitShaderKHRMask is not available in MSL + statement("if ((flags & ", RayFlagsCullBackFacingTrianglesKHRMask, ") != 0)"); + statement(" ip.set_triangle_cull_mode(triangle_cull_mode::back);"); + statement("if ((flags & ", RayFlagsCullFrontFacingTrianglesKHRMask, ") != 0)"); + statement(" ip.set_triangle_cull_mode(triangle_cull_mode::front);"); + statement("if ((flags & ", RayFlagsCullOpaqueKHRMask, ") != 0)"); + statement(" ip.set_opacity_cull_mode(opacity_cull_mode::opaque);"); + statement("if ((flags & ", RayFlagsCullNoOpaqueKHRMask, ") != 0)"); + statement(" ip.set_opacity_cull_mode(opacity_cull_mode::non_opaque);"); + statement("if ((flags & ", RayFlagsSkipTrianglesKHRMask, ") != 0)"); + statement(" ip.set_geometry_cull_mode(geometry_cull_mode::triangle);"); + statement("if ((flags & ", RayFlagsSkipAABBsKHRMask, ") != 0)"); + statement(" ip.set_geometry_cull_mode(geometry_cull_mode::bounding_box);"); + statement("return ip;"); + end_scope(); + statement(""); + break; + + case SPVFuncImplVariableDescriptor: + statement("template"); + statement("struct spvDescriptor"); + begin_scope(); + statement("T value;"); + end_scope_decl(); + statement(""); + break; + + case SPVFuncImplVariableSizedDescriptor: + statement("template"); + statement("struct spvBufferDescriptor;"); + statement(""); + statement("template"); + statement("struct spvBufferDescriptor"); + begin_scope(); + statement("device T* value;"); + statement("int length;"); + statement("int padding;"); + end_scope_decl(); + statement(""); + break; + + case SPVFuncImplVariableDescriptorArray: + if (spv_function_implementations.count(SPVFuncImplVariableDescriptor) != 0) + { + statement("template"); + statement("struct spvDescriptorArray"); + begin_scope(); + statement("spvDescriptorArray(const device spvDescriptor* ptr_) : ptr(&ptr_->value) {}"); + statement("spvDescriptorArray(const device void *ptr_) : spvDescriptorArray(static_cast*>(ptr_)) {}"); + statement("const device T& operator [] (size_t i) const { return ptr[i]; }"); + statement("const device T* ptr;"); + end_scope_decl(); + statement(""); + } + else + { + statement("template"); + statement("struct spvDescriptorArray;"); + statement(""); + } + + if (msl_options.runtime_array_rich_descriptor && + spv_function_implementations.count(SPVFuncImplVariableSizedDescriptor) != 0) + { + statement("template"); + statement("struct spvDescriptorArray"); + begin_scope(); + statement("spvDescriptorArray(const device spvBufferDescriptor* ptr_) : ptr(ptr_) {}"); + statement("spvDescriptorArray(const device void *ptr_) : spvDescriptorArray(static_cast*>(ptr_)) {}"); + statement("device T* operator [] (size_t i) const { return ptr[i].value; }"); + statement("int length(int i) const { return ptr[i].length; }"); + statement("const device spvBufferDescriptor* ptr;"); + end_scope_decl(); + statement(""); + } + break; + + case SPVFuncImplPaddedStd140: + // .data is used in access chain. + statement("template "); + statement("struct spvPaddedStd140 { alignas(16) T data; };"); + statement("template "); + statement("using spvPaddedStd140Matrix = spvPaddedStd140[n];"); + statement(""); + break; + + case SPVFuncImplPaddedArrayElement: + // .data is used in access chain. + statement("template "); + statement("struct spvPaddedArrayElement { T data; char padding[stride - sizeof(T)]; };"); + statement(""); + break; + + case SPVFuncImplReduceAdd: + // Metal doesn't support __builtin_reduce_add or simd_reduce_add, so we need this. + // Metal also doesn't support the other vector builtins, which would have been useful to make this a single template. + + statement("template "); + statement("T reduce_add(vec v) { return v.x + v.y; }"); + + statement("template "); + statement("T reduce_add(vec v) { return v.x + v.y + v.z; }"); + + statement("template "); + statement("T reduce_add(vec v) { return v.x + v.y + v.z + v.w; }"); + + statement(""); + break; + + case SPVFuncImplImageFence: + statement("template "); + statement("void spvImageFence(ImageT img) { img.fence(); }"); + statement(""); + break; + + case SPVFuncImplTextureCast: + statement("template "); + statement("T spvTextureCast(U img)"); + begin_scope(); + // MSL complains if you try to cast the texture itself, but casting the reference type is ... ok? *shrug* + // Gotta go what you gotta do I suppose. + statement("return reinterpret_cast(img);"); + end_scope(); + statement(""); + break; + + case SPVFuncImplMulExtended: + // Compiler may hit an internal error with mulhi, but doesn't when encapsulated for some reason. + statement("template"); + statement("[[clang::optnone]] T spvMulExtended(V l, V r)"); + begin_scope(); + statement("return T{U(l * r), U(mulhi(l, r))};"); + end_scope(); + statement(""); + break; + + case SPVFuncImplSetMeshOutputsEXT: + statement("void spvSetMeshOutputsEXT(uint gl_LocalInvocationIndex, threadgroup uint2& spvMeshSizes, uint vertexCount, uint primitiveCount)"); + begin_scope(); + statement("if (gl_LocalInvocationIndex == 0)"); + begin_scope(); + statement("spvMeshSizes.x = vertexCount;"); + statement("spvMeshSizes.y = primitiveCount;"); + end_scope(); + end_scope(); + statement(""); + break; + + case SPVFuncImplAssume: + statement_no_indent("#if defined(__has_builtin)"); + statement_no_indent("#if !defined(SPV_ASSUME) && __has_builtin(__builtin_assume)"); + statement_no_indent("#define SPV_ASSUME(x) __builtin_assume(x);"); + statement_no_indent("#endif"); + statement_no_indent("#if !defined(SPV_EXPECT) && __has_builtin(__builtin_expect)"); + statement_no_indent("#define SPV_EXPECT(x, y) __builtin_expect(x, y);"); + statement_no_indent("#endif"); + statement_no_indent("#endif"); + + statement_no_indent("#ifndef SPV_ASSUME"); + statement_no_indent("#define SPV_ASSUME(x)"); + statement_no_indent("#endif"); + + statement_no_indent("#ifndef SPV_EXPECT"); + statement_no_indent("#define SPV_EXPECT(x, y) x"); + statement_no_indent("#endif"); + + break; + + default: + break; + } + } +} + +static string inject_top_level_storage_qualifier(const string &expr, const string &qualifier) +{ + // Easier to do this through text munging since the qualifier does not exist in the type system at all, + // and plumbing in all that information is not very helpful. + size_t last_reference = expr.find_last_of('&'); + size_t last_pointer = expr.find_last_of('*'); + size_t last_significant = string::npos; + + if (last_reference == string::npos) + last_significant = last_pointer; + else if (last_pointer == string::npos) + last_significant = last_reference; + else + last_significant = max(last_reference, last_pointer); + + if (last_significant == string::npos) + return join(qualifier, " ", expr); + else + { + return join(expr.substr(0, last_significant + 1), " ", + qualifier, expr.substr(last_significant + 1, string::npos)); + } +} + +void CompilerMSL::declare_constant_arrays() +{ + bool fully_inlined = ir.ids_for_type[TypeFunction].size() == 1; + + // MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to + // global constants directly, so we are able to use constants as variable expressions. + bool emitted = false; + + ir.for_each_typed_id([&](uint32_t, SPIRConstant &c) { + if (c.specialization) + return; + + auto &type = this->get(c.constant_type); + // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries. + // FIXME: However, hoisting constants to main() means we need to pass down constant arrays to leaf functions if they are used there. + // If there are multiple functions in the module, drop this case to avoid breaking use cases which do not need to + // link into Metal libraries. This is hacky. + if (is_array(type) && (!fully_inlined || is_scalar(type) || is_vector(type))) + { + add_resource_name(c.self); + auto name = to_name(c.self); + statement(inject_top_level_storage_qualifier(variable_decl(type, name), "constant"), + " = ", constant_expression(c), ";"); + emitted = true; + } + }); + + if (emitted) + statement(""); +} + +// Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries +void CompilerMSL::declare_complex_constant_arrays() +{ + // If we do not have a fully inlined module, we did not opt in to + // declaring constant arrays of complex types. See CompilerMSL::declare_constant_arrays(). + bool fully_inlined = ir.ids_for_type[TypeFunction].size() == 1; + if (!fully_inlined) + return; + + // MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to + // global constants directly, so we are able to use constants as variable expressions. + bool emitted = false; + + ir.for_each_typed_id([&](uint32_t, SPIRConstant &c) { + if (c.specialization) + return; + + auto &type = this->get(c.constant_type); + if (is_array(type) && !(is_scalar(type) || is_vector(type))) + { + add_resource_name(c.self); + auto name = to_name(c.self); + statement("", variable_decl(type, name), " = ", constant_expression(c), ";"); + emitted = true; + } + }); + + if (emitted) + statement(""); +} + +void CompilerMSL::emit_resources() +{ + declare_constant_arrays(); + + // Emit the special [[stage_in]] and [[stage_out]] interface blocks which we created. + emit_interface_block(stage_out_var_id); + emit_interface_block(patch_stage_out_var_id); + emit_interface_block(stage_in_var_id); + emit_interface_block(patch_stage_in_var_id); + + if (get_execution_model() == ExecutionModelMeshEXT) + { + auto &execution = get_entry_point(); + const char *topology = ""; + if (execution.flags.get(ExecutionModeOutputTrianglesEXT)) + topology = "topology::triangle"; + else if (execution.flags.get(ExecutionModeOutputLinesEXT)) + topology = "topology::line"; + else if (execution.flags.get(ExecutionModeOutputPoints)) + topology = "topology::point"; + + const char *per_vertex = mesh_out_per_vertex ? "spvPerVertex" : "float4"; + const char *per_primitive = mesh_out_per_primitive ? "spvPerPrimitive" : "void"; + statement("using spvMesh_t = mesh<", per_vertex, ", ", per_primitive, ", ", execution.output_vertices, ", ", + execution.output_primitives, ", ", topology, ">;"); + statement(""); + } +} + +// Emit declarations for the specialization Metal function constants +void CompilerMSL::emit_specialization_constants_and_structs() +{ + SpecializationConstant wg_x, wg_y, wg_z; + ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z); + if (workgroup_size_id == 0 && is_mesh_shader()) + { + auto &execution = get_entry_point(); + statement("constant uint3 ", builtin_to_glsl(BuiltInWorkgroupSize, StorageClassWorkgroup), + " [[maybe_unused]] = ", "uint3(", execution.workgroup_size.x, ", ", execution.workgroup_size.y, ", ", + execution.workgroup_size.z, ");"); + statement(""); + } + + bool emitted = false; + unordered_set declared_structs; + unordered_set aligned_structs; + + // First, we need to deal with scalar block layout. + // It is possible that a struct may have to be placed at an alignment which does not match the innate alignment of the struct itself. + // In that case, if such a case exists for a struct, we must force that all elements of the struct become packed_ types. + // This makes the struct alignment as small as physically possible. + // When we actually align the struct later, we can insert padding as necessary to make the packed members behave like normally aligned types. + ir.for_each_typed_id([&](uint32_t type_id, const SPIRType &type) { + if (type.basetype == SPIRType::Struct && + has_extended_decoration(type_id, SPIRVCrossDecorationBufferBlockRepacked)) + mark_scalar_layout_structs(type); + }); + + bool builtin_block_type_is_required = is_mesh_shader(); + // Very special case. If gl_PerVertex is initialized as an array (tessellation) + // we have to potentially emit the gl_PerVertex struct type so that we can emit a constant LUT. + ir.for_each_typed_id([&](uint32_t, SPIRConstant &c) { + auto &type = this->get(c.constant_type); + if (is_array(type) && has_decoration(type.self, DecorationBlock) && is_builtin_type(type)) + builtin_block_type_is_required = true; + }); + + // Very particular use of the soft loop lock. + // align_struct may need to create custom types on the fly, but we don't care about + // these types for purpose of iterating over them in ir.ids_for_type and friends. + auto loop_lock = ir.create_loop_soft_lock(); + + // Physical storage buffer pointers can have cyclical references, + // so emit forward declarations of them before other structs. + // Ignore type_id because we want the underlying struct type from the pointer. + ir.for_each_typed_id([&](uint32_t /* type_id */, const SPIRType &type) { + if (type.basetype == SPIRType::Struct && + type.pointer && type.storage == StorageClassPhysicalStorageBuffer && + declared_structs.count(type.self) == 0) + { + statement("struct ", to_name(type.self), ";"); + declared_structs.insert(type.self); + emitted = true; + } + }); + if (emitted) + statement(""); + + emitted = false; + declared_structs.clear(); + + // It is possible to have multiple spec constants that use the same spec constant ID. + // The most common cause of this is defining spec constants in GLSL while also declaring + // the workgroup size to use those spec constants. But, Metal forbids declaring more than + // one variable with the same function constant ID. + // In this case, we must only declare one variable with the [[function_constant(id)]] + // attribute, and use its initializer to initialize all the spec constants with + // that ID. + std::unordered_map unique_func_constants; + + for (auto &id_ : ir.ids_for_constant_undef_or_type) + { + auto &id = ir.ids[id_]; + + if (id.get_type() == TypeConstant) + { + auto &c = id.get(); + + if (c.self == workgroup_size_id) + { + // TODO: This can be expressed as a [[threads_per_threadgroup]] input semantic, but we need to know + // the work group size at compile time in SPIR-V, and [[threads_per_threadgroup]] would need to be passed around as a global. + // The work group size may be a specialization constant. + statement("constant uint3 ", builtin_to_glsl(BuiltInWorkgroupSize, StorageClassWorkgroup), + " [[maybe_unused]] = ", constant_expression(get(workgroup_size_id)), ";"); + emitted = true; + } + else if (c.specialization) + { + auto &type = get(c.constant_type); + string sc_type_name = type_to_glsl(type); + add_resource_name(c.self); + string sc_name = to_name(c.self); + + // Function constants are only supported in MSL 1.2 and later. + // If we don't support it just declare the "default" directly. + // This "default" value can be overridden to the true specialization constant by the API user. + // Specialization constants which are used as array length expressions cannot be function constants in MSL, + // so just fall back to macros. + if (msl_options.supports_msl_version(1, 2) && has_decoration(c.self, DecorationSpecId) && + !c.is_used_as_array_length) + { + // Only scalar, non-composite values can be function constants. + uint32_t constant_id = get_decoration(c.self, DecorationSpecId); + if (!unique_func_constants.count(constant_id)) + unique_func_constants.insert(make_pair(constant_id, c.self)); + SPIRType::BaseType sc_tmp_type = expression_type(unique_func_constants[constant_id]).basetype; + string sc_tmp_name = to_name(unique_func_constants[constant_id]) + "_tmp"; + if (unique_func_constants[constant_id] == c.self) + statement("constant ", sc_type_name, " ", sc_tmp_name, " [[function_constant(", constant_id, + ")]];"); + // RenderDoc and other instrumentation may reuse the same SpecId with different base types. + // We deduplicate to one [[function_constant(id)]] temp and then initialize all variants from it. + // Metal forbids as_type to/from 'bool', so if either side is Boolean, avoid bitcasting here and + // prefer a value cast via a constructor instead (e.g. uint(tmp) / float(tmp) / bool(tmp)). + // This preserves expected toggle semantics and prevents illegal MSL like as_type(bool_tmp). + { + string sc_true_expr; + if (sc_tmp_type == type.basetype) + sc_true_expr = sc_tmp_name; + else if (sc_tmp_type == SPIRType::Boolean || type.basetype == SPIRType::Boolean) + sc_true_expr = join(sc_type_name, "(", sc_tmp_name, ")"); + else + sc_true_expr = bitcast_expression(type, sc_tmp_type, sc_tmp_name); + statement("constant ", sc_type_name, " ", sc_name, " = is_function_constant_defined(", sc_tmp_name, + ") ? ", sc_true_expr, " : ", constant_expression(c), ";"); + } + } + else if (has_decoration(c.self, DecorationSpecId)) + { + // Fallback to macro overrides. + uint32_t constant_id = get_decoration(c.self, DecorationSpecId); + c.specialization_constant_macro_name = + constant_value_macro_name(constant_id); + + statement("#ifndef ", c.specialization_constant_macro_name); + statement("#define ", c.specialization_constant_macro_name, " ", constant_expression(c)); + statement("#endif"); + statement("constant ", sc_type_name, " ", sc_name, " = ", c.specialization_constant_macro_name, + ";"); + + // Record the usage of macro + constant_macro_ids.insert(constant_id); + } + else + { + // Composite specialization constants must be built from other specialization constants. + statement("constant ", sc_type_name, " ", sc_name, " = ", constant_expression(c), ";"); + } + emitted = true; + } + } + else if (id.get_type() == TypeConstantOp) + { + auto &c = id.get(); + auto &type = get(c.basetype); + add_resource_name(c.self); + auto name = to_name(c.self); + statement("constant ", variable_decl(type, name), " = ", constant_op_expression(c), ";"); + emitted = true; + } + else if (id.get_type() == TypeType) + { + // Output non-builtin interface structs. These include local function structs + // and structs nested within uniform and read-write buffers. + auto &type = id.get(); + TypeID type_id = type.self; + + bool is_struct = (type.basetype == SPIRType::Struct) && type.array.empty() && !type.pointer; + bool is_block = + has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + + bool is_builtin_block = is_block && is_builtin_type(type); + bool is_declarable_struct = is_struct && (!is_builtin_block || builtin_block_type_is_required); + + // We'll declare this later. + if (stage_out_var_id && get_stage_out_struct_type().self == type_id) + is_declarable_struct = false; + if (patch_stage_out_var_id && get_patch_stage_out_struct_type().self == type_id) + is_declarable_struct = false; + if (stage_in_var_id && get_stage_in_struct_type().self == type_id) + is_declarable_struct = false; + if (patch_stage_in_var_id && get_patch_stage_in_struct_type().self == type_id) + is_declarable_struct = false; + + // Special case. Declare builtin struct anyways if we need to emit a threadgroup version of it. + if (stage_out_masked_builtin_type_id == type_id) + is_declarable_struct = true; + + // Align and emit declarable structs...but avoid declaring each more than once. + if (is_declarable_struct && declared_structs.count(type_id) == 0) + { + if (emitted) + statement(""); + emitted = false; + + declared_structs.insert(type_id); + + if (has_extended_decoration(type_id, SPIRVCrossDecorationBufferBlockRepacked)) + align_struct(type, aligned_structs); + + // Make sure we declare the underlying struct type, and not the "decorated" type with pointers, etc. + emit_struct(get(type_id)); + } + } + else if (id.get_type() == TypeUndef) + { + auto &undef = id.get(); + auto &type = get(undef.basetype); + // OpUndef can be void for some reason ... + if (type.basetype == SPIRType::Void) + return; + + // Undefined global memory is not allowed in MSL. + // Declare constant and init to zeros. Use {}, as global constructors can break Metal. + statement( + inject_top_level_storage_qualifier(variable_decl(type, to_name(undef.self), undef.self), "constant"), + " = {};"); + emitted = true; + } + } + + if (emitted) + statement(""); +} + +void CompilerMSL::emit_binary_ptr_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1); + emit_op(result_type, result_id, join(to_ptr_expression(op0), " ", op, " ", to_ptr_expression(op1)), forward); + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +string CompilerMSL::to_ptr_expression(uint32_t id, bool register_expression_read) +{ + auto *e = maybe_get(id); + auto expr = enclose_expression(e && e->need_transpose ? e->expression : to_expression(id, register_expression_read)); + if (!should_dereference(id)) + expr = address_of_expression(expr); + return expr; +} + +void CompilerMSL::emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, + const char *op) +{ + bool forward = should_forward(op0) && should_forward(op1); + emit_op(result_type, result_id, + join("(isunordered(", to_enclosed_unpacked_expression(op0), ", ", to_enclosed_unpacked_expression(op1), + ") || ", to_enclosed_unpacked_expression(op0), " ", op, " ", to_enclosed_unpacked_expression(op1), + ")"), + forward); + + inherit_expression_dependencies(result_id, op0); + inherit_expression_dependencies(result_id, op1); +} + +bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id, uint32_t ptr) +{ + auto &ptr_type = expression_type(ptr); + auto &result_type = get(result_type_id); + if (ptr_type.storage != StorageClassInput && ptr_type.storage != StorageClassOutput) + return false; + if (ptr_type.storage == StorageClassOutput && is_tese_shader()) + return false; + + if (has_decoration(ptr, DecorationPatch)) + return false; + bool ptr_is_io_variable = ir.ids[ptr].get_type() == TypeVariable; + + bool flattened_io = variable_storage_requires_stage_io(ptr_type.storage); + + bool flat_data_type = flattened_io && + (is_matrix(result_type) || is_array(result_type) || result_type.basetype == SPIRType::Struct); + + // Edge case, even with multi-patch workgroups, we still need to unroll load + // if we're loading control points directly. + if (ptr_is_io_variable && is_array(result_type)) + flat_data_type = true; + + if (!flat_data_type) + return false; + + // Now, we must unflatten a composite type and take care of interleaving array access with gl_in/gl_out. + // Lots of painful code duplication since we *really* should not unroll these kinds of loads in entry point fixup + // unless we're forced to do this when the code is emitting inoptimal OpLoads. + string expr; + + uint32_t interface_index = get_extended_decoration(ptr, SPIRVCrossDecorationInterfaceMemberIndex); + auto *var = maybe_get_backing_variable(ptr); + auto &expr_type = get_pointee_type(ptr_type.self); + + const auto &iface_type = expression_type(stage_in_ptr_var_id); + + if (!flattened_io) + { + // Simplest case for multi-patch workgroups, just unroll array as-is. + if (interface_index == uint32_t(-1)) + return false; + + expr += type_to_glsl(result_type) + "({ "; + uint32_t num_control_points = to_array_size_literal(result_type, uint32_t(result_type.array.size()) - 1); + + for (uint32_t i = 0; i < num_control_points; i++) + { + const uint32_t indices[2] = { i, interface_index }; + AccessChainMeta meta; + expr += access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, &meta, nullptr); + if (i + 1 < num_control_points) + expr += ", "; + } + expr += " })"; + } + else if (result_type.array.size() > 2) + { + SPIRV_CROSS_THROW("Cannot load tessellation IO variables with more than 2 dimensions."); + } + else if (result_type.array.size() == 2) + { + if (!ptr_is_io_variable) + SPIRV_CROSS_THROW("Loading an array-of-array must be loaded directly from an IO variable."); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + if (result_type.basetype == SPIRType::Struct || is_matrix(result_type)) + SPIRV_CROSS_THROW("Cannot load array-of-array of composite type in tessellation IO."); + + expr += type_to_glsl(result_type) + "({ "; + uint32_t num_control_points = to_array_size_literal(result_type, 1); + uint32_t base_interface_index = interface_index; + + auto &sub_type = get(result_type.parent_type); + + for (uint32_t i = 0; i < num_control_points; i++) + { + expr += type_to_glsl(sub_type) + "({ "; + interface_index = base_interface_index; + uint32_t array_size = to_array_size_literal(result_type, 0); + for (uint32_t j = 0; j < array_size; j++, interface_index++) + { + const uint32_t indices[2] = { i, interface_index }; + + AccessChainMeta meta; + expr += access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, + &meta, nullptr); + if (!is_matrix(sub_type) && sub_type.basetype != SPIRType::Struct && + expr_type.vecsize > sub_type.vecsize) + expr += vector_swizzle(sub_type.vecsize, 0); + + if (j + 1 < array_size) + expr += ", "; + } + expr += " })"; + if (i + 1 < num_control_points) + expr += ", "; + } + expr += " })"; + } + else if (result_type.basetype == SPIRType::Struct) + { + bool is_array_of_struct = is_array(result_type); + if (is_array_of_struct && !ptr_is_io_variable) + SPIRV_CROSS_THROW("Loading array of struct from IO variable must come directly from IO variable."); + + uint32_t num_control_points = 1; + if (is_array_of_struct) + { + num_control_points = to_array_size_literal(result_type, 0); + expr += type_to_glsl(result_type) + "({ "; + } + + auto &struct_type = is_array_of_struct ? get(result_type.parent_type) : result_type; + assert(struct_type.array.empty()); + + for (uint32_t i = 0; i < num_control_points; i++) + { + expr += type_to_glsl(struct_type) + "{ "; + for (uint32_t j = 0; j < uint32_t(struct_type.member_types.size()); j++) + { + // The base interface index is stored per variable for structs. + if (var) + { + interface_index = + get_extended_member_decoration(var->self, j, SPIRVCrossDecorationInterfaceMemberIndex); + } + + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + const auto &mbr_type = get(struct_type.member_types[j]); + const auto &expr_mbr_type = get(expr_type.member_types[j]); + if (is_matrix(mbr_type) && ptr_type.storage == StorageClassInput) + { + expr += type_to_glsl(mbr_type) + "("; + for (uint32_t k = 0; k < mbr_type.columns; k++, interface_index++) + { + if (is_array_of_struct) + { + const uint32_t indices[2] = { i, interface_index }; + AccessChainMeta meta; + expr += access_chain_internal( + stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, + &meta, nullptr); + } + else + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_mbr_type.vecsize > mbr_type.vecsize) + expr += vector_swizzle(mbr_type.vecsize, 0); + + if (k + 1 < mbr_type.columns) + expr += ", "; + } + expr += ")"; + } + else if (is_array(mbr_type)) + { + expr += type_to_glsl(mbr_type) + "({ "; + uint32_t array_size = to_array_size_literal(mbr_type, 0); + for (uint32_t k = 0; k < array_size; k++, interface_index++) + { + if (is_array_of_struct) + { + const uint32_t indices[2] = { i, interface_index }; + AccessChainMeta meta; + expr += access_chain_internal( + stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, + &meta, nullptr); + } + else + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_mbr_type.vecsize > mbr_type.vecsize) + expr += vector_swizzle(mbr_type.vecsize, 0); + + if (k + 1 < array_size) + expr += ", "; + } + expr += " })"; + } + else + { + if (is_array_of_struct) + { + const uint32_t indices[2] = { i, interface_index }; + AccessChainMeta meta; + expr += access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, + &meta, nullptr); + } + else + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_mbr_type.vecsize > mbr_type.vecsize) + expr += vector_swizzle(mbr_type.vecsize, 0); + } + + if (j + 1 < struct_type.member_types.size()) + expr += ", "; + } + expr += " }"; + if (i + 1 < num_control_points) + expr += ", "; + } + if (is_array_of_struct) + expr += " })"; + } + else if (is_matrix(result_type)) + { + bool is_array_of_matrix = is_array(result_type); + if (is_array_of_matrix && !ptr_is_io_variable) + SPIRV_CROSS_THROW("Loading array of matrix from IO variable must come directly from IO variable."); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + if (is_array_of_matrix) + { + // Loading a matrix from each control point. + uint32_t base_interface_index = interface_index; + uint32_t num_control_points = to_array_size_literal(result_type, 0); + expr += type_to_glsl(result_type) + "({ "; + + auto &matrix_type = get_variable_element_type(get(ptr)); + + for (uint32_t i = 0; i < num_control_points; i++) + { + interface_index = base_interface_index; + expr += type_to_glsl(matrix_type) + "("; + for (uint32_t j = 0; j < result_type.columns; j++, interface_index++) + { + const uint32_t indices[2] = { i, interface_index }; + + AccessChainMeta meta; + expr += access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, + &meta, nullptr); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + if (j + 1 < result_type.columns) + expr += ", "; + } + expr += ")"; + if (i + 1 < num_control_points) + expr += ", "; + } + + expr += " })"; + } + else + { + expr += type_to_glsl(result_type) + "("; + for (uint32_t i = 0; i < result_type.columns; i++, interface_index++) + { + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + if (i + 1 < result_type.columns) + expr += ", "; + } + expr += ")"; + } + } + else if (ptr_is_io_variable) + { + assert(is_array(result_type)); + assert(result_type.array.size() == 1); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + // We're loading an array directly from a global variable. + // This means we're loading one member from each control point. + expr += type_to_glsl(result_type) + "({ "; + uint32_t num_control_points = to_array_size_literal(result_type, 0); + + for (uint32_t i = 0; i < num_control_points; i++) + { + const uint32_t indices[2] = { i, interface_index }; + + AccessChainMeta meta; + expr += access_chain_internal(stage_in_ptr_var_id, indices, 2, + ACCESS_CHAIN_INDEX_IS_LITERAL_BIT | ACCESS_CHAIN_PTR_CHAIN_BIT, + &meta, nullptr); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + + if (i + 1 < num_control_points) + expr += ", "; + } + expr += " })"; + } + else + { + // We're loading an array from a concrete control point. + assert(is_array(result_type)); + assert(result_type.array.size() == 1); + if (interface_index == uint32_t(-1)) + SPIRV_CROSS_THROW("Interface index is unknown. Cannot continue."); + + expr += type_to_glsl(result_type) + "({ "; + uint32_t array_size = to_array_size_literal(result_type, 0); + for (uint32_t i = 0; i < array_size; i++, interface_index++) + { + expr += to_expression(ptr) + "." + to_member_name(iface_type, interface_index); + if (expr_type.vecsize > result_type.vecsize) + expr += vector_swizzle(result_type.vecsize, 0); + if (i + 1 < array_size) + expr += ", "; + } + expr += " })"; + } + + emit_op(result_type_id, id, expr, false); + register_read(id, ptr, false); + return true; +} + +bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t length) +{ + // If this is a per-vertex output, remap it to the I/O array buffer. + + // Any object which did not go through IO flattening shenanigans will go there instead. + // We will unflatten on-demand instead as needed, but not all possible cases can be supported, especially with arrays. + + auto *var = maybe_get_backing_variable(ops[2]); + bool patch = false; + bool flat_data = false; + bool flatten_composites = false; + + bool is_block = false; + bool is_arrayed = false; + + if (var) + { + auto &type = get_variable_data_type(*var); + is_block = has_decoration(type.self, DecorationBlock); + is_arrayed = !type.array.empty(); + + flatten_composites = variable_storage_requires_stage_io(var->storage); + patch = has_decoration(ops[2], DecorationPatch) || is_patch_block(type); + + // Should match strip_array in add_interface_block. + flat_data = var->storage == StorageClassInput || (var->storage == StorageClassOutput && is_tesc_shader()); + + // Patch inputs are treated as normal block IO variables, so they don't deal with this path at all. + if (patch && (!is_block || is_arrayed || var->storage == StorageClassInput)) + flat_data = false; + } + + bool builtin_variable = false; + bool variable_is_flat = false; + + if (var && flat_data) + { + builtin_variable = is_builtin_variable(*var); + + BuiltIn bi_type = BuiltInMax; + if (builtin_variable && !is_block) + bi_type = BuiltIn(get_decoration(var->self, DecorationBuiltIn)); + + variable_is_flat = !builtin_variable || is_block || + bi_type == BuiltInPosition || bi_type == BuiltInPointSize || + bi_type == BuiltInClipDistance || bi_type == BuiltInCullDistance; + } + + if (variable_is_flat) + { + if (auto *ptr_expr = maybe_get(ops[2])) + { + // Too many edge cases in incrementally resolving tessellation access chains. + // Only reasonable option is to completely rematerialize the chain from the start. + SmallVector rematerialize_ops; + rematerialize_ops.push_back(ops[0]); + rematerialize_ops.push_back(ops[1]); + + for (auto expr : ptr_expr->implied_read_expressions) + rematerialize_ops.push_back(expr); + + for (uint32_t i = 3; i < length; i++) + rematerialize_ops.push_back(ops[i]); + + return emit_tessellation_access_chain(rematerialize_ops.data(), uint32_t(rematerialize_ops.size())); + } + + // If output is masked, it is emitted as a "normal" variable, just go through normal code paths. + // Only check this for the first level of access chain. + // Dealing with this for partial access chains should be possible, but awkward. + if (var->storage == StorageClassOutput) + { + bool masked = false; + if (is_block) + { + uint32_t relevant_member_index = patch ? 3 : 4; + // FIXME: This won't work properly if the application first access chains into gl_out element, + // then access chains into the member. Super weird, but theoretically possible ... + if (length > relevant_member_index) + { + uint32_t mbr_idx = get(ops[relevant_member_index]).scalar(); + masked = is_stage_output_block_member_masked(*var, mbr_idx, true); + } + } + else if (var) + masked = is_stage_output_variable_masked(*var); + + if (masked) + return false; + } + + AccessChainMeta meta; + SmallVector indices; + uint32_t next_id = ir.increase_bound_by(1); + + indices.reserve(length - 3 + 1); + + uint32_t first_non_array_index = 4 - (patch ? 1 : 0); + + VariableID stage_var_id; + if (patch) + stage_var_id = var->storage == StorageClassInput ? patch_stage_in_var_id : patch_stage_out_var_id; + else + stage_var_id = var->storage == StorageClassInput ? stage_in_ptr_var_id : stage_out_ptr_var_id; + + VariableID ptr = stage_var_id; + + if (!patch) + { + // Index into gl_in/gl_out with first array index. + indices.push_back(ops[first_non_array_index - 1]); + } + + auto &result_ptr_type = get(ops[0]); + + uint32_t const_mbr_id = next_id++; + uint32_t index = get_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex); + + if (flatten_composites || is_block) + { + uint32_t i = first_non_array_index; + auto *type = &get_variable_element_type(*var); + if (index == uint32_t(-1) && length >= (first_non_array_index + 1)) + { + // Maybe this is a struct type in the input class, in which case + // we put it as a decoration on the corresponding member. + uint32_t mbr_idx = get_constant(ops[first_non_array_index]).scalar(); + index = get_extended_member_decoration(var->self, mbr_idx, + SPIRVCrossDecorationInterfaceMemberIndex); + assert(index != uint32_t(-1)); + i++; + type = &get(type->member_types[mbr_idx]); + } + + // In this case, we're poking into flattened structures and arrays, so now we have to + // combine the following indices. If we encounter a non-constant index, + // we're hosed. + for (; flatten_composites && i < length; ++i) + { + if (!is_array(*type) && !is_matrix(*type) && type->basetype != SPIRType::Struct) + break; + + auto *c = maybe_get(ops[i]); + if (!c || c->specialization) + SPIRV_CROSS_THROW("Trying to dynamically index into an array interface variable in tessellation. " + "This is currently unsupported."); + + // We're in flattened space, so just increment the member index into IO block. + // We can only do this once in the current implementation, so either: + // Struct, Matrix or 1-dimensional array for a control point. + if (type->basetype == SPIRType::Struct && var->storage == StorageClassOutput) + { + // Need to consider holes, since individual block members might be masked away. + uint32_t mbr_idx = c->scalar(); + for (uint32_t j = 0; j < mbr_idx; j++) + if (!is_stage_output_block_member_masked(*var, j, true)) + index++; + } + else + index += c->scalar(); + + if (type->parent_type) + type = &get(type->parent_type); + else if (type->basetype == SPIRType::Struct) + type = &get(type->member_types[c->scalar()]); + } + + // We're not going to emit the actual member name, we let any further OpLoad take care of that. + // Tag the access chain with the member index we're referencing. + auto &result_pointee_type = get_pointee_type(result_ptr_type); + bool defer_access_chain = flatten_composites && (is_matrix(result_pointee_type) || is_array(result_pointee_type) || + result_pointee_type.basetype == SPIRType::Struct); + + if (!defer_access_chain) + { + // Access the appropriate member of gl_in/gl_out. + set(const_mbr_id, get_uint_type_id(), index, false); + indices.push_back(const_mbr_id); + + // Member index is now irrelevant. + index = uint32_t(-1); + + // Append any straggling access chain indices. + if (i < length) + indices.insert(indices.end(), ops + i, ops + length); + } + else + { + // We must have consumed the entire access chain if we're deferring it. + assert(i == length); + } + + if (index != uint32_t(-1)) + set_extended_decoration(ops[1], SPIRVCrossDecorationInterfaceMemberIndex, index); + else + unset_extended_decoration(ops[1], SPIRVCrossDecorationInterfaceMemberIndex); + } + else + { + if (index != uint32_t(-1)) + { + set(const_mbr_id, get_uint_type_id(), index, false); + indices.push_back(const_mbr_id); + } + + // Member index is now irrelevant. + index = uint32_t(-1); + unset_extended_decoration(ops[1], SPIRVCrossDecorationInterfaceMemberIndex); + + indices.insert(indices.end(), ops + first_non_array_index, ops + length); + } + + // We use the pointer to the base of the input/output array here, + // so this is always a pointer chain. + // This is the start of an access chain, use ptr_chain to index into control point array. + auto e = access_chain(ptr, indices.data(), uint32_t(indices.size()), result_ptr_type, &meta, !patch); + + // Get the actual type of the object that was accessed. If it's a vector type and we changed it, + // then we'll need to add a swizzle. + // For this, we can't necessarily rely on the type of the base expression, because it might be + // another access chain, and it will therefore already have the "correct" type. + auto *expr_type = &get_variable_data_type(*var); + if (has_extended_decoration(ops[2], SPIRVCrossDecorationTessIOOriginalInputTypeID)) + expr_type = &get(get_extended_decoration(ops[2], SPIRVCrossDecorationTessIOOriginalInputTypeID)); + for (uint32_t i = 3; i < length; i++) + { + if (!is_array(*expr_type) && expr_type->basetype == SPIRType::Struct) + expr_type = &get(expr_type->member_types[get(ops[i]).scalar()]); + else + expr_type = &get(expr_type->parent_type); + } + if (!is_array(*expr_type) && !is_matrix(*expr_type) && expr_type->basetype != SPIRType::Struct && + expr_type->vecsize > result_ptr_type.vecsize) + e += vector_swizzle(result_ptr_type.vecsize, 0); + + auto &expr = set(ops[1], std::move(e), ops[0], should_forward(ops[2])); + expr.loaded_from = var->self; + expr.need_transpose = meta.need_transpose; + expr.access_chain = true; + + // Mark the result as being packed if necessary. + if (meta.storage_is_packed) + set_extended_decoration(ops[1], SPIRVCrossDecorationPhysicalTypePacked); + if (meta.storage_physical_type != 0) + set_extended_decoration(ops[1], SPIRVCrossDecorationPhysicalTypeID, meta.storage_physical_type); + if (meta.storage_is_invariant) + set_decoration(ops[1], DecorationInvariant); + // Save the type we found in case the result is used in another access chain. + set_extended_decoration(ops[1], SPIRVCrossDecorationTessIOOriginalInputTypeID, expr_type->self); + + // If we have some expression dependencies in our access chain, this access chain is technically a forwarded + // temporary which could be subject to invalidation. + // Need to assume we're forwarded while calling inherit_expression_depdendencies. + forwarded_temporaries.insert(ops[1]); + // The access chain itself is never forced to a temporary, but its dependencies might. + suppressed_usage_tracking.insert(ops[1]); + + for (uint32_t i = 2; i < length; i++) + { + inherit_expression_dependencies(ops[1], ops[i]); + add_implied_read_expression(expr, ops[i]); + } + + // If we have no dependencies after all, i.e., all indices in the access chain are immutable temporaries, + // we're not forwarded after all. + if (expr.expression_dependencies.empty()) + forwarded_temporaries.erase(ops[1]); + + return true; + } + + // If this is the inner tessellation level, and we're tessellating triangles, + // drop the last index. It isn't an array in this case, so we can't have an + // array reference here. We need to make this ID a variable instead of an + // expression so we don't try to dereference it as a variable pointer. + // Don't do this if the index is a constant 1, though. We need to drop stores + // to that one. + auto *m = ir.find_meta(var ? var->self : ID(0)); + if (is_tesc_shader() && var && m && m->decoration.builtin_type == BuiltInTessLevelInner && + is_tessellating_triangles()) + { + auto *c = maybe_get(ops[3]); + if (c && c->scalar() == 1) + return false; + auto &dest_var = set(ops[1], *var); + dest_var.basetype = ops[0]; + ir.meta[ops[1]] = ir.meta[ops[2]]; + inherit_expression_dependencies(ops[1], ops[2]); + return true; + } + + return false; +} + +bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs) +{ + if (!is_tessellating_triangles()) + return false; + + // In SPIR-V, TessLevelInner always has two elements and TessLevelOuter always has + // four. This is true even if we are tessellating triangles. This allows clients + // to use a single tessellation control shader with multiple tessellation evaluation + // shaders. + // In Metal, however, only the first element of TessLevelInner and the first three + // of TessLevelOuter are accessible. This stems from how in Metal, the tessellation + // levels must be stored to a dedicated buffer in a particular format that depends + // on the patch type. Therefore, in Triangles mode, any store to the second + // inner level or the fourth outer level must be dropped. + const auto *e = maybe_get(id_lhs); + if (!e || !e->access_chain) + return false; + BuiltIn builtin = BuiltIn(get_decoration(e->loaded_from, DecorationBuiltIn)); + if (builtin != BuiltInTessLevelInner && builtin != BuiltInTessLevelOuter) + return false; + auto *c = maybe_get(e->implied_read_expressions[1]); + if (!c) + return false; + return (builtin == BuiltInTessLevelInner && c->scalar() == 1) || + (builtin == BuiltInTessLevelOuter && c->scalar() == 3); +} + +bool CompilerMSL::prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, + StorageClass storage, bool &is_packed) +{ + // If there is any risk of writes happening with the access chain in question, + // and there is a risk of concurrent write access to other components, + // we must cast the access chain to a plain pointer to ensure we only access the exact scalars we expect. + // The MSL compiler refuses to allow component-level access for any non-packed vector types. + // MSL refuses to take address or reference to vector component, even for packed types, so just force + // through the pointer cast. No much we can do sadly. + // For packed types, we could technically omit this if we know the reference does not have to turn into a pointer + // of some kind, but that requires external analysis passes to figure out, and + // this case is likely rare enough that we don't need to bother. + if (storage == StorageClassStorageBuffer || storage == StorageClassWorkgroup) + { + const char *addr_space = storage == StorageClassWorkgroup ? "threadgroup" : "device"; + expr = join("((", addr_space, " ", type_to_glsl(type), "*)&", enclose_expression(expr), ")"); + + // Further indexing should happen with packed rules (array index, not swizzle). + is_packed = true; + return true; + } + else + return false; +} + +bool CompilerMSL::access_chain_needs_stage_io_builtin_translation(uint32_t base) +{ + auto *var = maybe_get_backing_variable(base); + if (!var || !is_tessellation_shader()) + return true; + + // We only need to rewrite builtin access chains when accessing flattened builtins like gl_ClipDistance_N. + // Avoid overriding it back to just gl_ClipDistance. + // This can only happen in scenarios where we cannot flatten/unflatten access chains, so, the only case + // where this triggers is evaluation shader inputs. + bool redirect_builtin = is_tese_shader() ? var->storage == StorageClassOutput : false; + return redirect_builtin; +} + +// Sets the interface member index for an access chain to a pull-model interpolant. +void CompilerMSL::fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length) +{ + auto *var = maybe_get_backing_variable(ops[2]); + if (!var || !pull_model_inputs.count(var->self)) + return; + // Get the base index. + uint32_t interface_index; + auto &var_type = get_variable_data_type(*var); + auto &result_type = get(ops[0]); + auto *type = &var_type; + if (has_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex)) + { + interface_index = get_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex); + } + else + { + // Assume an access chain into a struct variable. + assert(var_type.basetype == SPIRType::Struct); + auto &c = get(ops[3 + var_type.array.size()]); + interface_index = + get_extended_member_decoration(var->self, c.scalar(), SPIRVCrossDecorationInterfaceMemberIndex); + } + // Accumulate indices. We'll have to skip over the one for the struct, if present, because we already accounted + // for that getting the base index. + for (uint32_t i = 3; i < length; ++i) + { + if (is_vector(*type) && !is_array(*type) && is_scalar(result_type)) + { + // We don't want to combine the next index. Actually, we need to save it + // so we know to apply a swizzle to the result of the interpolation. + set_extended_decoration(ops[1], SPIRVCrossDecorationInterpolantComponentExpr, ops[i]); + break; + } + + auto *c = maybe_get(ops[i]); + if (!c || c->specialization) + SPIRV_CROSS_THROW("Trying to dynamically index into an array interface variable using pull-model " + "interpolation. This is currently unsupported."); + + if (type->parent_type) + type = &get(type->parent_type); + else if (type->basetype == SPIRType::Struct) + type = &get(type->member_types[c->scalar()]); + + if (!has_extended_decoration(ops[2], SPIRVCrossDecorationInterfaceMemberIndex) && + i - 3 == var_type.array.size()) + continue; + + interface_index += c->scalar(); + } + // Save this to the access chain itself so we can recover it later when calling an interpolation function. + set_extended_decoration(ops[1], SPIRVCrossDecorationInterfaceMemberIndex, interface_index); +} + + +// If the physical type of a physical buffer pointer has been changed +// to a ulong or ulongn vector, add a cast back to the pointer type. +bool CompilerMSL::check_physical_type_cast(std::string &expr, const SPIRType *type, uint32_t physical_type) +{ + auto *p_physical_type = maybe_get(physical_type); + if (p_physical_type && + p_physical_type->storage == StorageClassPhysicalStorageBuffer && + p_physical_type->basetype == to_unsigned_basetype(64)) + { + if (p_physical_type->vecsize > 1) + expr += ".x"; + + expr = join("((", type_to_glsl(*type), ")", expr, ")"); + return true; + } + + return false; +} + +// Override for MSL-specific syntax instructions +void CompilerMSL::emit_instruction(const Instruction &instruction) +{ +#define MSL_BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op) +#define MSL_PTR_BOP(op) emit_binary_ptr_op(ops[0], ops[1], ops[2], ops[3], #op) + // MSL does care about implicit integer promotion, but those cases are all handled in common code. +#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), false) +#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 MSL_UFOP(op) emit_unary_func_op(ops[0], ops[1], ops[2], #op) +#define MSL_UNORD_BOP(op) emit_binary_unord_op(ops[0], ops[1], ops[2], ops[3], #op) + + auto ops = stream(instruction); + auto opcode = static_cast(instruction.op); + + opcode = get_remapped_spirv_op(opcode); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(instruction); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + switch (opcode) + { + case OpLoad: + { + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + if (is_tessellation_shader()) + { + if (!emit_tessellation_io_load(ops[0], id, ptr)) + CompilerGLSL::emit_instruction(instruction); + } + else + { + auto is_sample_mask = BuiltIn(get_decoration(ptr, DecorationBuiltIn)) == BuiltInSampleMask; + auto ptr_storage = get_expression_effective_storage_class(ptr); + auto *ptr_var = maybe_get_backing_variable(ptr); + + // More edge cases ... Normally composite outputs are lowered at the end, + // but that's not the case for clip-cull arrays. + if (ptr_var && ptr_storage == StorageClassOutput && is_builtin_variable(*ptr_var) && + !is_sample_mask && is_array(get(ops[0]))) + { + emit_uninitialized_temporary_expression(ops[0], id); + auto &type = get(ops[0]); + if (type.array.size() != 1) + SPIRV_CROSS_THROW("Cannot load array of clip-cull distances from array of array."); + if (!type.array_size_literal.front()) + SPIRV_CROSS_THROW("Cannot load array of clip-cull distances from spec constant array size."); + for (uint32_t i = 0; i < type.array[0]; i++) + statement(to_expression(id), "[", i, "] = ", to_expression(ptr), "[", i, "];"); + } + else + { + // Sample mask input for Metal is not an array + if (is_sample_mask) + set_decoration(id, DecorationBuiltIn, BuiltInSampleMask); + CompilerGLSL::emit_instruction(instruction); + } + } + break; + } + + // Comparisons + case OpIEqual: + MSL_BOP_CAST(==, int_type); + break; + + case OpLogicalEqual: + case OpFOrdEqual: + MSL_BOP(==); + break; + + case OpINotEqual: + MSL_BOP_CAST(!=, int_type); + break; + + case OpLogicalNotEqual: + case OpFOrdNotEqual: + // TODO: Should probably negate the == result here. + // Typically OrdNotEqual comes from GLSL which itself does not really specify what + // happens with NaN. + // Consider fixing this if we run into real issues. + MSL_BOP(!=); + break; + + case OpUGreaterThan: + MSL_BOP_CAST(>, uint_type); + break; + + case OpSGreaterThan: + MSL_BOP_CAST(>, int_type); + break; + + case OpFOrdGreaterThan: + MSL_BOP(>); + break; + + case OpUGreaterThanEqual: + MSL_BOP_CAST(>=, uint_type); + break; + + case OpSGreaterThanEqual: + MSL_BOP_CAST(>=, int_type); + break; + + case OpFOrdGreaterThanEqual: + MSL_BOP(>=); + break; + + case OpULessThan: + MSL_BOP_CAST(<, uint_type); + break; + + case OpSLessThan: + MSL_BOP_CAST(<, int_type); + break; + + case OpFOrdLessThan: + MSL_BOP(<); + break; + + case OpULessThanEqual: + MSL_BOP_CAST(<=, uint_type); + break; + + case OpSLessThanEqual: + MSL_BOP_CAST(<=, int_type); + break; + + case OpFOrdLessThanEqual: + MSL_BOP(<=); + break; + + case OpFUnordEqual: + MSL_UNORD_BOP(==); + break; + + case OpFUnordNotEqual: + // not equal in MSL generates une opcodes to begin with. + // Since unordered not equal is how it works in C, just inherit that behavior. + MSL_BOP(!=); + break; + + case OpFUnordGreaterThan: + MSL_UNORD_BOP(>); + break; + + case OpFUnordGreaterThanEqual: + MSL_UNORD_BOP(>=); + break; + + case OpFUnordLessThan: + MSL_UNORD_BOP(<); + break; + + case OpFUnordLessThanEqual: + MSL_UNORD_BOP(<=); + break; + + // Pointer math + case OpPtrEqual: + MSL_PTR_BOP(==); + break; + + case OpPtrNotEqual: + MSL_PTR_BOP(!=); + break; + + case OpPtrDiff: + MSL_PTR_BOP(-); + break; + + // Derivatives + case OpDPdx: + case OpDPdxFine: + case OpDPdxCoarse: + MSL_UFOP(dfdx); + register_control_dependent_expression(ops[1]); + break; + + case OpDPdy: + case OpDPdyFine: + case OpDPdyCoarse: + MSL_UFOP(dfdy); + register_control_dependent_expression(ops[1]); + break; + + case OpFwidth: + case OpFwidthCoarse: + case OpFwidthFine: + MSL_UFOP(fwidth); + register_control_dependent_expression(ops[1]); + break; + + // Bitfield + case OpBitFieldInsert: + { + emit_bitfield_insert_op(ops[0], ops[1], ops[2], ops[3], ops[4], ops[5], "insert_bits", SPIRType::UInt); + break; + } + + case OpBitFieldSExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "extract_bits", int_type, int_type, + SPIRType::UInt, SPIRType::UInt); + break; + } + + case OpBitFieldUExtract: + { + emit_trinary_func_op_bitextract(ops[0], ops[1], ops[2], ops[3], ops[4], "extract_bits", uint_type, uint_type, + SPIRType::UInt, SPIRType::UInt); + break; + } + + case OpBitReverse: + // BitReverse does not have issues with sign since result type must match input type. + MSL_UFOP(reverse_bits); + break; + + case OpBitCount: + { + auto basetype = expression_type(ops[2]).basetype; + emit_unary_func_op_cast(ops[0], ops[1], ops[2], "popcount", basetype, basetype); + break; + } + + case OpSMod: + MSL_BFOP(spvSMod); + break; + + case OpFRem: + MSL_BFOP(fmod); + break; + + case OpFMul: + if (msl_options.invariant_float_math || has_legacy_nocontract(ops[0], ops[1])) + MSL_BFOP(spvFMul); + else + MSL_BOP(*); + break; + + case OpFAdd: + if (msl_options.invariant_float_math || has_legacy_nocontract(ops[0], ops[1])) + MSL_BFOP(spvFAdd); + else + MSL_BOP(+); + break; + + case OpFSub: + if (msl_options.invariant_float_math || has_legacy_nocontract(ops[0], ops[1])) + MSL_BFOP(spvFSub); + else + MSL_BOP(-); + break; + + case OpFmaKHR: + MSL_TFOP(fma); + break; + + // Atomics + case OpAtomicExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t mem_sem = ops[4]; + uint32_t val = ops[5]; + emit_atomic_func_op(result_type, id, "atomic_exchange", opcode, mem_sem, mem_sem, false, ptr, val); + break; + } + + case OpAtomicCompareExchange: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t mem_sem_pass = ops[4]; + uint32_t mem_sem_fail = ops[5]; + uint32_t val = ops[6]; + uint32_t comp = ops[7]; + emit_atomic_func_op(result_type, id, "atomic_compare_exchange_weak", opcode, + mem_sem_pass, mem_sem_fail, true, + ptr, comp, true, false, val); + break; + } + + case OpAtomicCompareExchangeWeak: + SPIRV_CROSS_THROW("OpAtomicCompareExchangeWeak is only supported in kernel profile."); + + case OpAtomicLoad: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t mem_sem = ops[4]; + check_atomic_image(ptr); + emit_atomic_func_op(result_type, id, "atomic_load", opcode, mem_sem, mem_sem, false, ptr, 0); + break; + } + + case OpAtomicStore: + { + uint32_t result_type = expression_type(ops[0]).self; + uint32_t id = ops[0]; + uint32_t ptr = ops[0]; + uint32_t mem_sem = ops[2]; + uint32_t val = ops[3]; + check_atomic_image(ptr); + emit_atomic_func_op(result_type, id, "atomic_store", opcode, mem_sem, mem_sem, false, ptr, val); + break; + } + +#define MSL_AFMO_IMPL(op, valsrc, valconst) \ + do \ + { \ + uint32_t result_type = ops[0]; \ + uint32_t id = ops[1]; \ + uint32_t ptr = ops[2]; \ + uint32_t mem_sem = ops[4]; \ + uint32_t val = valsrc; \ + emit_atomic_func_op(result_type, id, "atomic_fetch_" #op, opcode, \ + mem_sem, mem_sem, false, ptr, val, \ + false, valconst); \ + } while (false) + +#define MSL_AFMO(op) MSL_AFMO_IMPL(op, ops[5], false) +#define MSL_AFMIO(op) MSL_AFMO_IMPL(op, 1, true) + + case OpAtomicIIncrement: + MSL_AFMIO(add); + break; + + case OpAtomicIDecrement: + MSL_AFMIO(sub); + break; + + case OpAtomicIAdd: + case OpAtomicFAddEXT: + MSL_AFMO(add); + break; + + case OpAtomicISub: + MSL_AFMO(sub); + break; + + case OpAtomicSMin: + case OpAtomicUMin: + MSL_AFMO(min); + break; + + case OpAtomicSMax: + case OpAtomicUMax: + MSL_AFMO(max); + break; + + case OpAtomicAnd: + MSL_AFMO(and); + break; + + case OpAtomicOr: + MSL_AFMO(or); + break; + + case OpAtomicXor: + MSL_AFMO(xor); + break; + + // Images + + // Reads == Fetches in Metal + case OpImageRead: + { + // Mark that this shader reads from this image + uint32_t img_id = ops[2]; + auto &type = expression_type(img_id); + auto *p_var = maybe_get_backing_variable(img_id); + if (type.image.dim != DimSubpassData) + { + if (p_var && has_decoration(p_var->self, DecorationNonReadable)) + { + unset_decoration(p_var->self, DecorationNonReadable); + force_recompile(); + } + } + + // Metal requires explicit fences to break up RAW hazards, even within the same shader invocation + if (msl_options.readwrite_texture_fences && p_var && !has_decoration(p_var->self, DecorationNonWritable)) + { + add_spv_func_and_recompile(SPVFuncImplImageFence); + // Need to wrap this with a value type, + // since the Metal headers are broken and do not consider case when the image is a reference. + statement("spvImageFence(", to_expression(img_id), ");"); + } + + emit_texture_op(instruction, false); + break; + } + + // Emulate texture2D atomic operations + case OpImageTexelPointer: + { + // When using the pointer, we need to know which variable it is actually loaded from. + auto *var = maybe_get_backing_variable(ops[2]); + if (var && atomic_image_vars_emulated.count(var->self)) + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + std::string coord = to_expression(ops[3]); + auto &type = expression_type(ops[2]); + if (type.image.dim == Dim2D) + { + coord = join("spvImage2DAtomicCoord(", coord, ", ", to_expression(ops[2]), ")"); + } + + auto &e = set(id, join(to_expression(ops[2]), "_atomic[", coord, "]"), result_type, true); + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); + } + else + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + // Virtual expression. Split this up in the actual image atomic. + // In GLSL and HLSL we are able to resolve the dereference inline, but MSL has + // image.op(coord, ...) syntax. + auto &e = + set(id, join(to_expression(ops[2]), "@", + bitcast_expression(SPIRType::UInt, ops[3])), + result_type, true); + + // When using the pointer, we need to know which variable it is actually loaded from. + e.loaded_from = var ? var->self : ID(0); + inherit_expression_dependencies(id, ops[3]); + } + break; + } + + case OpImageWrite: + { + uint32_t img_id = ops[0]; + uint32_t coord_id = ops[1]; + uint32_t texel_id = ops[2]; + const uint32_t *opt = &ops[3]; + uint32_t length = instruction.length - 3; + + // Bypass pointers because we need the real image struct + auto &type = expression_type(img_id); + auto &img_type = get(type.self); + + // Ensure this image has been marked as being written to and force a + // recommpile so that the image type output will include write access + auto *p_var = maybe_get_backing_variable(img_id); + if (p_var && has_decoration(p_var->self, DecorationNonWritable)) + { + unset_decoration(p_var->self, DecorationNonWritable); + force_recompile(); + } + + bool forward = false; + uint32_t bias = 0; + uint32_t lod = 0; + uint32_t flags = 0; + + if (length) + { + flags = *opt++; + length--; + } + + auto test = [&](uint32_t &v, uint32_t flag) { + if (length && (flags & flag)) + { + v = *opt++; + length--; + } + }; + + test(bias, ImageOperandsBiasMask); + test(lod, ImageOperandsLodMask); + + auto &texel_type = expression_type(texel_id); + auto store_type = texel_type; + store_type.vecsize = 4; + + TextureFunctionArguments args = {}; + args.base.img = img_id; + args.base.imgtype = &img_type; + args.base.is_fetch = true; + args.coord = coord_id; + args.lod = lod; + + string expr; + if (needs_frag_discard_checks()) + expr = join("(", builtin_to_glsl(BuiltInHelperInvocation, StorageClassInput), " ? ((void)0) : "); + expr += join(to_expression(img_id), ".write(", + remap_swizzle(store_type, texel_type.vecsize, to_expression(texel_id)), ", ", + CompilerMSL::to_function_args(args, &forward), ")"); + if (needs_frag_discard_checks()) + expr += ")"; + statement(expr, ";"); + + if (p_var && variable_storage_is_aliased(*p_var)) + flush_all_aliased_variables(); + + break; + } + + case OpImageQuerySize: + case OpImageQuerySizeLod: + { + uint32_t rslt_type_id = ops[0]; + auto &rslt_type = get(rslt_type_id); + + uint32_t id = ops[1]; + + uint32_t img_id = ops[2]; + string img_exp = to_expression(img_id); + auto &img_type = expression_type(img_id); + Dim img_dim = img_type.image.dim; + bool img_is_array = img_type.image.arrayed; + + if (img_type.basetype != SPIRType::Image) + SPIRV_CROSS_THROW("Invalid type for OpImageQuerySize."); + + string lod; + if (opcode == OpImageQuerySizeLod) + { + // LOD index defaults to zero, so don't bother outputing level zero index + string decl_lod = to_expression(ops[3]); + if (decl_lod != "0") + lod = decl_lod; + } + + string expr = type_to_glsl(rslt_type) + "("; + expr += img_exp + ".get_width(" + lod + ")"; + + if (img_dim == Dim2D || img_dim == DimCube || img_dim == Dim3D) + expr += ", " + img_exp + ".get_height(" + lod + ")"; + + if (img_dim == Dim3D) + expr += ", " + img_exp + ".get_depth(" + lod + ")"; + + if (img_is_array) + { + expr += ", " + img_exp + ".get_array_size()"; + if (img_dim == DimCube && msl_options.emulate_cube_array) + expr += " / 6"; + } + + expr += ")"; + + emit_op(rslt_type_id, id, expr, should_forward(img_id)); + + break; + } + + case OpImageQueryLod: + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("ImageQueryLod is only supported on MSL 2.2 and up."); + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t image_id = ops[2]; + uint32_t coord_id = ops[3]; + emit_uninitialized_temporary_expression(result_type, id); + + std::string coord_expr = to_expression(coord_id); + auto sampler_expr = to_sampler_expression(image_id); + auto *combined = maybe_get(image_id); + auto image_expr = combined ? to_expression(combined->image) : to_expression(image_id); + const SPIRType &image_type = expression_type(image_id); + const SPIRType &coord_type = expression_type(coord_id); + + switch (image_type.image.dim) + { + case Dim1D: + if (!msl_options.texture_1D_as_2D) + SPIRV_CROSS_THROW("ImageQueryLod is not supported on 1D textures."); + [[fallthrough]]; + case Dim2D: + if (coord_type.vecsize > 2) + coord_expr = enclose_expression(coord_expr) + ".xy"; + break; + case DimCube: + case Dim3D: + if (coord_type.vecsize > 3) + coord_expr = enclose_expression(coord_expr) + ".xyz"; + break; + default: + SPIRV_CROSS_THROW("Bad image type given to OpImageQueryLod"); + } + + // TODO: It is unclear if calculcate_clamped_lod also conditionally rounds + // the reported LOD based on the sampler. NEAREST miplevel should + // round the LOD, but LINEAR miplevel should not round. + // Let's hope this does not become an issue ... + statement(to_expression(id), ".x = ", image_expr, ".calculate_clamped_lod(", sampler_expr, ", ", + coord_expr, ");"); + statement(to_expression(id), ".y = ", image_expr, ".calculate_unclamped_lod(", sampler_expr, ", ", + coord_expr, ");"); + register_control_dependent_expression(id); + break; + } + +#define MSL_ImgQry(qrytype) \ + do \ + { \ + uint32_t rslt_type_id = ops[0]; \ + auto &rslt_type = get(rslt_type_id); \ + uint32_t id = ops[1]; \ + uint32_t img_id = ops[2]; \ + string img_exp = to_expression(img_id); \ + string expr = type_to_glsl(rslt_type) + "(" + img_exp + ".get_num_" #qrytype "())"; \ + emit_op(rslt_type_id, id, expr, should_forward(img_id)); \ + } while (false) + + case OpImageQueryLevels: + MSL_ImgQry(mip_levels); + break; + + case OpImageQuerySamples: + MSL_ImgQry(samples); + break; + + case OpImage: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto *combined = maybe_get(ops[2]); + + if (combined) + { + auto &e = emit_op(result_type, id, to_expression(combined->image), true, true); + auto *var = maybe_get_backing_variable(combined->image); + if (var) + e.loaded_from = var->self; + } + else + { + auto *var = maybe_get_backing_variable(ops[2]); + SPIRExpression *e; + if (var && has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler)) + e = &emit_op(result_type, id, join(to_expression(ops[2]), ".plane0"), true, true); + else + e = &emit_op(result_type, id, to_expression(ops[2]), true, true); + if (var) + e->loaded_from = var->self; + } + break; + } + + // Casting + case OpQuantizeToF16: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t arg = ops[2]; + string exp = join("spvQuantizeToF16(", to_expression(arg), ")"); + emit_op(result_type, id, exp, should_forward(arg)); + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + if (is_tessellation_shader()) + { + if (!emit_tessellation_access_chain(ops, instruction.length)) + CompilerGLSL::emit_instruction(instruction); + } + else + CompilerGLSL::emit_instruction(instruction); + fix_up_interpolant_access_chain(ops, instruction.length); + break; + + case OpStore: + { + const auto &type = expression_type(ops[0]); + + if (is_out_of_bounds_tessellation_level(ops[0])) + break; + + if (needs_frag_discard_checks() && + (type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform)) + { + // If we're in a continue block, this kludge will make the block too complex + // to emit normally. + assert(current_emitting_block); + auto cont_type = continue_block_type(*current_emitting_block); + if (cont_type != SPIRBlock::ContinueNone && cont_type != SPIRBlock::ComplexLoop) + { + current_emitting_block->complex_continue = true; + force_recompile(); + } + statement("if (!", builtin_to_glsl(BuiltInHelperInvocation, StorageClassInput), ")"); + begin_scope(); + } + if (!maybe_emit_array_assignment(ops[0], ops[1])) + CompilerGLSL::emit_instruction(instruction); + if (needs_frag_discard_checks() && + (type.storage == StorageClassStorageBuffer || type.storage == StorageClassUniform)) + end_scope(); + if (has_decoration(ops[0], DecorationBuiltIn) && get_decoration(ops[0], DecorationBuiltIn) == BuiltInPointSize) + writes_to_point_size = true; + + break; + } + + // Compute barriers + case OpMemoryBarrier: + emit_barrier(0, ops[0], ops[1]); + break; + + case OpControlBarrier: + // In GLSL a memory barrier is often followed by a control barrier. + // But in MSL, memory barriers are also control barriers (before MSL 3.2), so don't + // emit a simple control barrier if a memory barrier has just been emitted. + if (previous_instruction_opcode != OpMemoryBarrier || msl_options.supports_msl_version(3, 2)) + emit_barrier(ops[0], ops[1], ops[2]); + break; + + case OpOuterProduct: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2]; + uint32_t b = ops[3]; + + auto &type = get(result_type); + string expr = type_to_glsl_constructor(type); + expr += "("; + for (uint32_t col = 0; col < type.columns; col++) + { + expr += to_enclosed_unpacked_expression(a); + expr += " * "; + expr += to_extract_component_expression(b, col); + if (col + 1 < type.columns) + expr += ", "; + } + expr += ")"; + emit_op(result_type, id, expr, should_forward(a) && should_forward(b)); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + case OpVectorTimesMatrix: + case OpMatrixTimesVector: + { + if (!msl_options.invariant_float_math && !has_legacy_nocontract(ops[0], ops[1])) + { + CompilerGLSL::emit_instruction(instruction); + break; + } + + // If the matrix needs transpose, just flip the multiply order. + auto *e = maybe_get(ops[opcode == OpMatrixTimesVector ? 2 : 3]); + if (e && e->need_transpose) + { + e->need_transpose = false; + string expr; + + if (opcode == OpMatrixTimesVector) + { + expr = join("spvFMulVectorMatrix(", to_enclosed_unpacked_expression(ops[3]), ", ", + to_unpacked_row_major_matrix_expression(ops[2]), ")"); + } + else + { + expr = join("spvFMulMatrixVector(", to_unpacked_row_major_matrix_expression(ops[3]), ", ", + to_enclosed_unpacked_expression(ops[2]), ")"); + } + + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + emit_op(ops[0], ops[1], expr, forward); + e->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + { + if (opcode == OpMatrixTimesVector) + MSL_BFOP(spvFMulMatrixVector); + else + MSL_BFOP(spvFMulVectorMatrix); + } + break; + } + + case OpMatrixTimesMatrix: + { + if (!msl_options.invariant_float_math && !has_legacy_nocontract(ops[0], ops[1])) + { + CompilerGLSL::emit_instruction(instruction); + break; + } + + auto *a = maybe_get(ops[2]); + auto *b = maybe_get(ops[3]); + + // If both matrices need transpose, we can multiply in flipped order and tag the expression as transposed. + // a^T * b^T = (b * a)^T. + if (a && b && a->need_transpose && b->need_transpose) + { + a->need_transpose = false; + b->need_transpose = false; + + auto expr = + join("spvFMulMatrixMatrix(", enclose_expression(to_unpacked_row_major_matrix_expression(ops[3])), ", ", + enclose_expression(to_unpacked_row_major_matrix_expression(ops[2])), ")"); + + bool forward = should_forward(ops[2]) && should_forward(ops[3]); + emit_transposed_op(ops[0], ops[1], expr, forward); + a->need_transpose = true; + b->need_transpose = true; + inherit_expression_dependencies(ops[1], ops[2]); + inherit_expression_dependencies(ops[1], ops[3]); + } + else + MSL_BFOP(spvFMulMatrixMatrix); + + break; + } + + case OpIAddCarry: + case OpISubBorrow: + { + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + emit_uninitialized_temporary_expression(result_type, result_id); + + auto &res_type = get(type.member_types[1]); + if (opcode == OpIAddCarry) + { + statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", + to_enclosed_unpacked_expression(op0), " + ", to_enclosed_unpacked_expression(op1), ";"); + statement(to_expression(result_id), ".", to_member_name(type, 1), " = select(", type_to_glsl(res_type), + "(1), ", type_to_glsl(res_type), "(0), ", to_unpacked_expression(result_id), ".", to_member_name(type, 0), + " >= max(", to_unpacked_expression(op0), ", ", to_unpacked_expression(op1), "));"); + } + else + { + statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", to_enclosed_unpacked_expression(op0), " - ", + to_enclosed_unpacked_expression(op1), ";"); + statement(to_expression(result_id), ".", to_member_name(type, 1), " = select(", type_to_glsl(res_type), + "(1), ", type_to_glsl(res_type), "(0), ", to_enclosed_unpacked_expression(op0), + " >= ", to_enclosed_unpacked_expression(op1), ");"); + } + break; + } + + case OpUMulExtended: + case OpSMulExtended: + { + uint32_t result_type = ops[0]; + uint32_t result_id = ops[1]; + uint32_t op0 = ops[2]; + uint32_t op1 = ops[3]; + auto &type = get(result_type); + auto &op_type = get(type.member_types[0]); + auto input_type = opcode == OpSMulExtended ? int_type : uint_type; + string cast_op0, cast_op1; + + binary_op_bitcast_helper(cast_op0, cast_op1, input_type, op0, op1, false); + auto expr = join("spvMulExtended<", type_to_glsl(type), ", ", type_to_glsl(op_type), ">(", cast_op0, ", ", cast_op1, ")"); + emit_op(result_type, result_id, expr, true); + break; + } + + case OpArrayLength: + { + auto &type = expression_type(ops[2]); + uint32_t offset = type_struct_member_offset(type, ops[3]); + uint32_t stride = type_struct_member_array_stride(type, ops[3]); + + auto expr = join("(", to_buffer_size_expression(ops[2]), " - ", offset, ") / ", stride); + emit_op(ops[0], ops[1], expr, true); + break; + } + + // Legacy sub-group stuff ... + case OpSubgroupBallotKHR: + case OpSubgroupFirstInvocationKHR: + case OpSubgroupReadInvocationKHR: + case OpSubgroupAllKHR: + case OpSubgroupAnyKHR: + case OpSubgroupAllEqualKHR: + emit_subgroup_op(instruction); + break; + + // SPV_INTEL_shader_integer_functions2 + case OpUCountLeadingZerosINTEL: + MSL_UFOP(clz); + break; + + case OpUCountTrailingZerosINTEL: + MSL_UFOP(ctz); + break; + + case OpAbsISubINTEL: + case OpAbsUSubINTEL: + MSL_BFOP(absdiff); + break; + + case OpIAddSatINTEL: + case OpUAddSatINTEL: + MSL_BFOP(addsat); + break; + + case OpIAverageINTEL: + case OpUAverageINTEL: + MSL_BFOP(hadd); + break; + + case OpIAverageRoundedINTEL: + case OpUAverageRoundedINTEL: + MSL_BFOP(rhadd); + break; + + case OpISubSatINTEL: + case OpUSubSatINTEL: + MSL_BFOP(subsat); + break; + + case OpIMul32x16INTEL: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2], b = ops[3]; + bool forward = should_forward(a) && should_forward(b); + emit_op(result_type, id, join("int(short(", to_unpacked_expression(a), ")) * int(short(", to_unpacked_expression(b), "))"), forward); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + case OpUMul32x16INTEL: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t a = ops[2], b = ops[3]; + bool forward = should_forward(a) && should_forward(b); + emit_op(result_type, id, join("uint(ushort(", to_unpacked_expression(a), ")) * uint(ushort(", to_unpacked_expression(b), "))"), forward); + inherit_expression_dependencies(id, a); + inherit_expression_dependencies(id, b); + break; + } + + // SPV_EXT_demote_to_helper_invocation + case OpDemoteToHelperInvocationEXT: + if (!msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("discard_fragment() does not formally have demote semantics until MSL 2.3."); + CompilerGLSL::emit_instruction(instruction); + break; + + case OpIsHelperInvocationEXT: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires MSL 2.3 on iOS."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires MSL 2.1 on macOS."); + emit_op(ops[0], ops[1], + needs_manual_helper_invocation_updates() ? builtin_to_glsl(BuiltInHelperInvocation, StorageClassInput) : + "simd_is_helper_thread()", + false); + break; + + case OpBeginInvocationInterlockEXT: + case OpEndInvocationInterlockEXT: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("Raster order groups require MSL 2.0."); + break; // Nothing to do in the body + + case OpConvertUToAccelerationStructureKHR: + SPIRV_CROSS_THROW("ConvertUToAccelerationStructure is not supported in MSL."); + case OpRayQueryGetIntersectionInstanceShaderBindingTableRecordOffsetKHR: + SPIRV_CROSS_THROW("BindingTableRecordOffset is not supported in MSL."); + + case OpRayQueryInitializeKHR: + { + flush_variable_declaration(ops[0]); + register_write(ops[0]); + add_spv_func_and_recompile(SPVFuncImplRayQueryIntersectionParams); + + statement(to_expression(ops[0]), ".reset(", "ray(", to_expression(ops[4]), ", ", to_expression(ops[6]), ", ", + to_expression(ops[5]), ", ", to_expression(ops[7]), "), ", to_expression(ops[1]), ", ", to_expression(ops[3]), + ", spvMakeIntersectionParams(", to_expression(ops[2]), "));"); + break; + } + case OpRayQueryProceedKHR: + { + flush_variable_declaration(ops[0]); + register_write(ops[2]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".next()"), false); + break; + } +#define MSL_RAY_QUERY_IS_CANDIDATE get(ops[3]).scalar_i32() == 0 + +#define MSL_RAY_QUERY_GET_OP(op, msl_op) \ + case OpRayQueryGet##op##KHR: \ + flush_variable_declaration(ops[2]); \ + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".get_" #msl_op "()"), false); \ + break + +#define MSL_RAY_QUERY_OP_INNER2(op, msl_prefix, msl_op) \ + case OpRayQueryGet##op##KHR: \ + flush_variable_declaration(ops[2]); \ + if (MSL_RAY_QUERY_IS_CANDIDATE) \ + emit_op(ops[0], ops[1], join(to_expression(ops[2]), #msl_prefix "_candidate_" #msl_op "()"), false); \ + else \ + emit_op(ops[0], ops[1], join(to_expression(ops[2]), #msl_prefix "_committed_" #msl_op "()"), false); \ + break + +#define MSL_RAY_QUERY_GET_OP2(op, msl_op) MSL_RAY_QUERY_OP_INNER2(op, .get, msl_op) +#define MSL_RAY_QUERY_IS_OP2(op, msl_op) MSL_RAY_QUERY_OP_INNER2(op, .is, msl_op) + + MSL_RAY_QUERY_GET_OP(RayTMin, ray_min_distance); + MSL_RAY_QUERY_GET_OP(WorldRayOrigin, world_space_ray_origin); + MSL_RAY_QUERY_GET_OP(WorldRayDirection, world_space_ray_direction); + MSL_RAY_QUERY_GET_OP2(IntersectionInstanceId, instance_id); + MSL_RAY_QUERY_GET_OP2(IntersectionInstanceCustomIndex, user_instance_id); + MSL_RAY_QUERY_GET_OP2(IntersectionBarycentrics, triangle_barycentric_coord); + MSL_RAY_QUERY_GET_OP2(IntersectionPrimitiveIndex, primitive_id); + MSL_RAY_QUERY_GET_OP2(IntersectionGeometryIndex, geometry_id); + MSL_RAY_QUERY_GET_OP2(IntersectionObjectRayOrigin, ray_origin); + MSL_RAY_QUERY_GET_OP2(IntersectionObjectRayDirection, ray_direction); + MSL_RAY_QUERY_GET_OP2(IntersectionObjectToWorld, object_to_world_transform); + MSL_RAY_QUERY_GET_OP2(IntersectionWorldToObject, world_to_object_transform); + MSL_RAY_QUERY_IS_OP2(IntersectionFrontFace, triangle_front_facing); + + case OpRayQueryGetIntersectionTypeKHR: + flush_variable_declaration(ops[2]); + if (MSL_RAY_QUERY_IS_CANDIDATE) + emit_op(ops[0], ops[1], join("uint(", to_expression(ops[2]), ".get_candidate_intersection_type()) - 1"), + false); + else + emit_op(ops[0], ops[1], join("uint(", to_expression(ops[2]), ".get_committed_intersection_type())"), false); + break; + case OpRayQueryGetIntersectionTKHR: + flush_variable_declaration(ops[2]); + if (MSL_RAY_QUERY_IS_CANDIDATE) + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".get_candidate_triangle_distance()"), false); + else + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".get_committed_distance()"), false); + break; + case OpRayQueryGetIntersectionCandidateAABBOpaqueKHR: + { + flush_variable_declaration(ops[0]); + emit_op(ops[0], ops[1], join(to_expression(ops[2]), ".is_candidate_non_opaque_bounding_box()"), false); + break; + } + case OpRayQueryConfirmIntersectionKHR: + flush_variable_declaration(ops[0]); + register_write(ops[0]); + statement(to_expression(ops[0]), ".commit_triangle_intersection();"); + break; + case OpRayQueryGenerateIntersectionKHR: + flush_variable_declaration(ops[0]); + register_write(ops[0]); + statement(to_expression(ops[0]), ".commit_bounding_box_intersection(", to_expression(ops[1]), ");"); + break; + case OpRayQueryTerminateKHR: + flush_variable_declaration(ops[0]); + register_write(ops[0]); + statement(to_expression(ops[0]), ".abort();"); + break; +#undef MSL_RAY_QUERY_GET_OP +#undef MSL_RAY_QUERY_IS_CANDIDATE +#undef MSL_RAY_QUERY_IS_OP2 +#undef MSL_RAY_QUERY_GET_OP2 +#undef MSL_RAY_QUERY_OP_INNER2 + + case OpConvertPtrToU: + case OpConvertUToPtr: + case OpBitcast: + { + auto &type = get(ops[0]); + auto &input_type = expression_type(ops[2]); + + if (opcode != OpBitcast || is_pointer(type) || is_pointer(input_type)) + { + string op; + auto input_expr = to_unpacked_expression(ops[2]); + + if ((type.vecsize == 1 || is_pointer(type)) && (input_type.vecsize == 1 || is_pointer(input_type))) + op = join("reinterpret_cast<", type_to_glsl(type), ">(", input_expr, ")"); + else if (input_type.vecsize == 2) + op = join("reinterpret_cast<", type_to_glsl(type), ">(as_type(", input_expr, "))"); + else + op = join("as_type<", type_to_glsl(type), ">(reinterpret_cast(", input_expr, "))"); + + auto &expr = emit_op(ops[0], ops[1], op, should_forward(ops[2])); + if (is_pointer(type)) + { + if (auto *backing_var = maybe_get_backing_variable(ops[2])) + expr.loaded_from = backing_var->self; + else + expr.loaded_from = ID(ops[2]); + } + inherit_expression_dependencies(ops[1], ops[2]); + } + else + CompilerGLSL::emit_instruction(instruction); + + break; + } + + case OpSDot: + case OpUDot: + case OpSUDot: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t vec1 = ops[2]; + uint32_t vec2 = ops[3]; + + auto &input_type1 = expression_type(vec1); + auto &input_type2 = expression_type(vec2); + + string vec1input, vec2input; + auto input_size = input_type1.vecsize; + if (instruction.length == 5) + { + if (ops[4] == PackedVectorFormatPackedVectorFormat4x8Bit) + { + string type = opcode == OpSDot || opcode == OpSUDot ? "char4" : "uchar4"; + vec1input = join("as_type<", type, ">(", to_expression(vec1), ")"); + type = opcode == OpSDot ? "char4" : "uchar4"; + vec2input = join("as_type<", type, ">(", to_expression(vec2), ")"); + input_size = 4; + } + else + SPIRV_CROSS_THROW("Packed vector formats other than 4x8Bit for integer dot product is not supported."); + } + else + { + // Inputs are sign or zero-extended to their target width. + SPIRType::BaseType vec1_expected_type = + opcode != OpUDot ? + to_signed_basetype(input_type1.width) : + to_unsigned_basetype(input_type1.width); + + SPIRType::BaseType vec2_expected_type = + opcode != OpSDot ? + to_unsigned_basetype(input_type2.width) : + to_signed_basetype(input_type2.width); + + vec1input = bitcast_expression(vec1_expected_type, vec1); + vec2input = bitcast_expression(vec2_expected_type, vec2); + } + + auto &type = get(result_type); + + // We'll get the appropriate sign-extend or zero-extend, no matter which type we cast to here. + // The addition in reduce_add is sign-invariant. + auto result_type_cast = join(type_to_glsl(type), input_size); + + string exp = join("reduce_add(", + result_type_cast, "(", vec1input, ") * ", + result_type_cast, "(", vec2input, "))"); + + emit_op(result_type, id, exp, should_forward(vec1) && should_forward(vec2)); + inherit_expression_dependencies(id, vec1); + inherit_expression_dependencies(id, vec2); + break; + } + + case OpSDotAccSat: + case OpUDotAccSat: + case OpSUDotAccSat: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t vec1 = ops[2]; + uint32_t vec2 = ops[3]; + uint32_t acc = ops[4]; + + auto input_type1 = expression_type(vec1); + auto input_type2 = expression_type(vec2); + + string vec1input, vec2input; + if (instruction.length == 6) + { + if (ops[5] == PackedVectorFormatPackedVectorFormat4x8Bit) + { + string type = opcode == OpSDotAccSat || opcode == OpSUDotAccSat ? "char4" : "uchar4"; + vec1input = join("as_type<", type, ">(", to_expression(vec1), ")"); + type = opcode == OpSDotAccSat ? "char4" : "uchar4"; + vec2input = join("as_type<", type, ">(", to_expression(vec2), ")"); + input_type1.vecsize = 4; + input_type2.vecsize = 4; + } + else + SPIRV_CROSS_THROW("Packed vector formats other than 4x8Bit for integer dot product is not supported."); + } + else + { + // Inputs are sign or zero-extended to their target width. + SPIRType::BaseType vec1_expected_type = + opcode != OpUDotAccSat ? + to_signed_basetype(input_type1.width) : + to_unsigned_basetype(input_type1.width); + + SPIRType::BaseType vec2_expected_type = + opcode != OpSDotAccSat ? + to_unsigned_basetype(input_type2.width) : + to_signed_basetype(input_type2.width); + + vec1input = bitcast_expression(vec1_expected_type, vec1); + vec2input = bitcast_expression(vec2_expected_type, vec2); + } + + auto &type = get(result_type); + + SPIRType::BaseType pre_saturate_type = + opcode != OpUDotAccSat ? + to_signed_basetype(type.width) : + to_unsigned_basetype(type.width); + + input_type1.basetype = pre_saturate_type; + input_type2.basetype = pre_saturate_type; + + string exp = join(type_to_glsl(type), "(addsat(reduce_add(", + type_to_glsl(input_type1), "(", vec1input, ") * ", + type_to_glsl(input_type2), "(", vec2input, ")), ", + bitcast_expression(pre_saturate_type, acc), "))"); + + emit_op(result_type, id, exp, should_forward(vec1) && should_forward(vec2)); + inherit_expression_dependencies(id, vec1); + inherit_expression_dependencies(id, vec2); + break; + } + + case OpSetMeshOutputsEXT: + { + flush_variable_declaration(builtin_mesh_primitive_indices_id); + add_spv_func_and_recompile(SPVFuncImplSetMeshOutputsEXT); + statement("spvSetMeshOutputsEXT(gl_LocalInvocationIndex, spvMeshSizes, ", to_unpacked_expression(ops[0]), ", ", to_unpacked_expression(ops[1]), ");"); + break; + } + + case OpAssumeTrueKHR: + { + auto condition = ops[0]; + statement(join("SPV_ASSUME(", to_unpacked_expression(condition), ")")); + break; + } + + case OpExpectKHR: + { + auto result_type = ops[0]; + auto ret = ops[1]; + auto value = ops[2]; + auto exp_value = ops[3]; + + auto exp = join("SPV_EXPECT(", to_unpacked_expression(value), ", ", to_unpacked_expression(exp_value), ")"); + emit_op(result_type, ret, exp, should_forward(value), should_forward(exp_value)); + inherit_expression_dependencies(ret, value); + inherit_expression_dependencies(ret, exp_value); + break; + } + + case OpCooperativeMatrixLoadKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t ptr = ops[2]; + uint32_t layout = ops[3]; + + auto &layout_c = get(layout); + if (layout_c.specialization) + SPIRV_CROSS_THROW("MSL cooperative matrix load does not support spec-constant layout."); + uint32_t layout_val = layout_c.scalar(); + bool col_major = false; + + switch (layout_val) + { + case CooperativeMatrixLayoutRowMajorKHR: + case CooperativeMatrixLayoutColumnMajorKHR: + if (instruction.length < 5) + SPIRV_CROSS_THROW("MSL cooperative matrix load requires Stride for row/column-major layouts."); + col_major = (layout_val == CooperativeMatrixLayoutColumnMajorKHR); + break; + + default: + SPIRV_CROSS_THROW("MSL cooperative matrix load only supports RowMajorKHR and ColumnMajorKHR layouts."); + } + + uint32_t stride = ops[4]; + + emit_uninitialized_temporary_expression(result_type, id); + + auto ptr_expr = to_ptr_expression(ptr); + string stride_expr = to_expression(stride); + + // The pointer operand is allowed to use a different element type than the cooperative matrix component type. + // In that case, cast the pointer and convert the stride from source element units to component element units. + auto &mat_type = get(result_type); + auto &component_type = get(mat_type.parent_type); + auto &ptr_type = expression_type(ptr); + auto &pointee_type = get(ptr_type.parent_type); + if (pointee_type.self != component_type.self) + { + auto addr_space = get_type_address_space(ptr_type, ptr); + ptr_expr = join("reinterpret_cast<", addr_space, " ", type_to_glsl(component_type), "*>(", ptr_expr, ")"); + + uint32_t src_bytes = (pointee_type.width * pointee_type.vecsize) / 8; + uint32_t dst_bytes = (component_type.width * component_type.vecsize) / 8; + if (src_bytes == 0 || dst_bytes == 0) + SPIRV_CROSS_THROW("Cannot determine element size for cooperative matrix load/store."); + + if (src_bytes == dst_bytes) + { + // No conversion needed. + } + else if (src_bytes > dst_bytes && (src_bytes % dst_bytes) == 0) + { + uint32_t multiplier = src_bytes / dst_bytes; + stride_expr = join("(", stride_expr, ") * ", multiplier, "u"); + } + else if (src_bytes < dst_bytes && (dst_bytes % src_bytes) == 0) + { + uint32_t divisor = dst_bytes / src_bytes; + stride_expr = join("(", stride_expr, ") / ", divisor, "u"); + } + else + { + stride_expr = join("((", stride_expr, ") * ", src_bytes, "u) / ", dst_bytes, "u"); + } + } + + if (col_major) + statement("simdgroup_load(", to_expression(id), ", ", + ptr_expr, ", ", stride_expr, ", ulong2(0), true);"); + else + statement("simdgroup_load(", to_expression(id), ", ", + ptr_expr, ", ", stride_expr, ");"); + + register_read(id, ptr, false); + break; + } + + case OpCooperativeMatrixStoreKHR: + { + uint32_t ptr = ops[0]; + uint32_t obj = ops[1]; + uint32_t layout = ops[2]; + + auto &layout_c = get(layout); + if (layout_c.specialization) + SPIRV_CROSS_THROW("MSL cooperative matrix store does not support spec-constant layout."); + uint32_t layout_val = layout_c.scalar(); + bool col_major = false; + + switch (layout_val) + { + case CooperativeMatrixLayoutRowMajorKHR: + case CooperativeMatrixLayoutColumnMajorKHR: + if (instruction.length < 4) + SPIRV_CROSS_THROW("MSL cooperative matrix store requires Stride for row/column-major layouts."); + col_major = (layout_val == CooperativeMatrixLayoutColumnMajorKHR); + break; + + default: + SPIRV_CROSS_THROW("MSL cooperative matrix store only supports RowMajorKHR and ColumnMajorKHR layouts."); + } + + uint32_t stride = ops[3]; + + auto ptr_expr = to_ptr_expression(ptr); + string stride_expr = to_expression(stride); + + // The pointer operand is allowed to use a different element type than the cooperative matrix component type. + // In that case, cast the pointer and convert the stride from source element units to component element units. + auto &mat_type = expression_type(obj); + auto &component_type = get(mat_type.parent_type); + auto &ptr_type = expression_type(ptr); + auto &pointee_type = get(ptr_type.parent_type); + if (pointee_type.self != component_type.self) + { + auto addr_space = get_type_address_space(ptr_type, ptr); + ptr_expr = join("reinterpret_cast<", addr_space, " ", type_to_glsl(component_type), "*>(", ptr_expr, ")"); + + uint32_t src_bytes = (pointee_type.width * pointee_type.vecsize) / 8; + uint32_t dst_bytes = (component_type.width * component_type.vecsize) / 8; + if (src_bytes == 0 || dst_bytes == 0) + SPIRV_CROSS_THROW("Cannot determine element size for cooperative matrix load/store."); + + if (src_bytes == dst_bytes) + { + // No conversion needed. + } + else if (src_bytes > dst_bytes && (src_bytes % dst_bytes) == 0) + { + uint32_t multiplier = src_bytes / dst_bytes; + stride_expr = join("(", stride_expr, ") * ", multiplier, "u"); + } + else if (src_bytes < dst_bytes && (dst_bytes % src_bytes) == 0) + { + uint32_t divisor = dst_bytes / src_bytes; + stride_expr = join("(", stride_expr, ") / ", divisor, "u"); + } + else + { + stride_expr = join("((", stride_expr, ") * ", src_bytes, "u) / ", dst_bytes, "u"); + } + } + + if (col_major) + statement("simdgroup_store(", to_expression(obj), ", ", + ptr_expr, ", ", stride_expr, ", ulong2(0), true);"); + else + statement("simdgroup_store(", to_expression(obj), ", ", + ptr_expr, ", ", stride_expr, ");"); + + register_write(ptr); + break; + } + + case OpCooperativeMatrixMulAddKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + uint32_t A = ops[2], B = ops[3], C = ops[4]; + uint32_t matrix_operands = instruction.length >= 6 ? ops[5] : uint32_t(CooperativeMatrixOperandsMaskNone); + + if (matrix_operands != uint32_t(CooperativeMatrixOperandsMaskNone)) + SPIRV_CROSS_THROW("MSL cooperative matrix muladd does not support setting matrix operands flags."); + + emit_uninitialized_temporary_expression(result_type, id); + statement("simdgroup_multiply_accumulate(", to_expression(id), ", ", + to_unpacked_expression(A), ", ", + to_unpacked_expression(B), ", ", + to_unpacked_expression(C), ");"); + + inherit_expression_dependencies(id, A); + inherit_expression_dependencies(id, B); + inherit_expression_dependencies(id, C); + break; + } + + case OpCooperativeMatrixLengthKHR: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto &coop_type = get(ops[2]); + + if (coop_type.op != OpTypeCooperativeMatrixKHR) + SPIRV_CROSS_THROW("OpCooperativeMatrixLengthKHR requires cooperative matrix type."); + + auto &component_type = get(coop_type.parent_type); + auto coop_type_name = type_to_glsl(coop_type); + auto component_type_name = type_to_glsl(component_type); + + auto expr = join(type_to_glsl(get(result_type)), + "(sizeof(", coop_type_name, "::storage_type) / sizeof(", component_type_name, "))"); + emit_op(result_type, id, expr, true); + break; + } + + default: + { + // Prevent GLSL cooperative matrix code from leaking into MSL output. + // Element-wise arithmetic on cooperative matrices is not supported in Metal. + // Should cover any reasonable situation we come across. + if (instruction.length >= 2) + { + bool has_result = false, has_result_type = false; + HasResultAndType(opcode, &has_result, &has_result_type); + + if (has_result_type) + { + auto *type = &get(ops[0]); + while (type && (is_pointer(*type) || is_array(*type))) + type = this->maybe_get(type->parent_type); + if (type->op == OpTypeCooperativeMatrixKHR) + SPIRV_CROSS_THROW("Unsupported operation on cooperative matrix in MSL backend."); + } + + auto is_cooperative_matrix_typed_id = [&](uint32_t id) -> bool { + auto &type = expression_type(id); + return type.op == OpTypeCooperativeMatrixKHR; + }; + + if (opcode == OpCompositeExtract || opcode == OpVectorExtractDynamic) + { + if (instruction.length >= 3 && is_cooperative_matrix_typed_id(ops[2])) + SPIRV_CROSS_THROW("Unsupported extraction from cooperative matrix in MSL backend."); + } + else if (opcode == OpCompositeInsert || opcode == OpVectorInsertDynamic) + { + if ((instruction.length >= 3 && is_cooperative_matrix_typed_id(ops[2])) || + (instruction.length >= 4 && is_cooperative_matrix_typed_id(ops[3]))) + SPIRV_CROSS_THROW("Unsupported operation on cooperative matrix in MSL backend."); + } + } + CompilerGLSL::emit_instruction(instruction); + break; + } + } + + previous_instruction_opcode = opcode; +} + +void CompilerMSL::emit_texture_op(const Instruction &i, bool sparse) +{ + if (sparse) + SPIRV_CROSS_THROW("Sparse feedback not yet supported in MSL."); + + if (msl_options.use_framebuffer_fetch_subpasses) + { + auto *ops = stream(i); + + uint32_t result_type_id = ops[0]; + uint32_t id = ops[1]; + uint32_t img = ops[2]; + + auto &type = expression_type(img); + auto &imgtype = get(type.self); + + // Use Metal's native frame-buffer fetch API for subpass inputs. + if (imgtype.image.dim == DimSubpassData) + { + // Subpass inputs cannot be invalidated, + // so just forward the expression directly. + string expr = to_expression(img); + emit_op(result_type_id, id, expr, true); + return; + } + } + + // Fallback to default implementation + CompilerGLSL::emit_texture_op(i, sparse); +} + +void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem) +{ + auto model = get_execution_model(); + + if (model != ExecutionModelGLCompute && model != ExecutionModelTaskEXT && + model != ExecutionModelMeshEXT && !is_tesc_shader()) + { + return; + } + + uint32_t exe_scope = id_exe_scope ? evaluate_constant_u32(id_exe_scope) : uint32_t(ScopeInvocation); + uint32_t mem_scope = id_mem_scope ? evaluate_constant_u32(id_mem_scope) : uint32_t(ScopeInvocation); + // Use the wider of the two scopes (smaller value) + exe_scope = min(exe_scope, mem_scope); + + if (msl_options.emulate_subgroups && exe_scope >= ScopeSubgroup && !id_mem_sem) + // In this case, we assume a "subgroup" size of 1. The barrier, then, is a noop. + return; + + string bar_stmt; + + if (!id_exe_scope && msl_options.supports_msl_version(3, 2)) + { + // Just took 10 years to get a proper barrier, but hey! + bar_stmt = "atomic_thread_fence"; + } + else + { + if ((msl_options.is_ios() && msl_options.supports_msl_version(1, 2)) || msl_options.supports_msl_version(2)) + bar_stmt = exe_scope < ScopeSubgroup ? "threadgroup_barrier" : "simdgroup_barrier"; + else + bar_stmt = "threadgroup_barrier"; + } + + bar_stmt += "("; + + uint32_t mem_sem = id_mem_sem ? evaluate_constant_u32(id_mem_sem) : uint32_t(MemorySemanticsMaskNone); + + // Use the | operator to combine flags if we can. + if (msl_options.supports_msl_version(1, 2)) + { + string mem_flags; + + // For tesc shaders, this also affects objects in the Output storage class. + // Since in Metal, these are placed in a device buffer, we have to sync device memory here. + if (is_tesc_shader() || + (mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask))) + mem_flags += "mem_flags::mem_device"; + + // Fix tessellation patch function processing + if (is_tesc_shader() || (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask))) + { + if (!mem_flags.empty()) + mem_flags += " | "; + mem_flags += "mem_flags::mem_threadgroup"; + } + if (mem_sem & MemorySemanticsImageMemoryMask) + { + if (!mem_flags.empty()) + mem_flags += " | "; + mem_flags += "mem_flags::mem_texture"; + } + + if (mem_flags.empty()) + mem_flags = "mem_flags::mem_none"; + + bar_stmt += mem_flags; + } + else + { + if ((mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask)) && + (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask))) + bar_stmt += "mem_flags::mem_device_and_threadgroup"; + else if (mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask)) + bar_stmt += "mem_flags::mem_device"; + else if (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask)) + bar_stmt += "mem_flags::mem_threadgroup"; + else if (mem_sem & MemorySemanticsImageMemoryMask) + bar_stmt += "mem_flags::mem_texture"; + else + bar_stmt += "mem_flags::mem_none"; + } + + if (!id_exe_scope && msl_options.supports_msl_version(3, 2)) + { + // If there's no device-related memory in the barrier, demote to workgroup scope. + // glslang seems to emit device scope even for memoryBarrierShared(). + if (mem_scope == ScopeDevice && + (mem_sem & (MemorySemanticsUniformMemoryMask | + MemorySemanticsImageMemoryMask | + MemorySemanticsCrossWorkgroupMemoryMask)) == 0) + { + mem_scope = ScopeWorkgroup; + } + + // MSL 3.2 only supports seq_cst or relaxed. + if (mem_sem & (MemorySemanticsAcquireReleaseMask | + MemorySemanticsAcquireMask | + MemorySemanticsReleaseMask | + MemorySemanticsSequentiallyConsistentMask)) + { + bar_stmt += ", memory_order_seq_cst"; + } + else + { + bar_stmt += ", memory_order_relaxed"; + } + + switch (mem_scope) + { + case ScopeDevice: + bar_stmt += ", thread_scope_device"; + break; + + case ScopeWorkgroup: + bar_stmt += ", thread_scope_threadgroup"; + break; + + case ScopeSubgroup: + bar_stmt += ", thread_scope_simdgroup"; + break; + + case ScopeInvocation: + bar_stmt += ", thread_scope_thread"; + break; + + default: + // The default argument is device, which is conservative. + break; + } + } + + bar_stmt += ");"; + + statement(bar_stmt); + + assert(current_emitting_block); + flush_control_dependent_expressions(current_emitting_block->self); + flush_all_active_variables(); +} + +static bool storage_class_array_is_thread(StorageClass storage) +{ + switch (storage) + { + case StorageClassInput: + case StorageClassOutput: + case StorageClassGeneric: + case StorageClassFunction: + case StorageClassPrivate: + return true; + + default: + return false; + } +} + +bool CompilerMSL::emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, + StorageClass lhs_storage, StorageClass rhs_storage) +{ + // Allow Metal to use the array template to make arrays a value type. + // This, however, cannot be used for threadgroup address specifiers, so consider the custom array copy as fallback. + bool lhs_is_thread_storage = storage_class_array_is_thread(lhs_storage); + bool rhs_is_thread_storage = storage_class_array_is_thread(rhs_storage); + + bool lhs_is_array_template = lhs_is_thread_storage || lhs_storage == StorageClassWorkgroup; + bool rhs_is_array_template = rhs_is_thread_storage || rhs_storage == StorageClassWorkgroup; + + // Special considerations for stage IO variables. + // If the variable is actually backed by non-user visible device storage, we use array templates for those. + // + // Another special consideration is given to thread local variables which happen to have Offset decorations + // applied to them. Block-like types do not use array templates, so we need to force POD path if we detect + // these scenarios. This check isn't perfect since it would be technically possible to mix and match these things, + // and for a fully correct solution we might have to track array template state through access chains as well, + // but for all reasonable use cases, this should suffice. + // This special case should also only apply to Function/Private storage classes. + // We should not check backing variable for temporaries. + auto *lhs_var = maybe_get_backing_variable(lhs_id); + if (lhs_var && lhs_storage == StorageClassStorageBuffer && storage_class_array_is_thread(lhs_var->storage)) + lhs_is_array_template = true; + else if (lhs_var && lhs_storage != StorageClassGeneric && type_is_explicit_layout(get(lhs_var->basetype))) + lhs_is_array_template = false; + + auto *rhs_var = maybe_get_backing_variable(rhs_id); + if (rhs_var && rhs_storage == StorageClassStorageBuffer && storage_class_array_is_thread(rhs_var->storage)) + rhs_is_array_template = true; + else if (rhs_var && rhs_storage != StorageClassGeneric && type_is_explicit_layout(get(rhs_var->basetype))) + rhs_is_array_template = false; + + // Special consideration for clip/culldistance. Normally composites are lowered, but clip/cull is special for reasons ... + if (lhs_var && lhs_storage == StorageClassOutput && is_builtin_variable(*lhs_var)) + lhs_is_array_template = false; + if (rhs_var && rhs_storage == StorageClassOutput && is_builtin_variable(*rhs_var)) + rhs_is_array_template = false; + + // If threadgroup storage qualifiers are *not* used: + // Avoid spvCopy* wrapper functions; Otherwise, spvUnsafeArray<> template cannot be used with that storage qualifier. + if (lhs_is_array_template && rhs_is_array_template && !using_builtin_array()) + { + // Fall back to normal copy path. + return false; + } + else + { + // Ensure the LHS variable has been declared + if (lhs_var) + flush_variable_declaration(lhs_var->self); + + string lhs; + if (expr) + lhs = expr; + else + lhs = to_expression(lhs_id); + + // Assignment from an array initializer is fine. + auto &type = expression_type(rhs_id); + auto *var = maybe_get_backing_variable(rhs_id); + + // Unfortunately, we cannot template on address space in MSL, + // so explicit address space redirection it is ... + bool is_constant = false; + if (ir.ids[rhs_id].get_type() == TypeConstant) + { + is_constant = true; + } + else if (var && var->remapped_variable && var->statically_assigned && + ir.ids[var->static_expression].get_type() == TypeConstant) + { + is_constant = true; + } + else if (rhs_storage == StorageClassUniform || rhs_storage == StorageClassUniformConstant) + { + is_constant = true; + } + + // For the case where we have OpLoad triggering an array copy, + // we cannot easily detect this case ahead of time since it's + // context dependent. We might have to force a recompile here + // if this is the only use of array copies in our shader. + add_spv_func_and_recompile(type.array.size() > 1 ? SPVFuncImplArrayCopyMultidim : SPVFuncImplArrayCopy); + + const char *tag = nullptr; + if (lhs_is_thread_storage && is_constant) + tag = "FromConstantToStack"; + else if (lhs_storage == StorageClassWorkgroup && is_constant) + tag = "FromConstantToThreadGroup"; + else if (lhs_is_thread_storage && rhs_is_thread_storage) + tag = "FromStackToStack"; + else if (lhs_storage == StorageClassWorkgroup && rhs_is_thread_storage) + tag = "FromStackToThreadGroup"; + else if (lhs_is_thread_storage && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToStack"; + else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToThreadGroup"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && is_constant) + tag = "FromConstantToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_storage == StorageClassWorkgroup) + tag = "FromThreadGroupToDevice"; + else if (lhs_storage == StorageClassStorageBuffer && rhs_is_thread_storage) + tag = "FromStackToDevice"; + else if (lhs_storage == StorageClassWorkgroup && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToThreadGroup"; + else if (lhs_is_thread_storage && rhs_storage == StorageClassStorageBuffer) + tag = "FromDeviceToStack"; + else + SPIRV_CROSS_THROW("Unknown storage class used for copying arrays."); + + // Should be very rare, but mark if we need extra magic template overloads. + if (has_extended_decoration(lhs_id, SPIRVCrossDecorationPhysicalTypeID)) + add_spv_func_and_recompile(SPVFuncImplArrayCopyExtendedDst); + if (has_extended_decoration(rhs_id, SPIRVCrossDecorationPhysicalTypeID)) + add_spv_func_and_recompile(SPVFuncImplArrayCopyExtendedSrc); + + // Pass internal array of spvUnsafeArray<> into wrapper functions + if (lhs_is_array_template && rhs_is_array_template && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, "(", lhs, ".elements, ", to_expression(rhs_id), ".elements);"); + if (lhs_is_array_template && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, "(", lhs, ".elements, ", to_expression(rhs_id), ");"); + else if (rhs_is_array_template && !msl_options.force_native_arrays) + statement("spvArrayCopy", tag, "(", lhs, ", ", to_expression(rhs_id), ".elements);"); + else + statement("spvArrayCopy", tag, "(", lhs, ", ", to_expression(rhs_id), ");"); + } + + return true; +} + +uint32_t CompilerMSL::get_physical_tess_level_array_size(BuiltIn builtin) const +{ + if (is_tessellating_triangles()) + return builtin == BuiltInTessLevelInner ? 1 : 3; + else + return builtin == BuiltInTessLevelInner ? 2 : 4; +} + +// Since MSL does not allow arrays to be copied via simple variable assignment, +// if the LHS and RHS represent an assignment of an entire array, it must be +// implemented by calling an array copy function. +// Returns whether the struct assignment was emitted. +bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs) +{ + // We only care about assignments of an entire array + auto &type = expression_type(id_lhs); + if (!is_array(get_pointee_type(type))) + return false; + + auto *var = maybe_get(id_lhs); + + // Is this a remapped, static constant? Don't do anything. + if (var && var->remapped_variable && var->statically_assigned) + return true; + + if (ir.ids[id_rhs].get_type() == TypeConstant && var && var->deferred_declaration) + { + // Special case, if we end up declaring a variable when assigning the constant array, + // we can avoid the copy by directly assigning the constant expression. + // This is likely necessary to be able to use a variable as a true look-up table, as it is unlikely + // the compiler will be able to optimize the spvArrayCopy() into a constant LUT. + // After a variable has been declared, we can no longer assign constant arrays in MSL unfortunately. + statement(to_expression(id_lhs), " = ", constant_expression(get(id_rhs)), ";"); + return true; + } + + if (is_tesc_shader() && has_decoration(id_lhs, DecorationBuiltIn)) + { + auto builtin = BuiltIn(get_decoration(id_lhs, DecorationBuiltIn)); + // Need to manually unroll the array store. + if (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter) + { + uint32_t array_size = get_physical_tess_level_array_size(builtin); + if (array_size == 1) + statement(to_expression(id_lhs), " = half(", to_expression(id_rhs), "[0]);"); + else + { + for (uint32_t i = 0; i < array_size; i++) + statement(to_expression(id_lhs), "[", i, "] = half(", to_expression(id_rhs), "[", i, "]);"); + } + return true; + } + } + + auto lhs_storage = get_expression_effective_storage_class(id_lhs); + auto rhs_storage = get_expression_effective_storage_class(id_rhs); + if (!emit_array_copy(nullptr, id_lhs, id_rhs, lhs_storage, rhs_storage)) + return false; + + register_write(id_lhs); + + return true; +} + +// Emits one of the atomic functions. In MSL, the atomic functions operate on pointers +void CompilerMSL::emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, Op opcode, + uint32_t mem_order_1, uint32_t mem_order_2, bool has_mem_order_2, uint32_t obj, uint32_t op1, + bool op1_is_pointer, bool op1_is_literal, uint32_t op2) +{ + string exp; + + auto &ptr_type = expression_type(obj); + auto &type = get_pointee_type(ptr_type); + auto expected_type = type.basetype; + if (opcode == OpAtomicUMax || opcode == OpAtomicUMin) + expected_type = to_unsigned_basetype(type.width); + else if (opcode == OpAtomicSMax || opcode == OpAtomicSMin) + expected_type = to_signed_basetype(type.width); + + bool use_native_image_atomic; + if (msl_options.supports_msl_version(3, 1)) + use_native_image_atomic = check_atomic_image(obj); + else + use_native_image_atomic = false; + + if (type.width == 64) + SPIRV_CROSS_THROW("MSL currently does not support 64-bit atomics."); + + auto remapped_type = type; + remapped_type.basetype = expected_type; + + auto *var = maybe_get_backing_variable(obj); + const auto *res_type = var ? &get(var->basetype) : nullptr; + assert(type.storage != StorageClassImage || res_type); + + bool is_atomic_compare_exchange_strong = op1_is_pointer && op1; + + bool check_discard = opcode != OpAtomicLoad && needs_frag_discard_checks() && + ptr_type.storage != StorageClassWorkgroup; + + // Even compare exchange atomics are vec4 on metal for ... reasons :v + uint32_t vec4_temporary_id = 0; + if (use_native_image_atomic && is_atomic_compare_exchange_strong) + { + uint32_t &tmp_id = extra_sub_expressions[result_id]; + if (!tmp_id) + { + tmp_id = ir.increase_bound_by(2); + + auto vec4_type = get(result_type); + vec4_type.vecsize = 4; + set(tmp_id + 1, vec4_type); + } + + vec4_temporary_id = tmp_id; + } + + if (check_discard) + { + if (is_atomic_compare_exchange_strong) + { + // We're already emitting a CAS loop here; a conditional won't hurt. + emit_uninitialized_temporary_expression(result_type, result_id); + if (vec4_temporary_id) + emit_uninitialized_temporary_expression(vec4_temporary_id + 1, vec4_temporary_id); + statement("if (!", builtin_to_glsl(BuiltInHelperInvocation, StorageClassInput), ")"); + begin_scope(); + } + else + exp = join("(!", builtin_to_glsl(BuiltInHelperInvocation, StorageClassInput), " ? "); + } + + if (use_native_image_atomic) + { + auto obj_expression = to_expression(obj); + auto split_index = obj_expression.find_first_of('@'); + bool needs_reinterpret = opcode == OpAtomicUMax || opcode == OpAtomicUMin || opcode == OpAtomicSMax || opcode == OpAtomicSMin; + needs_reinterpret &= type.basetype != expected_type; + SPIRVariable *backing_var = nullptr; + + // Try to avoid waiting until not force recompile later mode to enable force recompile later + if (needs_reinterpret && (backing_var = maybe_get_backing_variable(obj))) + add_spv_func_and_recompile(SPVFuncImplTextureCast); + + // Will only be false if we're in "force recompile later" mode. + if (split_index != string::npos) + { + auto coord = obj_expression.substr(split_index + 1); + auto image_expr = obj_expression.substr(0, split_index); + + // Handle problem cases with sign where we need signed min/max on a uint image for example. + // It seems to work to cast the texture type itself, even if it is probably wildly outside of spec, + // but SPIR-V requires this to work. + if (needs_reinterpret && backing_var) + { + assert(spv_function_implementations.count(SPVFuncImplTextureCast) && "Should have been added above"); + + const auto *backing_type = &get(backing_var->basetype); + while (backing_type->op != OpTypeImage) + backing_type = &get(backing_type->parent_type); + + auto img_type = *backing_type; + auto tmp_type = type; + tmp_type.basetype = expected_type; + img_type.image.type = ir.increase_bound_by(1); + set(img_type.image.type, tmp_type); + + image_expr = join("spvTextureCast<", type_to_glsl(img_type, obj), ">(", image_expr, ")"); + } + + exp += join(image_expr, ".", op, "("); + if (ptr_type.storage == StorageClassImage && (res_type->image.arrayed || res_type->image.dim == DimCube)) + { + switch (res_type->image.dim) + { + case Dim1D: + if (msl_options.texture_1D_as_2D) + exp += join("uint2(", coord, ".x, 0), ", coord, ".y"); + else + exp += join(coord, ".x, ", coord, ".y"); + break; + case Dim2D: + exp += join(coord, ".xy, ", coord, ".z"); + break; + case DimCube: + if (!msl_options.supports_msl_version(4, 0)) + SPIRV_CROSS_THROW("Cannot do atomics on Cube textures before 4.0."); + + if (res_type->image.arrayed) + exp += join(coord, ".xy, ", coord, ".z % 6u, ", coord, ".z / 6u"); + else + exp += join(coord, ".xy, ", coord, ".z"); + break; + default: + SPIRV_CROSS_THROW("Cannot do atomics on unknown dimension."); + } + } + else if (ptr_type.storage == StorageClassImage && res_type->image.dim == Dim1D && msl_options.texture_1D_as_2D) + exp += join("uint2(", coord, ", 0)"); + else + exp += coord; + } + else + { + exp += obj_expression; + } + } + else + { + exp += string(op) + "_explicit("; + exp += "("; + // Emulate texture2D atomic operations + if (ptr_type.storage == StorageClassImage) + { + auto &flags = ir.get_decoration_bitset(var->self); + if (decoration_flags_signal_volatile(flags)) + exp += "volatile "; + exp += "device"; + } + else if (var && ptr_type.storage != StorageClassPhysicalStorageBuffer) + { + exp += get_variable_address_space(*var); + } + else + { + // Fallback scenario, could happen for raw pointers. + exp += ptr_type.storage == StorageClassWorkgroup ? "threadgroup" : "device"; + } + + exp += " atomic_"; + // For signed and unsigned min/max, we can signal this through the pointer type. + // There is no other way, since C++ does not have explicit signage for atomics. + exp += type_to_glsl(remapped_type); + exp += "*)"; + + exp += "&"; + exp += to_enclosed_expression(obj); + } + + if (is_atomic_compare_exchange_strong) + { + assert(strcmp(op, "atomic_compare_exchange_weak") == 0); + assert(op2); + assert(has_mem_order_2); + exp += ", &"; + exp += to_name(vec4_temporary_id ? vec4_temporary_id : result_id); + exp += ", "; + exp += to_expression(op2); + + if (!use_native_image_atomic) + { + exp += ", "; + exp += get_memory_order(mem_order_1); + exp += ", "; + exp += get_memory_order(mem_order_2); + } + exp += ")"; + + // MSL only supports the weak atomic compare exchange, so emit a CAS loop here. + // The MSL function returns false if the atomic write fails OR the comparison test fails, + // so we must validate that it wasn't the comparison test that failed before continuing + // the CAS loop, otherwise it will loop infinitely, with the comparison test always failing. + // The function updates the comparator value from the memory value, so the additional + // comparison test evaluates the memory value against the expected value. + if (!check_discard) + { + emit_uninitialized_temporary_expression(result_type, result_id); + if (vec4_temporary_id) + emit_uninitialized_temporary_expression(vec4_temporary_id + 1, vec4_temporary_id); + } + + statement("do"); + begin_scope(); + + string scalar_expression; + if (vec4_temporary_id) + scalar_expression = join(to_expression(vec4_temporary_id), ".x"); + else + scalar_expression = to_expression(result_id); + + statement(scalar_expression, " = ", to_expression(op1), ";"); + end_scope_decl(join("while (!", exp, " && ", scalar_expression, " == ", to_enclosed_expression(op1), ")")); + if (vec4_temporary_id) + statement(to_expression(result_id), " = ", scalar_expression, ";"); + + // Vulkan: (section 9.29: ... and values returned by atomic instructions in helper invocations are undefined) + if (check_discard) + { + end_scope(); + statement("else"); + begin_scope(); + statement(to_expression(result_id), " = {};"); + end_scope(); + } + } + else + { + assert(strcmp(op, "atomic_compare_exchange_weak") != 0); + + if (op1) + { + exp += ", "; + if (op1_is_literal) + exp += to_string(op1); + else + exp += bitcast_expression(expected_type, op1); + } + + if (op2) + exp += ", " + to_expression(op2); + + if (!use_native_image_atomic) + { + exp += string(", ") + get_memory_order(mem_order_1); + if (has_mem_order_2) + exp += string(", ") + get_memory_order(mem_order_2); + } + + exp += ")"; + + // For some particular reason, atomics return vec4 in Metal ... + if (use_native_image_atomic) + exp += ".x"; + + // Vulkan: (section 9.29: ... and values returned by atomic instructions in helper invocations are undefined) + if (check_discard) + { + exp += " : "; + if (strcmp(op, "atomic_store") != 0) + exp += join(type_to_glsl(get(result_type)), "{}"); + else + exp += "((void)0)"; + exp += ")"; + } + + if (expected_type != type.basetype) + exp = bitcast_expression(type, expected_type, exp); + + if (strcmp(op, "atomic_store") != 0) + emit_op(result_type, result_id, exp, false); + else + statement(exp, ";"); + } + + flush_all_atomic_capable_variables(); +} + +// Metal only supports relaxed memory order for now +const char *CompilerMSL::get_memory_order(uint32_t) +{ + return "memory_order_relaxed"; +} + +// Override for MSL-specific extension syntax instructions. +// In some cases, deliberately select either the fast or precise versions of the MSL functions to match Vulkan math precision results. +void CompilerMSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop, const uint32_t *args, uint32_t count) +{ + auto op = static_cast(eop); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_glsl_instruction(op, args, count); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + op = get_remapped_glsl_op(op); + + auto &restype = get(result_type); + + // Only precise:: preserves NaN in trancendentals (supposedly, cannot find documentation for this). + const auto drop_nan_inf = FPFastMathModeNotInfMask | FPFastMathModeNotNaNMask; + bool preserve_nan = (get_fp_fast_math_flags_for_op(result_type, id) & drop_nan_inf) != drop_nan_inf; + const char *preserve_str = preserve_nan ? "precise" : "fast"; + + // TODO: Emit the default behavior to match existing code. Might need to be revisited. + // Only fp32 has the precise:: override. +#define EMIT_PRECISE_OVERRIDE(glsl_op, op) \ + case GLSLstd450##glsl_op: \ + if (restype.basetype == SPIRType::Float && preserve_nan) \ + emit_unary_func_op(result_type, id, args[0], "precise::" op); \ + else \ + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); \ + break + + switch (op) + { + EMIT_PRECISE_OVERRIDE(Cos, "cos"); + EMIT_PRECISE_OVERRIDE(Sin, "sin"); + EMIT_PRECISE_OVERRIDE(Tan, "tan"); + EMIT_PRECISE_OVERRIDE(Acos, "acos"); + EMIT_PRECISE_OVERRIDE(Asin, "asin"); + EMIT_PRECISE_OVERRIDE(Atan, "atan"); + EMIT_PRECISE_OVERRIDE(Exp, "exp"); + EMIT_PRECISE_OVERRIDE(Exp2, "exp2"); + EMIT_PRECISE_OVERRIDE(Log, "log"); + EMIT_PRECISE_OVERRIDE(Log2, "log2"); + EMIT_PRECISE_OVERRIDE(Sqrt, "sqrt"); +#undef EMIT_PRECISE_OVERRIDE + + case GLSLstd450Sinh: + if (restype.basetype == SPIRType::Half) + { + auto ftype = restype; + ftype.basetype = SPIRType::Float; + + // MSL does not have overload for half. Force-cast back to half. + auto expr = join(type_to_glsl(restype), "(", preserve_str, "::sinh(", type_to_glsl(ftype), "(", to_unpacked_expression(args[0]), ")))"); + emit_op(result_type, id, expr, should_forward(args[0])); + inherit_expression_dependencies(id, args[0]); + } + else if (preserve_nan) + emit_unary_func_op(result_type, id, args[0], "precise::sinh"); + else + emit_unary_func_op(result_type, id, args[0], "fast::sinh"); + break; + case GLSLstd450Cosh: + if (restype.basetype == SPIRType::Half) + { + auto ftype = restype; + ftype.basetype = SPIRType::Float; + + // MSL does not have overload for half. Force-cast back to half. + auto expr = join(type_to_glsl(restype), "(", preserve_str, "::cosh(", type_to_glsl(ftype), "(", to_unpacked_expression(args[0]), ")))"); + emit_op(result_type, id, expr, should_forward(args[0])); + inherit_expression_dependencies(id, args[0]); + } + else if (preserve_nan) + emit_unary_func_op(result_type, id, args[0], "precise::cosh"); + else + emit_unary_func_op(result_type, id, args[0], "fast::cosh"); + break; + case GLSLstd450Tanh: + if (restype.basetype == SPIRType::Half) + { + auto ftype = restype; + ftype.basetype = SPIRType::Float; + + // MSL does not have overload for half. Force-cast back to half. + auto expr = join(type_to_glsl(restype), "(", preserve_str, "::tanh(", type_to_glsl(ftype), "(", to_unpacked_expression(args[0]), ")))"); + emit_op(result_type, id, expr, should_forward(args[0])); + inherit_expression_dependencies(id, args[0]); + } + else + emit_unary_func_op(result_type, id, args[0], "precise::tanh"); + break; + case GLSLstd450Atan2: + if (restype.basetype == SPIRType::Half) + { + // MSL does not have overload for half. Force-cast back to half. + auto ftype = restype; + ftype.basetype = SPIRType::Float; + + auto expr = join(type_to_glsl(restype), + "(", preserve_str, "::atan2(", + type_to_glsl(ftype), "(", to_unpacked_expression(args[0]), "), ", + type_to_glsl(ftype), "(", to_unpacked_expression(args[1]), ")))"); + emit_op(result_type, id, expr, should_forward(args[0]) && should_forward(args[1])); + inherit_expression_dependencies(id, args[0]); + inherit_expression_dependencies(id, args[1]); + } + else + emit_binary_func_op(result_type, id, args[0], args[1], "precise::atan2"); + break; + case GLSLstd450InverseSqrt: + if (restype.basetype == SPIRType::Float && preserve_nan) + emit_unary_func_op(result_type, id, args[0], "precise::rsqrt"); + else + emit_unary_func_op(result_type, id, args[0], "rsqrt"); + break; + case GLSLstd450RoundEven: + emit_unary_func_op(result_type, id, args[0], "rint"); + break; + + case GLSLstd450FindILsb: + { + // In this template version of findLSB, we return T. + auto basetype = expression_type(args[0]).basetype; + emit_unary_func_op_cast(result_type, id, args[0], "spvFindLSB", basetype, basetype); + break; + } + + case GLSLstd450FindSMsb: + emit_unary_func_op_cast(result_type, id, args[0], "spvFindSMSB", int_type, int_type); + break; + + case GLSLstd450FindUMsb: + emit_unary_func_op_cast(result_type, id, args[0], "spvFindUMSB", uint_type, uint_type); + break; + + case GLSLstd450PackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_snorm4x8"); + break; + case GLSLstd450PackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_unorm4x8"); + break; + case GLSLstd450PackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_snorm2x16"); + break; + case GLSLstd450PackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "pack_float_to_unorm2x16"); + break; + + case GLSLstd450PackHalf2x16: + { + auto expr = join("as_type(half2(", to_expression(args[0]), "))"); + emit_op(result_type, id, expr, should_forward(args[0])); + inherit_expression_dependencies(id, args[0]); + break; + } + + case GLSLstd450UnpackSnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpack_snorm4x8_to_float"); + break; + case GLSLstd450UnpackUnorm4x8: + emit_unary_func_op(result_type, id, args[0], "unpack_unorm4x8_to_float"); + break; + case GLSLstd450UnpackSnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpack_snorm2x16_to_float"); + break; + case GLSLstd450UnpackUnorm2x16: + emit_unary_func_op(result_type, id, args[0], "unpack_unorm2x16_to_float"); + break; + + case GLSLstd450UnpackHalf2x16: + { + auto expr = join("float2(as_type(", to_expression(args[0]), "))"); + emit_op(result_type, id, expr, should_forward(args[0])); + inherit_expression_dependencies(id, args[0]); + break; + } + + case GLSLstd450PackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "unsupported_GLSLstd450PackDouble2x32"); // Currently unsupported + break; + case GLSLstd450UnpackDouble2x32: + emit_unary_func_op(result_type, id, args[0], "unsupported_GLSLstd450UnpackDouble2x32"); // Currently unsupported + break; + + case GLSLstd450MatrixInverse: + { + auto &mat_type = get(result_type); + switch (mat_type.columns) + { + case 2: + emit_unary_func_op(result_type, id, args[0], "spvInverse2x2"); + break; + case 3: + emit_unary_func_op(result_type, id, args[0], "spvInverse3x3"); + break; + case 4: + emit_unary_func_op(result_type, id, args[0], "spvInverse4x4"); + break; + default: + break; + } + break; + } + + case GLSLstd450FMin: + // If the result type isn't float, don't bother calling the specific + // precise::/fast:: version. Metal doesn't have those for half and + // double types. + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "min"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "fast::min"); + break; + + case GLSLstd450FMax: + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "max"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "fast::max"); + break; + + case GLSLstd450FClamp: + // TODO: If args[1] is 0 and args[2] is 1, emit a saturate() call. + if (get(result_type).basetype != SPIRType::Float) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp"); + else + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fast::clamp"); + break; + + case GLSLstd450NMin: + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "min"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "precise::min"); + break; + + case GLSLstd450NMax: + if (get(result_type).basetype != SPIRType::Float) + emit_binary_func_op(result_type, id, args[0], args[1], "max"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "precise::max"); + break; + + case GLSLstd450NClamp: + // TODO: If args[1] is 0 and args[2] is 1, emit a saturate() call. + if (get(result_type).basetype != SPIRType::Float) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "clamp"); + else + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "precise::clamp"); + break; + + case GLSLstd450InterpolateAtCentroid: + { + // We can't just emit the expression normally, because the qualified name contains a call to the default + // interpolate method, or refers to a local variable. We saved the interface index we need; use it to construct + // the base for the method call. + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_centroid()", component), + should_forward(args[0])); + break; + } + + case GLSLstd450InterpolateAtSample: + { + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_sample(", to_expression(args[1]), ")", component), + should_forward(args[0]) && should_forward(args[1])); + break; + } + + case GLSLstd450InterpolateAtOffset: + { + uint32_t interface_index = get_extended_decoration(args[0], SPIRVCrossDecorationInterfaceMemberIndex); + string component; + if (has_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr)) + { + uint32_t index_expr = get_extended_decoration(args[0], SPIRVCrossDecorationInterpolantComponentExpr); + auto *c = maybe_get(index_expr); + if (!c || c->specialization) + component = join("[", to_expression(index_expr), "]"); + else + component = join(".", index_to_swizzle(c->scalar())); + } + // Like Direct3D, Metal puts the (0, 0) at the upper-left corner, not the center as SPIR-V and GLSL do. + // Offset the offset by (1/2 - 1/16), or 0.4375, to compensate for this. + // It has to be (1/2 - 1/16) and not 1/2, or several CTS tests subtly break on Intel. + emit_op(result_type, id, + join(to_name(stage_in_var_id), ".", to_member_name(get_stage_in_struct_type(), interface_index), + ".interpolate_at_offset(", to_expression(args[1]), " + 0.4375)", component), + should_forward(args[0]) && should_forward(args[1])); + break; + } + + case GLSLstd450Distance: + // MSL does not support scalar versions here. + if (expression_type(args[0]).vecsize == 1) + { + // Equivalent to length(a - b) -> abs(a - b). + emit_op(result_type, id, + join("abs(", to_enclosed_unpacked_expression(args[0]), " - ", + to_enclosed_unpacked_expression(args[1]), ")"), + should_forward(args[0]) && should_forward(args[1])); + inherit_expression_dependencies(id, args[0]); + inherit_expression_dependencies(id, args[1]); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Length: + // MSL does not support scalar versions, so use abs(). + if (expression_type(args[0]).vecsize == 1) + emit_unary_func_op(result_type, id, args[0], "abs"); + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Normalize: + { + auto &exp_type = expression_type(args[0]); + // MSL does not support scalar versions here. + // MSL has no implementation for normalize in the fast:: namespace for half + // Returns -1 or 1 for valid input, sign() does the job. + + // precise::normalize asm looks ridiculous. + // Don't think this actually matters unless proven otherwise. + if (exp_type.vecsize == 1) + emit_unary_func_op(result_type, id, args[0], "sign"); + else if (exp_type.basetype == SPIRType::Half) + emit_unary_func_op(result_type, id, args[0], "normalize"); + else + emit_unary_func_op(result_type, id, args[0], "fast::normalize"); + break; + } + case GLSLstd450Reflect: + if (get(result_type).vecsize == 1) + emit_binary_func_op(result_type, id, args[0], args[1], "spvReflect"); + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Refract: + if (get(result_type).vecsize == 1) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvRefract"); + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450FaceForward: + if (get(result_type).vecsize == 1) + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "spvFaceForward"); + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + + case GLSLstd450Modf: + case GLSLstd450Frexp: + { + // Special case. If the variable is a scalar access chain, we cannot use it directly. We have to emit a temporary. + // Another special case is if the variable is in a storage class which is not thread. + auto *ptr = maybe_get(args[1]); + auto &type = expression_type(args[1]); + + bool is_thread_storage = storage_class_array_is_thread(type.storage); + if (type.storage == StorageClassOutput && capture_output_to_buffer) + is_thread_storage = false; + + if (!is_thread_storage || + (ptr && ptr->access_chain && is_scalar(expression_type(args[1])))) + { + register_call_out_argument(args[1]); + forced_temporaries.insert(id); + + // Need to create temporaries and copy over to access chain after. + // We cannot directly take the reference of a vector swizzle in MSL, even if it's scalar ... + uint32_t &tmp_id = extra_sub_expressions[id]; + if (!tmp_id) + tmp_id = ir.increase_bound_by(1); + + uint32_t tmp_type_id = get_pointee_type_id(expression_type_id(args[1])); + emit_uninitialized_temporary_expression(tmp_type_id, tmp_id); + emit_binary_func_op(result_type, id, args[0], tmp_id, eop == GLSLstd450Modf ? "modf" : "frexp"); + statement(to_expression(args[1]), " = ", to_expression(tmp_id), ";"); + } + else + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + } + + case GLSLstd450Pow: + // powr makes x < 0.0 undefined, just like SPIR-V. + if (restype.basetype == SPIRType::Float && preserve_nan) + emit_binary_func_op(result_type, id, args[0], args[1], "precise::powr"); + else + emit_binary_func_op(result_type, id, args[0], args[1], "powr"); + break; + + default: + CompilerGLSL::emit_glsl_op(result_type, id, eop, args, count); + break; + } +} + +void CompilerMSL::emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t id, uint32_t eop, + const uint32_t *args, uint32_t count) +{ + enum AMDShaderTrinaryMinMax + { + FMin3AMD = 1, + UMin3AMD = 2, + SMin3AMD = 3, + FMax3AMD = 4, + UMax3AMD = 5, + SMax3AMD = 6, + FMid3AMD = 7, + UMid3AMD = 8, + SMid3AMD = 9 + }; + + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Trinary min/max functions require MSL 2.1."); + + auto op = static_cast(eop); + + switch (op) + { + case FMid3AMD: + case UMid3AMD: + case SMid3AMD: + emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "median3"); + break; + default: + CompilerGLSL::emit_spv_amd_shader_trinary_minmax_op(result_type, id, eop, args, count); + break; + } +} + +// Emit a structure declaration for the specified interface variable. +void CompilerMSL::emit_interface_block(uint32_t ib_var_id) +{ + if (ib_var_id) + { + auto &ib_var = get(ib_var_id); + auto &ib_type = get_variable_data_type(ib_var); + //assert(ib_type.basetype == SPIRType::Struct && !ib_type.member_types.empty()); + assert(ib_type.basetype == SPIRType::Struct); + emit_struct(ib_type); + } +} + +// Emits the declaration signature of the specified function. +// If this is the entry point function, Metal-specific return value and function arguments are added. +void CompilerMSL::emit_function_prototype(SPIRFunction &func, const Bitset &) +{ + if (func.self != ir.default_entry_point) + add_function_overload(func); + + local_variable_names = resource_names; + string decl; + + processing_entry_point = func.self == ir.default_entry_point; + + // Metal helper functions must be static force-inline otherwise they will cause problems when linked together in a single Metallib. + if (!processing_entry_point) + statement(force_inline); + + auto &type = get(func.return_type); + + if (!type.array.empty() && msl_options.force_native_arrays) + { + // We cannot return native arrays in MSL, so "return" through an out variable. + decl += "void"; + } + else + { + decl += func_type_decl(type); + } + + decl += " "; + decl += to_name(func.self); + decl += "("; + + if (!type.array.empty() && msl_options.force_native_arrays) + { + // Fake arrays returns by writing to an out array instead. + decl += "thread "; + decl += type_to_glsl(type); + decl += " (&spvReturnValue)"; + decl += type_to_array_glsl(type, 0); + if (!func.arguments.empty()) + decl += ", "; + } + + if (processing_entry_point) + { + if (msl_options.argument_buffers) + decl += entry_point_args_argument_buffer(!func.arguments.empty()); + else + decl += entry_point_args_classic(!func.arguments.empty()); + + // append entry point args to avoid conflicts in local variable names. + local_variable_names.insert(resource_names.begin(), resource_names.end()); + + // If entry point function has variables that require early declaration, + // ensure they each have an empty initializer, creating one if needed. + // This is done at this late stage because the initialization expression + // is cleared after each compilation pass. + for (auto var_id : vars_needing_early_declaration) + { + auto &ed_var = get(var_id); + ID &initializer = ed_var.initializer; + if (!initializer) + initializer = ir.increase_bound_by(1); + + // Do not override proper initializers. + if (ir.ids[initializer].get_type() == TypeNone || ir.ids[initializer].get_type() == TypeExpression) + set(ed_var.initializer, "{}", ed_var.basetype, true); + } + + // add `taskPayloadSharedEXT` variable to entry-point arguments + for (auto &v : func.local_variables) + { + auto &var = get(v); + if (var.storage != StorageClassTaskPayloadWorkgroupEXT) + continue; + + add_local_variable_name(v); + SPIRFunction::Parameter arg = {}; + arg.id = v; + arg.type = var.basetype; + arg.alias_global_variable = true; + decl += join(", ", argument_decl(arg), " [[payload]]"); + } + } + + for (auto &arg : func.arguments) + { + uint32_t name_id = arg.id; + + auto *var = maybe_get(arg.id); + if (var) + { + // If we need to modify the name of the variable, make sure we modify the original variable. + // Our alias is just a shadow variable. + if (arg.alias_global_variable && var->basevariable) + name_id = var->basevariable; + + var->parameter = &arg; // Hold a pointer to the parameter so we can invalidate the readonly field if needed. + } + + add_local_variable_name(name_id); + + decl += argument_decl(arg); + + bool is_dynamic_img_sampler = has_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler); + + auto &arg_type = get(arg.type); + if (arg_type.basetype == SPIRType::SampledImage && !is_dynamic_img_sampler) + { + // Manufacture automatic plane args for multiplanar texture + uint32_t planes = 1; + if (auto *constexpr_sampler = find_constexpr_sampler(name_id)) + if (constexpr_sampler->ycbcr_conversion_enable) + planes = constexpr_sampler->planes; + for (uint32_t i = 1; i < planes; i++) + decl += join(", ", argument_decl(arg), plane_name_suffix, i); + + // Manufacture automatic sampler arg for SampledImage texture + if (arg_type.image.dim != DimBuffer) + { + if (arg_type.array.empty() || (var ? is_var_runtime_size_array(*var) : is_runtime_size_array(arg_type))) + { + decl += join(", ", sampler_type(arg_type, arg.id, false), " ", to_sampler_expression(name_id)); + } + else + { + const char *sampler_address_space = + descriptor_address_space(name_id, + StorageClassUniformConstant, + "thread const"); + decl += join(", ", sampler_address_space, " ", sampler_type(arg_type, name_id, false), "& ", + to_sampler_expression(name_id)); + } + } + } + + // Manufacture automatic swizzle arg. + if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(arg_type) && + !is_dynamic_img_sampler) + { + bool arg_is_array = !arg_type.array.empty(); + decl += join(", constant uint", arg_is_array ? "* " : "& ", to_swizzle_expression(name_id)); + } + + if (buffer_requires_array_length(name_id)) + { + bool arg_is_array = !arg_type.array.empty(); + decl += join(", constant uint", arg_is_array ? "* " : "& ", to_buffer_size_expression(name_id)); + } + + if (&arg != &func.arguments.back()) + decl += ", "; + } + + decl += ")"; + statement(decl); +} + +static bool needs_chroma_reconstruction(const MSLConstexprSampler *constexpr_sampler) +{ + // For now, only multiplanar images need explicit reconstruction. GBGR and BGRG images + // use implicit reconstruction. + return constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable && constexpr_sampler->planes > 1; +} + +// Returns the texture sampling function string for the specified image and sampling characteristics. +string CompilerMSL::to_function_name(const TextureFunctionNameArguments &args) +{ + VariableID img = args.base.img; + const MSLConstexprSampler *constexpr_sampler = nullptr; + bool is_dynamic_img_sampler = false; + if (auto *var = maybe_get_backing_variable(img)) + { + constexpr_sampler = find_constexpr_sampler(var->basevariable ? var->basevariable : VariableID(var->self)); + is_dynamic_img_sampler = has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler); + } + + // Special-case gather. We have to alter the component being looked up in the swizzle case. + if (msl_options.swizzle_texture_samples && args.base.is_gather && !is_dynamic_img_sampler && + (!constexpr_sampler || !constexpr_sampler->ycbcr_conversion_enable)) + { + bool is_compare = comparison_ids.count(img); + add_spv_func_and_recompile(is_compare ? SPVFuncImplGatherCompareSwizzle : SPVFuncImplGatherSwizzle); + return is_compare ? "spvGatherCompareSwizzle" : "spvGatherSwizzle"; + } + + // Special-case gather with an array of offsets. We have to lower into 4 separate gathers. + if (args.has_array_offsets && !is_dynamic_img_sampler && + (!constexpr_sampler || !constexpr_sampler->ycbcr_conversion_enable)) + { + bool is_compare = comparison_ids.count(img); + add_spv_func_and_recompile(is_compare ? SPVFuncImplGatherCompareConstOffsets : SPVFuncImplGatherConstOffsets); + return is_compare ? "spvGatherCompareConstOffsets" : "spvGatherConstOffsets"; + } + + auto *combined = maybe_get(img); + + // Texture reference + string fname; + if (needs_chroma_reconstruction(constexpr_sampler) && !is_dynamic_img_sampler) + { + if (constexpr_sampler->planes != 2 && constexpr_sampler->planes != 3) + SPIRV_CROSS_THROW("Unhandled number of color image planes!"); + // 444 images aren't downsampled, so we don't need to do linear filtering. + if (constexpr_sampler->resolution == MSL_FORMAT_RESOLUTION_444 || + constexpr_sampler->chroma_filter == MSL_SAMPLER_FILTER_NEAREST) + { + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructNearest2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructNearest3Plane); + fname = "spvChromaReconstructNearest"; + } + else // Linear with a downsampled format + { + fname = "spvChromaReconstructLinear"; + switch (constexpr_sampler->resolution) + { + case MSL_FORMAT_RESOLUTION_444: + assert(false); + break; // not reached + case MSL_FORMAT_RESOLUTION_422: + switch (constexpr_sampler->x_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422CositedEven2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422CositedEven3Plane); + fname += "422CositedEven"; + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422Midpoint2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear422Midpoint3Plane); + fname += "422Midpoint"; + break; + default: + SPIRV_CROSS_THROW("Invalid chroma location."); + } + break; + case MSL_FORMAT_RESOLUTION_420: + fname += "420"; + switch (constexpr_sampler->x_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + switch (constexpr_sampler->y_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane); + else + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane); + fname += "XCositedEvenYCositedEven"; + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane); + else + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane); + fname += "XCositedEvenYMidpoint"; + break; + default: + SPIRV_CROSS_THROW("Invalid Y chroma location."); + } + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + switch (constexpr_sampler->y_chroma_offset) + { + case MSL_CHROMA_LOCATION_COSITED_EVEN: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane); + else + add_spv_func_and_recompile( + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane); + fname += "XMidpointYCositedEven"; + break; + case MSL_CHROMA_LOCATION_MIDPOINT: + if (constexpr_sampler->planes == 2) + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane); + else + add_spv_func_and_recompile(SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane); + fname += "XMidpointYMidpoint"; + break; + default: + SPIRV_CROSS_THROW("Invalid Y chroma location."); + } + break; + default: + SPIRV_CROSS_THROW("Invalid X chroma location."); + } + break; + default: + SPIRV_CROSS_THROW("Invalid format resolution."); + } + } + } + else + { + fname = to_expression(combined ? combined->image : img) + "."; + + // Texture function and sampler + if (args.base.is_fetch) + fname += "read"; + else if (args.base.is_gather) + fname += "gather"; + else + fname += "sample"; + + if (args.has_dref) + fname += "_compare"; + } + + return fname; +} + +string CompilerMSL::convert_to_f32(const string &expr, uint32_t components) +{ + SPIRType t { components > 1 ? OpTypeVector : OpTypeFloat }; + t.basetype = SPIRType::Float; + t.vecsize = components; + t.columns = 1; + return join(type_to_glsl_constructor(t), "(", expr, ")"); +} + +static inline bool sampling_type_needs_f32_conversion(const SPIRType &type) +{ + // Double is not supported to begin with, but doesn't hurt to check for completion. + return type.basetype == SPIRType::Half || type.basetype == SPIRType::Double; +} + +// Returns the function args for a texture sampling function for the specified image and sampling characteristics. +string CompilerMSL::to_function_args(const TextureFunctionArguments &args, bool *p_forward) +{ + VariableID img = args.base.img; + auto &imgtype = *args.base.imgtype; + uint32_t lod = args.lod; + uint32_t grad_x = args.grad_x; + uint32_t grad_y = args.grad_y; + uint32_t bias = args.bias; + + const MSLConstexprSampler *constexpr_sampler = nullptr; + bool is_dynamic_img_sampler = false; + if (auto *var = maybe_get_backing_variable(img)) + { + constexpr_sampler = find_constexpr_sampler(var->basevariable ? var->basevariable : VariableID(var->self)); + is_dynamic_img_sampler = has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler); + } + + string farg_str; + bool forward = true; + + if (!is_dynamic_img_sampler) + { + // Texture reference (for some cases) + if (needs_chroma_reconstruction(constexpr_sampler)) + { + // Multiplanar images need two or three textures. + farg_str += to_expression(img); + for (uint32_t i = 1; i < constexpr_sampler->planes; i++) + farg_str += join(", ", to_expression(img), plane_name_suffix, i); + } + else if ((!constexpr_sampler || !constexpr_sampler->ycbcr_conversion_enable) && + msl_options.swizzle_texture_samples && args.base.is_gather) + { + auto *combined = maybe_get(img); + farg_str += to_expression(combined ? combined->image : img); + } + + // Gathers with constant offsets call a special function, so include the texture. + if (args.has_array_offsets) + farg_str += to_expression(img); + + // Sampler reference + if (!args.base.is_fetch) + { + if (!farg_str.empty()) + farg_str += ", "; + farg_str += to_sampler_expression(img); + } + + if ((!constexpr_sampler || !constexpr_sampler->ycbcr_conversion_enable) && + msl_options.swizzle_texture_samples && args.base.is_gather) + { + // Add the swizzle constant from the swizzle buffer. + farg_str += ", " + to_swizzle_expression(img); + used_swizzle_buffer = true; + } + + // Const offsets gather puts the const offsets before the other args. + if (args.has_array_offsets) + { + forward = forward && should_forward(args.offset); + farg_str += ", " + to_unpacked_expression(args.offset); + } + + // Const offsets gather or swizzled gather puts the component before the other args. + if (args.component && (args.has_array_offsets || msl_options.swizzle_texture_samples)) + { + forward = forward && should_forward(args.component); + farg_str += ", " + to_component_argument(args.component); + } + } + + // Texture coordinates + forward = forward && should_forward(args.coord); + auto coord_expr = to_enclosed_unpacked_expression(args.coord); + auto &coord_type = expression_type(args.coord); + bool coord_is_fp = type_is_floating_point(coord_type); + bool is_cube_fetch = false; + + string tex_coords = coord_expr; + uint32_t alt_coord_component = 0; + + switch (imgtype.image.dim) + { + + case Dim1D: + if (coord_type.vecsize > 1) + tex_coords = enclose_expression(tex_coords) + ".x"; + + if (args.base.is_fetch) + tex_coords = "uint(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + else if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 1); + + if (msl_options.texture_1D_as_2D) + { + if (args.base.is_fetch) + tex_coords = "uint2(" + tex_coords + ", 0)"; + else + tex_coords = "float2(" + tex_coords + ", 0.5)"; + } + + alt_coord_component = 1; + break; + + case DimBuffer: + if (coord_type.vecsize > 1) + tex_coords = enclose_expression(tex_coords) + ".x"; + + if (msl_options.texture_buffer_native) + { + tex_coords = "uint(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + } + else + { + // Metal texel buffer textures are 2D, so convert 1D coord to 2D. + // Support for Metal 2.1's new texture_buffer type. + if (args.base.is_fetch) + { + if (msl_options.texel_buffer_texture_width > 0) + { + tex_coords = "spvTexelBufferCoord(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + } + else + { + tex_coords = "spvTexelBufferCoord(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ", " + + to_expression(img) + ")"; + } + } + } + + alt_coord_component = 1; + break; + + case DimSubpassData: + // If we're using Metal's native frame-buffer fetch API for subpass inputs, + // this path will not be hit. + tex_coords = "uint2(gl_FragCoord.xy)"; + alt_coord_component = 2; + break; + + case Dim2D: + if (coord_type.vecsize > 2) + tex_coords = enclose_expression(tex_coords) + ".xy"; + + if (args.base.is_fetch) + tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + else if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 2); + + alt_coord_component = 2; + break; + + case Dim3D: + if (coord_type.vecsize > 3) + tex_coords = enclose_expression(tex_coords) + ".xyz"; + + if (args.base.is_fetch) + tex_coords = "uint3(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + else if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 3); + + alt_coord_component = 3; + break; + + case DimCube: + if (args.base.is_fetch) + { + is_cube_fetch = true; + tex_coords += ".xy"; + tex_coords = "uint2(" + round_fp_tex_coords(tex_coords, coord_is_fp) + ")"; + } + else + { + if (coord_type.vecsize > 3) + tex_coords = enclose_expression(tex_coords) + ".xyz"; + } + + if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords = convert_to_f32(tex_coords, 3); + + alt_coord_component = 3; + break; + + default: + break; + } + + if (args.base.is_fetch && args.offset) + { + // Fetch offsets must be applied directly to the coordinate. + forward = forward && should_forward(args.offset); + auto &type = expression_type(args.offset); + if (imgtype.image.dim == Dim1D && msl_options.texture_1D_as_2D) + { + if (type.basetype != SPIRType::UInt) + tex_coords += join(" + uint2(", bitcast_expression(SPIRType::UInt, args.offset), ", 0)"); + else + tex_coords += join(" + uint2(", to_enclosed_unpacked_expression(args.offset), ", 0)"); + } + else + { + if (type.basetype != SPIRType::UInt) + tex_coords += " + " + bitcast_expression(SPIRType::UInt, args.offset); + else + tex_coords += " + " + to_enclosed_unpacked_expression(args.offset); + } + } + + // If projection, use alt coord as divisor + if (args.base.is_proj) + { + if (sampling_type_needs_f32_conversion(coord_type)) + tex_coords += " / " + convert_to_f32(to_extract_component_expression(args.coord, alt_coord_component), 1); + else + tex_coords += " / " + to_extract_component_expression(args.coord, alt_coord_component); + } + + if (!farg_str.empty()) + farg_str += ", "; + + if (imgtype.image.dim == DimCube && imgtype.image.arrayed && msl_options.emulate_cube_array) + { + farg_str += "spvCubemapTo2DArrayFace(" + tex_coords + ").xy"; + + if (is_cube_fetch) + farg_str += ", uint(" + to_extract_component_expression(args.coord, 2) + ")"; + else + farg_str += + ", uint(spvCubemapTo2DArrayFace(" + tex_coords + ").z) + (uint(" + + round_fp_tex_coords(to_extract_component_expression(args.coord, alt_coord_component), coord_is_fp) + + ") * 6u)"; + + add_spv_func_and_recompile(SPVFuncImplCubemapTo2DArrayFace); + } + else + { + farg_str += tex_coords; + + // If fetch from cube, add face explicitly + if (is_cube_fetch) + { + // Special case for cube arrays, face and layer are packed in one dimension. + if (imgtype.image.arrayed) + farg_str += ", uint(" + to_extract_component_expression(args.coord, 2) + ") % 6u"; + else + farg_str += + ", uint(" + round_fp_tex_coords(to_extract_component_expression(args.coord, 2), coord_is_fp) + ")"; + } + + // If array, use alt coord + if (imgtype.image.arrayed) + { + // Special case for cube arrays, face and layer are packed in one dimension. + if (imgtype.image.dim == DimCube && args.base.is_fetch) + { + farg_str += ", uint(" + to_extract_component_expression(args.coord, 2) + ") / 6u"; + } + else + { + farg_str += + ", uint(" + + round_fp_tex_coords(to_extract_component_expression(args.coord, alt_coord_component), coord_is_fp) + + ")"; + if (imgtype.image.dim == DimSubpassData) + { + if (msl_options.multiview) + farg_str += " + gl_ViewIndex"; + else if (msl_options.arrayed_subpass_input) + farg_str += " + gl_Layer"; + } + } + } + else if (imgtype.image.dim == DimSubpassData) + { + if (msl_options.multiview) + farg_str += ", gl_ViewIndex"; + else if (msl_options.arrayed_subpass_input) + farg_str += ", gl_Layer"; + } + } + + // Depth compare reference value + if (args.dref) + { + forward = forward && should_forward(args.dref); + farg_str += ", "; + + auto &dref_type = expression_type(args.dref); + + string dref_expr; + if (args.base.is_proj) + dref_expr = join(to_enclosed_unpacked_expression(args.dref), " / ", + to_extract_component_expression(args.coord, alt_coord_component)); + else + dref_expr = to_unpacked_expression(args.dref); + + if (sampling_type_needs_f32_conversion(dref_type)) + dref_expr = convert_to_f32(dref_expr, 1); + + farg_str += dref_expr; + + if (msl_options.is_macos() && (grad_x || grad_y)) + { + // For sample compare, MSL does not support gradient2d for all targets (only iOS apparently according to docs). + // However, the most common case here is to have a constant gradient of 0, as that is the only way to express + // LOD == 0 in GLSL with sampler2DArrayShadow (cascaded shadow mapping). + // We will detect a compile-time constant 0 value for gradient and promote that to level(0) on MSL. + bool constant_zero_x = !grad_x || expression_is_constant_null(grad_x); + bool constant_zero_y = !grad_y || expression_is_constant_null(grad_y); + if (constant_zero_x && constant_zero_y && + (!imgtype.image.arrayed || !msl_options.sample_dref_lod_array_as_grad)) + { + lod = 0; + grad_x = 0; + grad_y = 0; + farg_str += ", level(0)"; + } + else if (!msl_options.supports_msl_version(2, 3)) + { + SPIRV_CROSS_THROW("Using non-constant 0.0 gradient() qualifier for sample_compare. This is not " + "supported on macOS prior to MSL 2.3."); + } + } + + if (msl_options.is_macos() && bias) + { + // Bias is not supported either on macOS with sample_compare. + // Verify it is compile-time zero, and drop the argument. + if (expression_is_constant_null(bias)) + { + bias = 0; + } + else if (!msl_options.supports_msl_version(2, 3)) + { + SPIRV_CROSS_THROW("Using non-constant 0.0 bias() qualifier for sample_compare. This is not supported " + "on macOS prior to MSL 2.3."); + } + } + } + + // LOD Options + // Metal does not support LOD for 1D textures. + if (bias && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D)) + { + forward = forward && should_forward(bias); + farg_str += ", bias(" + to_unpacked_expression(bias) + ")"; + } + + // Metal does not support LOD for 1D textures. + if (lod && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D)) + { + forward = forward && should_forward(lod); + if (args.base.is_fetch) + { + farg_str += ", " + to_unpacked_expression(lod); + } + else if (msl_options.sample_dref_lod_array_as_grad && args.dref && imgtype.image.arrayed) + { + if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Using non-constant 0.0 gradient() qualifier for sample_compare. This is not " + "supported on macOS prior to MSL 2.3."); + // Some Metal devices have a bug where the LoD is erroneously biased upward + // when using a level() argument. Since this doesn't happen as much with gradient2d(), + // if we perform the LoD calculation in reverse, we can pass a gradient + // instead. + // lod = log2(rhoMax/eta) -> exp2(lod) = rhoMax/eta + // If we make all of the scale factors the same, eta will be 1 and + // exp2(lod) = rho. + // rhoX = dP/dx * extent; rhoY = dP/dy * extent + // Therefore, dP/dx = dP/dy = exp2(lod)/extent. + // (Subtracting 0.5 before exponentiation gives better results.) + string grad_opt, extent, grad_coord; + VariableID base_img = img; + if (auto *combined = maybe_get(img)) + base_img = combined->image; + switch (imgtype.image.dim) + { + case Dim1D: + grad_opt = "gradient2d"; + extent = join("float2(", to_expression(base_img), ".get_width(), 1.0)"); + break; + case Dim2D: + grad_opt = "gradient2d"; + extent = join("float2(", to_expression(base_img), ".get_width(), ", to_expression(base_img), ".get_height())"); + break; + case DimCube: + if (imgtype.image.arrayed && msl_options.emulate_cube_array) + { + grad_opt = "gradient2d"; + extent = join("float2(", to_expression(base_img), ".get_width())"); + } + else + { + if (msl_options.agx_manual_cube_grad_fixup) + { + add_spv_func_and_recompile(SPVFuncImplGradientCube); + grad_opt = "spvGradientCube"; + grad_coord = tex_coords + ", "; + } + else + { + grad_opt = "gradientcube"; + } + extent = join("float3(", to_expression(base_img), ".get_width())"); + } + break; + default: + grad_opt = "unsupported_gradient_dimension"; + extent = "float3(1.0)"; + break; + } + farg_str += join(", ", grad_opt, "(", grad_coord, "exp2(", to_unpacked_expression(lod), " - 0.5) / ", extent, + ", exp2(", to_unpacked_expression(lod), " - 0.5) / ", extent, ")"); + } + else + { + farg_str += ", level(" + to_unpacked_expression(lod) + ")"; + } + } + else if (args.base.is_fetch && !lod && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D) && + imgtype.image.dim != DimBuffer && !imgtype.image.ms && imgtype.image.sampled != 2) + { + // Lod argument is optional in OpImageFetch, but we require a LOD value, pick 0 as the default. + // Check for sampled type as well, because is_fetch is also used for OpImageRead in MSL. + farg_str += ", 0"; + } + + // Metal does not support LOD for 1D textures. + if ((grad_x || grad_y) && (imgtype.image.dim != Dim1D || msl_options.texture_1D_as_2D)) + { + forward = forward && should_forward(grad_x); + forward = forward && should_forward(grad_y); + string grad_opt, grad_coord; + switch (imgtype.image.dim) + { + case Dim1D: + case Dim2D: + grad_opt = "gradient2d"; + break; + case Dim3D: + grad_opt = "gradient3d"; + break; + case DimCube: + if (imgtype.image.arrayed && msl_options.emulate_cube_array) + { + grad_opt = "gradient2d"; + } + else if (msl_options.agx_manual_cube_grad_fixup) + { + add_spv_func_and_recompile(SPVFuncImplGradientCube); + grad_opt = "spvGradientCube"; + grad_coord = tex_coords + ", "; + } + else + { + grad_opt = "gradientcube"; + } + break; + default: + grad_opt = "unsupported_gradient_dimension"; + break; + } + farg_str += join(", ", grad_opt, "(", grad_coord, to_unpacked_expression(grad_x), ", ", to_unpacked_expression(grad_y), ")"); + } + + if (args.min_lod) + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("min_lod_clamp() is only supported in MSL 2.2+ and up."); + + forward = forward && should_forward(args.min_lod); + farg_str += ", min_lod_clamp(" + to_unpacked_expression(args.min_lod) + ")"; + } + + // Add offsets + string offset_expr; + const SPIRType *offset_type = nullptr; + if (args.offset && !args.base.is_fetch && !args.has_array_offsets) + { + forward = forward && should_forward(args.offset); + offset_expr = to_unpacked_expression(args.offset); + offset_type = &expression_type(args.offset); + } + + if (!offset_expr.empty()) + { + switch (imgtype.image.dim) + { + case Dim1D: + if (!msl_options.texture_1D_as_2D) + break; + if (offset_type->vecsize > 1) + offset_expr = enclose_expression(offset_expr) + ".x"; + + farg_str += join(", int2(", offset_expr, ", 0)"); + break; + + case Dim2D: + if (offset_type->vecsize > 2) + offset_expr = enclose_expression(offset_expr) + ".xy"; + + farg_str += ", " + offset_expr; + break; + + case Dim3D: + if (offset_type->vecsize > 3) + offset_expr = enclose_expression(offset_expr) + ".xyz"; + + farg_str += ", " + offset_expr; + break; + + default: + break; + } + } + + if (args.component && !args.has_array_offsets) + { + // If 2D has gather component, ensure it also has an offset arg + if (imgtype.image.dim == Dim2D && offset_expr.empty()) + farg_str += ", int2(0)"; + + if (!msl_options.swizzle_texture_samples || is_dynamic_img_sampler) + { + forward = forward && should_forward(args.component); + + uint32_t image_var = 0; + if (const auto *combined = maybe_get(img)) + { + if (const auto *img_var = maybe_get_backing_variable(combined->image)) + image_var = img_var->self; + } + else if (const auto *var = maybe_get_backing_variable(img)) + { + image_var = var->self; + } + + if (image_var == 0 || !is_depth_image(expression_type(image_var), image_var)) + farg_str += ", " + to_component_argument(args.component); + } + } + + if (args.sample) + { + forward = forward && should_forward(args.sample); + farg_str += ", "; + farg_str += to_unpacked_expression(args.sample); + } + + *p_forward = forward; + + return farg_str; +} + +// If the texture coordinates are floating point, invokes MSL round() function to round them. +string CompilerMSL::round_fp_tex_coords(string tex_coords, bool coord_is_fp) +{ + return coord_is_fp ? ("rint(" + tex_coords + ")") : tex_coords; +} + +// Returns a string to use in an image sampling function argument. +// The ID must be a scalar constant. +string CompilerMSL::to_component_argument(uint32_t id) +{ + uint32_t component_index = evaluate_constant_u32(id); + switch (component_index) + { + case 0: + return "component::x"; + case 1: + return "component::y"; + case 2: + return "component::z"; + case 3: + return "component::w"; + + default: + SPIRV_CROSS_THROW("The value (" + to_string(component_index) + ") of OpConstant ID " + to_string(id) + + " is not a valid Component index, which must be one of 0, 1, 2, or 3."); + } +} + +// Establish sampled image as expression object and assign the sampler to it. +void CompilerMSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) +{ + set(result_id, result_type, image_id, samp_id); +} + +string CompilerMSL::to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions) +{ + auto *ops = stream(i); + uint32_t result_type_id = ops[0]; + uint32_t img = ops[2]; + auto &result_type = get(result_type_id); + auto op = static_cast(i.op); + bool is_gather = (op == OpImageGather || op == OpImageDrefGather); + + // Bypass pointers because we need the real image struct + auto &type = expression_type(img); + auto &imgtype = get(type.self); + + const MSLConstexprSampler *constexpr_sampler = nullptr; + bool is_dynamic_img_sampler = false; + if (auto *var = maybe_get_backing_variable(img)) + { + constexpr_sampler = find_constexpr_sampler(var->basevariable ? var->basevariable : VariableID(var->self)); + is_dynamic_img_sampler = has_extended_decoration(var->self, SPIRVCrossDecorationDynamicImageSampler); + } + + string expr; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable && !is_dynamic_img_sampler) + { + // If this needs sampler Y'CbCr conversion, we need to do some additional + // processing. + switch (constexpr_sampler->ycbcr_model) + { + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY: + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY: + // Default + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709: + add_spv_func_and_recompile(SPVFuncImplConvertYCbCrBT709); + expr += "spvConvertYCbCrBT709("; + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601: + add_spv_func_and_recompile(SPVFuncImplConvertYCbCrBT601); + expr += "spvConvertYCbCrBT601("; + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020: + add_spv_func_and_recompile(SPVFuncImplConvertYCbCrBT2020); + expr += "spvConvertYCbCrBT2020("; + break; + default: + SPIRV_CROSS_THROW("Invalid Y'CbCr model conversion."); + } + + if (constexpr_sampler->ycbcr_model != MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY) + { + switch (constexpr_sampler->ycbcr_range) + { + case MSL_SAMPLER_YCBCR_RANGE_ITU_FULL: + add_spv_func_and_recompile(SPVFuncImplExpandITUFullRange); + expr += "spvExpandITUFullRange("; + break; + case MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW: + add_spv_func_and_recompile(SPVFuncImplExpandITUNarrowRange); + expr += "spvExpandITUNarrowRange("; + break; + default: + SPIRV_CROSS_THROW("Invalid Y'CbCr range."); + } + } + } + else if (msl_options.swizzle_texture_samples && !is_gather && is_sampled_image_type(imgtype) && + !is_dynamic_img_sampler) + { + add_spv_func_and_recompile(SPVFuncImplTextureSwizzle); + expr += "spvTextureSwizzle("; + } + + string inner_expr = CompilerGLSL::to_texture_op(i, sparse, forward, inherited_expressions); + + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable && !is_dynamic_img_sampler) + { + if (!constexpr_sampler->swizzle_is_identity()) + { + static const char swizzle_names[] = "rgba"; + if (!constexpr_sampler->swizzle_has_one_or_zero()) + { + // If we can, do it inline. + expr += inner_expr + "."; + for (uint32_t c = 0; c < 4; c++) + { + switch (constexpr_sampler->swizzle[c]) + { + case MSL_COMPONENT_SWIZZLE_IDENTITY: + expr += swizzle_names[c]; + break; + case MSL_COMPONENT_SWIZZLE_R: + case MSL_COMPONENT_SWIZZLE_G: + case MSL_COMPONENT_SWIZZLE_B: + case MSL_COMPONENT_SWIZZLE_A: + expr += swizzle_names[constexpr_sampler->swizzle[c] - MSL_COMPONENT_SWIZZLE_R]; + break; + default: + SPIRV_CROSS_THROW("Invalid component swizzle."); + } + } + } + else + { + // Otherwise, we need to emit a temporary and swizzle that. + uint32_t temp_id = ir.increase_bound_by(1); + emit_op(result_type_id, temp_id, inner_expr, false); + for (auto &inherit : inherited_expressions) + inherit_expression_dependencies(temp_id, inherit); + inherited_expressions.clear(); + inherited_expressions.push_back(temp_id); + + switch (op) + { + case OpImageSampleDrefImplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleProjDrefImplicitLod: + register_control_dependent_expression(temp_id); + break; + + default: + break; + } + expr += type_to_glsl(result_type) + "("; + for (uint32_t c = 0; c < 4; c++) + { + switch (constexpr_sampler->swizzle[c]) + { + case MSL_COMPONENT_SWIZZLE_IDENTITY: + expr += to_expression(temp_id) + "." + swizzle_names[c]; + break; + case MSL_COMPONENT_SWIZZLE_ZERO: + expr += "0"; + break; + case MSL_COMPONENT_SWIZZLE_ONE: + expr += "1"; + break; + case MSL_COMPONENT_SWIZZLE_R: + case MSL_COMPONENT_SWIZZLE_G: + case MSL_COMPONENT_SWIZZLE_B: + case MSL_COMPONENT_SWIZZLE_A: + expr += to_expression(temp_id) + "." + + swizzle_names[constexpr_sampler->swizzle[c] - MSL_COMPONENT_SWIZZLE_R]; + break; + default: + SPIRV_CROSS_THROW("Invalid component swizzle."); + } + if (c < 3) + expr += ", "; + } + expr += ")"; + } + } + else + expr += inner_expr; + if (constexpr_sampler->ycbcr_model != MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY) + { + expr += join(", ", constexpr_sampler->bpc, ")"); + if (constexpr_sampler->ycbcr_model != MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY) + expr += ")"; + } + } + else + { + expr += inner_expr; + if (msl_options.swizzle_texture_samples && !is_gather && is_sampled_image_type(imgtype) && + !is_dynamic_img_sampler) + { + // Add the swizzle constant from the swizzle buffer. + expr += ", " + to_swizzle_expression(img) + ")"; + used_swizzle_buffer = true; + } + } + + return expr; +} + +static string create_swizzle(MSLComponentSwizzle swizzle) +{ + switch (swizzle) + { + case MSL_COMPONENT_SWIZZLE_IDENTITY: + return "spvSwizzle::none"; + case MSL_COMPONENT_SWIZZLE_ZERO: + return "spvSwizzle::zero"; + case MSL_COMPONENT_SWIZZLE_ONE: + return "spvSwizzle::one"; + case MSL_COMPONENT_SWIZZLE_R: + return "spvSwizzle::red"; + case MSL_COMPONENT_SWIZZLE_G: + return "spvSwizzle::green"; + case MSL_COMPONENT_SWIZZLE_B: + return "spvSwizzle::blue"; + case MSL_COMPONENT_SWIZZLE_A: + return "spvSwizzle::alpha"; + default: + SPIRV_CROSS_THROW("Invalid component swizzle."); + } +} + +// Returns a string representation of the ID, usable as a function arg. +// Manufacture automatic sampler arg for SampledImage texture. +string CompilerMSL::to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) +{ + string arg_str; + + auto &type = expression_type(id); + bool is_dynamic_img_sampler = has_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler); + // If the argument *itself* is a "dynamic" combined-image sampler, then we can just pass that around. + bool arg_is_dynamic_img_sampler = has_extended_decoration(id, SPIRVCrossDecorationDynamicImageSampler); + if (is_dynamic_img_sampler && !arg_is_dynamic_img_sampler) + arg_str = join("spvDynamicImageSampler<", type_to_glsl(get(type.image.type)), ">("); + + auto *c = maybe_get(id); + if (msl_options.force_native_arrays && c && !get(c->constant_type).array.empty()) + { + // If we are passing a constant array directly to a function for some reason, + // the callee will expect an argument in thread const address space + // (since we can only bind to arrays with references in MSL). + // To resolve this, we must emit a copy in this address space. + // This kind of code gen should be rare enough that performance is not a real concern. + // Inline the SPIR-V to avoid this kind of suboptimal codegen. + // + // We risk calling this inside a continue block (invalid code), + // so just create a thread local copy in the current function. + arg_str = join("_", id, "_array_copy"); + auto &constants = current_function->constant_arrays_needed_on_stack; + auto itr = find(begin(constants), end(constants), ID(id)); + if (itr == end(constants)) + { + force_recompile(); + constants.push_back(id); + } + } + // Dereference pointer variables where needed. + // FIXME: This dereference is actually backwards. We should really just support passing pointer variables between functions. + else if (should_dereference_caller_param(id)) + arg_str += dereference_expression(type, CompilerGLSL::to_func_call_arg(arg, id)); + else + arg_str += CompilerGLSL::to_func_call_arg(arg, id); + + // Need to check the base variable in case we need to apply a qualified alias. + uint32_t var_id = 0; + auto *var = maybe_get(id); + if (var) + var_id = var->basevariable; + + if (!arg_is_dynamic_img_sampler) + { + auto *constexpr_sampler = find_constexpr_sampler(var_id ? var_id : id); + if (type.basetype == SPIRType::SampledImage) + { + // Manufacture automatic plane args for multiplanar texture + uint32_t planes = 1; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + { + planes = constexpr_sampler->planes; + // If this parameter isn't aliasing a global, then we need to use + // the special "dynamic image-sampler" class to pass it--and we need + // to use it for *every* non-alias parameter, in case a combined + // image-sampler with a Y'CbCr conversion is passed. Hopefully, this + // pathological case is so rare that it should never be hit in practice. + if (!arg.alias_global_variable) + add_spv_func_and_recompile(SPVFuncImplDynamicImageSampler); + } + for (uint32_t i = 1; i < planes; i++) + arg_str += join(", ", CompilerGLSL::to_func_call_arg(arg, id), plane_name_suffix, i); + // Manufacture automatic sampler arg if the arg is a SampledImage texture. + if (type.image.dim != DimBuffer) + arg_str += ", " + to_sampler_expression(var_id ? var_id : id); + + // Add sampler Y'CbCr conversion info if we have it + if (is_dynamic_img_sampler && constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + { + SmallVector samp_args; + + switch (constexpr_sampler->resolution) + { + case MSL_FORMAT_RESOLUTION_444: + // Default + break; + case MSL_FORMAT_RESOLUTION_422: + samp_args.push_back("spvFormatResolution::_422"); + break; + case MSL_FORMAT_RESOLUTION_420: + samp_args.push_back("spvFormatResolution::_420"); + break; + default: + SPIRV_CROSS_THROW("Invalid format resolution."); + } + + if (constexpr_sampler->chroma_filter != MSL_SAMPLER_FILTER_NEAREST) + samp_args.push_back("spvChromaFilter::linear"); + + if (constexpr_sampler->x_chroma_offset != MSL_CHROMA_LOCATION_COSITED_EVEN) + samp_args.push_back("spvXChromaLocation::midpoint"); + if (constexpr_sampler->y_chroma_offset != MSL_CHROMA_LOCATION_COSITED_EVEN) + samp_args.push_back("spvYChromaLocation::midpoint"); + switch (constexpr_sampler->ycbcr_model) + { + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY: + // Default + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_identity"); + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_bt_709"); + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_bt_601"); + break; + case MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020: + samp_args.push_back("spvYCbCrModelConversion::ycbcr_bt_2020"); + break; + default: + SPIRV_CROSS_THROW("Invalid Y'CbCr model conversion."); + } + if (constexpr_sampler->ycbcr_range != MSL_SAMPLER_YCBCR_RANGE_ITU_FULL) + samp_args.push_back("spvYCbCrRange::itu_narrow"); + samp_args.push_back(join("spvComponentBits(", constexpr_sampler->bpc, ")")); + arg_str += join(", spvYCbCrSampler(", merge(samp_args), ")"); + } + } + + if (is_dynamic_img_sampler && constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + arg_str += join(", (uint(", create_swizzle(constexpr_sampler->swizzle[3]), ") << 24) | (uint(", + create_swizzle(constexpr_sampler->swizzle[2]), ") << 16) | (uint(", + create_swizzle(constexpr_sampler->swizzle[1]), ") << 8) | uint(", + create_swizzle(constexpr_sampler->swizzle[0]), ")"); + else if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(type)) + arg_str += ", " + to_swizzle_expression(var_id ? var_id : id); + + if (buffer_requires_array_length(var_id)) + arg_str += ", " + to_buffer_size_expression(var_id ? var_id : id); + + if (is_dynamic_img_sampler) + arg_str += ")"; + } + + // Emulate texture2D atomic operations + auto *backing_var = maybe_get_backing_variable(var_id); + if (backing_var && atomic_image_vars_emulated.count(backing_var->self)) + { + arg_str += ", " + to_expression(var_id) + "_atomic"; + } + + return arg_str; +} + +// If the ID represents a sampled image that has been assigned a sampler already, +// generate an expression for the sampler, otherwise generate a fake sampler name +// by appending a suffix to the expression constructed from the ID. +string CompilerMSL::to_sampler_expression(uint32_t id) +{ + auto *combined = maybe_get(id); + if (combined && combined->sampler) + return to_expression(combined->sampler); + + uint32_t expr_id = combined ? uint32_t(combined->image) : id; + + // Constexpr samplers are declared as local variables, + // so exclude any qualifier names on the image expression. + if (auto *var = maybe_get_backing_variable(expr_id)) + { + uint32_t img_id = var->basevariable ? var->basevariable : VariableID(var->self); + if (find_constexpr_sampler(img_id)) + return Compiler::to_name(img_id) + sampler_name_suffix; + } + + auto img_expr = to_expression(expr_id); + auto index = img_expr.find_first_of('['); + if (index == string::npos) + return img_expr + sampler_name_suffix; + else + return img_expr.substr(0, index) + sampler_name_suffix + img_expr.substr(index); +} + +string CompilerMSL::to_swizzle_expression(uint32_t id) +{ + auto *combined = maybe_get(id); + + auto expr = to_expression(combined ? combined->image : VariableID(id)); + auto index = expr.find_first_of('['); + + // If an image is part of an argument buffer translate this to a legal identifier. + string::size_type period = 0; + while ((period = expr.find_first_of('.', period)) != string::npos && period < index) + expr[period] = '_'; + + if (index == string::npos) + return expr + swizzle_name_suffix; + else + { + auto image_expr = expr.substr(0, index); + auto array_expr = expr.substr(index); + return image_expr + swizzle_name_suffix + array_expr; + } +} + +string CompilerMSL::to_buffer_size_expression(uint32_t id) +{ + auto expr = to_expression(id); + + // This is quite crude, but we need to translate the reference name (*spvDescriptorSetN.name) to + // the pointer expression spvDescriptorSetN.name to make a reasonable expression here. + // This only happens if we have argument buffers and we are using OpArrayLength on a lone SSBO in that set. + if (expr.size() >= 3 && expr[0] == '(' && expr[1] == '*') + expr = address_of_expression(expr); + + auto index = expr.find_first_of('['); + string buffer_expr, array_expr; + + if (index != string::npos) + { + buffer_expr = expr.substr(0, index); + array_expr = expr.substr(index); + } + + // If a buffer is part of an argument buffer translate this to a legal identifier. + for (auto &c : expr) + if (c == '.') + c = '_'; + + if (index == string::npos) + { + return expr + buffer_size_name_suffix; + } + else + { + if (auto var = maybe_get_backing_variable(id)) + { + if (is_var_runtime_size_array(*var)) + { + if (!msl_options.runtime_array_rich_descriptor) + SPIRV_CROSS_THROW("OpArrayLength requires rich descriptor format"); + + auto last_pos = array_expr.find_last_of(']'); + if (last_pos != std::string::npos) + return buffer_expr + ".length(" + array_expr.substr(1, last_pos - 1) + ")"; + } + } + + for (auto &c : buffer_expr) + if (c == '.') + c = '_'; + + return buffer_expr + buffer_size_name_suffix + array_expr; + } +} + +// Checks whether the type is a Block all of whose members have DecorationPatch. +bool CompilerMSL::is_patch_block(const SPIRType &type) +{ + if (!has_decoration(type.self, DecorationBlock)) + return false; + + for (uint32_t i = 0; i < type.member_types.size(); i++) + { + if (!has_member_decoration(type.self, i, DecorationPatch)) + return false; + } + + return true; +} + +// Checks whether the ID is a row_major matrix that requires conversion before use +bool CompilerMSL::is_non_native_row_major_matrix(uint32_t id) +{ + auto *e = maybe_get(id); + if (e) + return e->need_transpose; + else + return has_decoration(id, DecorationRowMajor); +} + +// Checks whether the member is a row_major matrix that requires conversion before use +bool CompilerMSL::member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) +{ + return has_member_decoration(type.self, index, DecorationRowMajor); +} + +string CompilerMSL::convert_row_major_matrix(string exp_str, const SPIRType &exp_type, uint32_t physical_type_id, + bool is_packed, bool relaxed) +{ + if (!is_matrix(exp_type)) + { + return CompilerGLSL::convert_row_major_matrix(std::move(exp_str), exp_type, physical_type_id, is_packed, relaxed); + } + else + { + strip_enclosed_expression(exp_str); + if (physical_type_id != 0 || is_packed) + exp_str = unpack_expression_type(exp_str, exp_type, physical_type_id, is_packed, true); + return join("transpose(", exp_str, ")"); + } +} + +// Called automatically at the end of the entry point function +void CompilerMSL::emit_fixup() +{ + if (stage_out_var_id && !capture_output_to_buffer) + { + if (needs_point_size_output && !writes_to_point_size) + statement(builtin_to_glsl(BuiltInPointSize, StorageClassOutput), " = ", format_float(msl_options.default_point_size), ";"); + + if (is_vertex_like_shader() && !qual_pos_var_name.empty()) + { + if (options.vertex.fixup_clipspace) + statement(qual_pos_var_name, ".z = (", qual_pos_var_name, ".z + ", qual_pos_var_name, + ".w) * 0.5; // Adjust clip-space for Metal"); + + if (options.vertex.flip_vert_y) + statement(qual_pos_var_name, ".y = -(", qual_pos_var_name, ".y);", " // Invert Y-axis for Metal"); + } + } +} + +// Return a string defining a structure member, with padding and packing. +string CompilerMSL::to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier) +{ + uint32_t orig_member_type_id = member_type_id; + if (member_is_remapped_physical_type(type, index)) + member_type_id = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypeID); + auto &physical_type = get(member_type_id); + + // If this member is packed, mark it as so. + string pack_pfx; + + // Allow Metal to use the array template to make arrays a value type + uint32_t orig_id = 0; + if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID)) + orig_id = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID); + + bool row_major = false; + if (is_matrix(physical_type)) + row_major = has_member_decoration(type.self, index, DecorationRowMajor); + + SPIRType row_major_physical_type { OpTypeMatrix }; + const SPIRType *declared_type = &physical_type; + + // If a struct is being declared with physical layout, + // do not use array wrappers. + // This avoids a lot of complicated cases with packed vectors and matrices, + // and generally we cannot copy full arrays in and out of buffers into Function + // address space. + // Array of resources should also be declared as builtin arrays. + if (has_member_decoration(type.self, index, DecorationOffset)) + is_using_builtin_array = true; + else if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationResourceIndexPrimary)) + is_using_builtin_array = true; + + if (member_is_packed_physical_type(type, index)) + { + // If we're packing a matrix, output an appropriate typedef + if (physical_type.basetype == SPIRType::Struct) + { + SPIRV_CROSS_THROW("Cannot emit a packed struct currently."); + } + else if (is_matrix(physical_type)) + { + uint32_t rows = physical_type.vecsize; + uint32_t cols = physical_type.columns; + pack_pfx = "packed_"; + if (row_major) + { + // These are stored transposed. + rows = physical_type.columns; + cols = physical_type.vecsize; + pack_pfx = "packed_rm_"; + } + string base_type = physical_type.width == 16 ? "half" : "float"; + string td_line = "typedef "; + td_line += "packed_" + base_type + to_string(rows); + td_line += " " + pack_pfx; + // Use the actual matrix size here. + td_line += base_type + to_string(physical_type.columns) + "x" + to_string(physical_type.vecsize); + td_line += "[" + to_string(cols) + "]"; + td_line += ";"; + add_typedef_line(td_line); + } + else if (!is_scalar(physical_type)) // scalar type is already packed. + pack_pfx = "packed_"; + } + else if (is_matrix(physical_type)) + { + if (!msl_options.supports_msl_version(3, 0) && + has_extended_decoration(type.self, SPIRVCrossDecorationWorkgroupStruct)) + { + pack_pfx = "spvStorage_"; + add_spv_func_and_recompile(SPVFuncImplStorageMatrix); + // The pack prefix causes problems with array wrappers. + is_using_builtin_array = true; + } + if (row_major) + { + // Need to declare type with flipped vecsize/columns. + row_major_physical_type = physical_type; + swap(row_major_physical_type.vecsize, row_major_physical_type.columns); + declared_type = &row_major_physical_type; + } + } + + // iOS Tier 1 argument buffers do not support writable images. + if (physical_type.basetype == SPIRType::Image && + physical_type.image.sampled == 2 && + msl_options.is_ios() && + msl_options.argument_buffers_tier <= Options::ArgumentBuffersTier::Tier1 && + !has_decoration(orig_id, DecorationNonWritable)) + { + SPIRV_CROSS_THROW("Writable images are not allowed on Tier1 argument buffers on iOS."); + } + + // Array information is baked into these types. + string array_type; + if (physical_type.basetype != SPIRType::Image && physical_type.basetype != SPIRType::Sampler && + physical_type.basetype != SPIRType::SampledImage) + { + BuiltIn builtin = BuiltInMax; + + // Special handling. In [[stage_out]] or [[stage_in]] blocks, + // we need flat arrays, but if we're somehow declaring gl_PerVertex for constant array reasons, we want + // template array types to be declared. + bool is_ib_in_out = + ((stage_out_var_id && get_stage_out_struct_type().self == type.self && + variable_storage_requires_stage_io(StorageClassOutput)) || + (stage_in_var_id && get_stage_in_struct_type().self == type.self && + variable_storage_requires_stage_io(StorageClassInput))) || + is_mesh_shader(); + if (is_ib_in_out && is_member_builtin(type, index, &builtin)) + is_using_builtin_array = true; + array_type = type_to_array_glsl(physical_type, orig_id); + } + + if (is_mesh_shader()) + { + BuiltIn builtin = BuiltInMax; + if (is_member_builtin(type, index, &builtin)) + { + if (builtin == BuiltInPrimitiveShadingRateKHR) + { + // not supported in metal 3.0 + is_using_builtin_array = false; + return ""; + } + + SPIRType metallic_type = *declared_type; + if (builtin == BuiltInCullPrimitiveEXT) + metallic_type.basetype = SPIRType::Boolean; + else if (builtin == BuiltInPrimitiveId || builtin == BuiltInLayer || builtin == BuiltInViewportIndex) + metallic_type.basetype = SPIRType::UInt; + + is_using_builtin_array = true; + std::string result; + if (has_member_decoration(type.self, orig_id, DecorationBuiltIn)) + { + // avoid '_RESERVED_IDENTIFIER_FIXUP_' in variable name + result = join(type_to_glsl(metallic_type, orig_id, false), " ", qualifier, + builtin_to_glsl(builtin, StorageClassOutput), member_attribute_qualifier(type, index), + array_type, ";"); + } + else + { + result = join(type_to_glsl(metallic_type, orig_id, false), " ", qualifier, + to_member_name(type, index), member_attribute_qualifier(type, index), array_type, ";"); + } + is_using_builtin_array = false; + return result; + } + } + + if (orig_id) + { + auto *data_type = declared_type; + if (is_pointer(*data_type)) + data_type = &get_pointee_type(*data_type); + + if (is_array(*data_type) && get_resource_array_size(*data_type, orig_id) == 0) + { + // Hack for declaring unsized array of resources. Need to declare dummy sized array by value inline. + // This can then be wrapped in spvDescriptorArray as usual. + array_type = "[1] /* unsized array hack */"; + } + } + + string decl_type; + if (declared_type->vecsize > 4) + { + auto orig_type = get(orig_member_type_id); + if (is_matrix(orig_type) && row_major) + swap(orig_type.vecsize, orig_type.columns); + orig_type.columns = 1; + decl_type = type_to_glsl(orig_type, orig_id, true); + + if (declared_type->columns > 1) + decl_type = join("spvPaddedStd140Matrix<", decl_type, ", ", declared_type->columns, ">"); + else + decl_type = join("spvPaddedStd140<", decl_type, ">"); + } + else + decl_type = type_to_glsl(*declared_type, orig_id, true); + + if (physical_type.basetype == SPIRType::Struct && + has_decoration(physical_type.self, DecorationArrayStride) && + is_array(physical_type)) + { + uint32_t native_stride = get_decoration(physical_type.self, DecorationArrayStride); + uint32_t array_stride = get_decoration(type.member_types[index], DecorationArrayStride); + auto *struct_array_type = &physical_type; + + while (struct_array_type->parent_type && is_array(get(struct_array_type->parent_type))) + { + array_stride = get_decoration(struct_array_type->parent_type, DecorationArrayStride); + struct_array_type = &get(struct_array_type->parent_type); + } + + if (array_stride != native_stride) + decl_type = join("spvPaddedArrayElement<", decl_type, ", ", array_stride, ">"); + } + + const char *overlapping_binding_tag = + has_extended_member_decoration(type.self, index, SPIRVCrossDecorationOverlappingBinding) ? + "// Overlapping binding: " : ""; + + auto result = join(overlapping_binding_tag, pack_pfx, decl_type, " ", qualifier, + to_member_name(type, index), member_attribute_qualifier(type, index), array_type, ";"); + + is_using_builtin_array = false; + return result; +} + +// Emit a structure member, padding and packing to maintain the correct memeber alignments. +void CompilerMSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const string &qualifier, uint32_t) +{ + // If this member requires padding to maintain its declared offset, emit a dummy padding member before it. + if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationPaddingTarget)) + { + uint32_t pad_len = get_extended_member_decoration(type.self, index, SPIRVCrossDecorationPaddingTarget); + statement("char _m", index, "_pad", "[", pad_len, "];"); + } + + BuiltIn builtin = BuiltInMax; + if (is_mesh_shader() && is_member_builtin(type, index, &builtin)) + { + if (!has_active_builtin(builtin, StorageClassOutput) && !has_active_builtin(builtin, StorageClassInput)) + { + // Do not emit unused builtins in mesh-output blocks + return; + } + } + + // Handle HLSL-style 0-based vertex/instance index. + builtin_declaration = true; + statement(to_struct_member(type, member_type_id, index, qualifier)); + builtin_declaration = false; +} + +// Return a MSL qualifier for the specified function attribute member +string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t index) +{ + auto &execution = get_entry_point(); + + uint32_t mbr_type_id = type.member_types[index]; + auto &mbr_type = get(mbr_type_id); + + BuiltIn builtin = BuiltInMax; + bool is_builtin = is_member_builtin(type, index, &builtin); + + if (has_extended_member_decoration(type.self, index, SPIRVCrossDecorationResourceIndexPrimary)) + { + string quals = join( + " [[id(", get_extended_member_decoration(type.self, index, SPIRVCrossDecorationResourceIndexPrimary), ")"); + if (interlocked_resources.count( + get_extended_member_decoration(type.self, index, SPIRVCrossDecorationInterfaceOrigID))) + quals += ", raster_order_group(0)"; + quals += "]]"; + return quals; + } + + // Vertex function inputs + if (execution.model == ExecutionModelVertex && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInBaseVertex: + case BuiltInInstanceId: + case BuiltInInstanceIndex: + case BuiltInBaseInstance: + if (msl_options.vertex_for_tessellation) + return ""; + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + default: + return ""; + } + } + + uint32_t locn; + if (is_builtin) + locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index); + else + locn = get_member_location(type.self, index); + + if (locn != k_unknown_location) + return string(" [[attribute(") + convert_to_string(locn) + ")]]"; + } + + bool use_semantic_stage_output = is_mesh_shader() || is_tese_shader() || + (execution.model == ExecutionModelVertex && !msl_options.vertex_for_tessellation); + + // Vertex, mesh and tessellation evaluation function outputs + if ((type.storage == StorageClassOutput || is_mesh_shader()) && use_semantic_stage_output) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInPointSize: + // Only mark the PointSize builtin if really rendering points. + // Some shaders may include a PointSize builtin even when used to render + // non-point topologies, and Metal will reject this builtin when compiling + // the shader into a render pipeline that uses a non-point topology. + return msl_options.enable_point_size_builtin ? (string(" [[") + builtin_qualifier(builtin) + "]]") : ""; + + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + /* fallthrough */ + case BuiltInPosition: + case BuiltInLayer: + case BuiltInCullPrimitiveEXT: + case BuiltInPrimitiveShadingRateKHR: + case BuiltInPrimitiveId: + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + + case BuiltInClipDistance: + if (has_member_decoration(type.self, index, DecorationIndex)) + return join(" [[user(clip", get_member_decoration(type.self, index, DecorationIndex), ")]]"); + else + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + + case BuiltInCullDistance: + if (has_member_decoration(type.self, index, DecorationIndex)) + return join(" [[user(cull", get_member_decoration(type.self, index, DecorationIndex), ")]]"); + else + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + + default: + return ""; + } + } + string loc_qual = member_location_attribute_qualifier(type, index); + if (!loc_qual.empty()) + return join(" [[", loc_qual, "]]"); + } + + if (execution.model == ExecutionModelVertex && msl_options.vertex_for_tessellation && type.storage == StorageClassOutput) + { + // For this type of shader, we always arrange for it to capture its + // output to a buffer. For this reason, qualifiers are irrelevant here. + if (is_builtin) + // We still have to assign a location so the output struct will sort correctly. + get_or_allocate_builtin_output_member_location(builtin, type.self, index); + return ""; + } + + // Tessellation control function inputs + if (is_tesc_shader() && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInInvocationId: + case BuiltInPrimitiveId: + if (msl_options.multi_patch_workgroup) + return ""; + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + case BuiltInSubgroupLocalInvocationId: // FIXME: Should work in any stage + case BuiltInSubgroupSize: // FIXME: Should work in any stage + if (msl_options.emulate_subgroups) + return ""; + return string(" [[") + builtin_qualifier(builtin) + "]]" + (mbr_type.array.empty() ? "" : " "); + case BuiltInPatchVertices: + return ""; + // Others come from stage input. + default: + break; + } + } + if (msl_options.multi_patch_workgroup) + return ""; + + uint32_t locn; + if (is_builtin) + locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index); + else + locn = get_member_location(type.self, index); + + if (locn != k_unknown_location) + return string(" [[attribute(") + convert_to_string(locn) + ")]]"; + } + + // Tessellation control function outputs + if (is_tesc_shader() && type.storage == StorageClassOutput) + { + // For this type of shader, we always arrange for it to capture its + // output to a buffer. For this reason, qualifiers are irrelevant here. + if (is_builtin) + // We still have to assign a location so the output struct will sort correctly. + get_or_allocate_builtin_output_member_location(builtin, type.self, index); + return ""; + } + + // Tessellation evaluation function inputs + if (is_tese_shader() && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInPrimitiveId: + case BuiltInTessCoord: + return string(" [[") + builtin_qualifier(builtin) + "]]"; + case BuiltInPatchVertices: + return ""; + // Others come from stage input. + default: + break; + } + } + + if (msl_options.raw_buffer_tese_input) + return ""; + + // The special control point array must not be marked with an attribute. + if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray) + return ""; + + uint32_t locn; + if (is_builtin) + locn = get_or_allocate_builtin_input_member_location(builtin, type.self, index); + else + locn = get_member_location(type.self, index); + + if (locn != k_unknown_location) + return string(" [[attribute(") + convert_to_string(locn) + ")]]"; + } + + // Tessellation evaluation function outputs were handled above. + + // Fragment function inputs + if (execution.model == ExecutionModelFragment && type.storage == StorageClassInput) + { + string quals; + if (is_builtin) + { + switch (builtin) + { + case BuiltInViewIndex: + if (!msl_options.multiview || !msl_options.multiview_layered_rendering) + break; + /* fallthrough */ + case BuiltInFrontFacing: + case BuiltInPointCoord: + case BuiltInFragCoord: + case BuiltInSampleId: + case BuiltInSampleMask: + case BuiltInLayer: + case BuiltInBaryCoordKHR: + case BuiltInBaryCoordNoPerspKHR: + quals = builtin_qualifier(builtin); + break; + + case BuiltInClipDistance: + return join(" [[user(clip", get_member_decoration(type.self, index, DecorationIndex), ")]]"); + case BuiltInCullDistance: + return join(" [[user(cull", get_member_decoration(type.self, index, DecorationIndex), ")]]"); + + default: + break; + } + } + else + quals = member_location_attribute_qualifier(type, index); + + if (builtin == BuiltInBaryCoordKHR && has_member_decoration(type.self, index, DecorationNoPerspective)) + { + // NoPerspective is baked into the builtin type. + SPIRV_CROSS_THROW("NoPerspective decorations are not supported for BaryCoord inputs."); + } + + // Don't bother decorating integers with the 'flat' attribute; it's + // the default (in fact, the only option). Also don't bother with the + // FragCoord builtin; it's always noperspective on Metal. + if (!type_is_integral(mbr_type) && (!is_builtin || builtin != BuiltInFragCoord)) + { + if (has_member_decoration(type.self, index, DecorationFlat)) + { + if (!quals.empty()) + quals += ", "; + quals += "flat"; + } + else if (has_member_decoration(type.self, index, DecorationCentroid)) + { + if (!quals.empty()) + quals += ", "; + + if (builtin == BuiltInBaryCoordNoPerspKHR || builtin == BuiltInBaryCoordKHR) + SPIRV_CROSS_THROW("Centroid interpolation not supported for barycentrics in MSL."); + + if (has_member_decoration(type.self, index, DecorationNoPerspective)) + quals += "centroid_no_perspective"; + else + quals += "centroid_perspective"; + } + else if (has_member_decoration(type.self, index, DecorationSample)) + { + if (!quals.empty()) + quals += ", "; + + if (builtin == BuiltInBaryCoordNoPerspKHR || builtin == BuiltInBaryCoordKHR) + SPIRV_CROSS_THROW("Sample interpolation not supported for barycentrics in MSL."); + + if (has_member_decoration(type.self, index, DecorationNoPerspective)) + quals += "sample_no_perspective"; + else + quals += "sample_perspective"; + } + else if (has_member_decoration(type.self, index, DecorationNoPerspective) || builtin == BuiltInBaryCoordNoPerspKHR) + { + if (!quals.empty()) + quals += ", "; + quals += "center_no_perspective"; + } + else if (builtin == BuiltInBaryCoordKHR) + { + if (!quals.empty()) + quals += ", "; + quals += "center_perspective"; + } + } + + if (!quals.empty()) + return " [[" + quals + "]]"; + } + + // Fragment function outputs + if (execution.model == ExecutionModelFragment && type.storage == StorageClassOutput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInFragStencilRefEXT: + // Similar to PointSize, only mark FragStencilRef if there's a stencil buffer. + // Some shaders may include a FragStencilRef builtin even when used to render + // without a stencil attachment, and Metal will reject this builtin + // when compiling the shader into a render pipeline that does not set + // stencilAttachmentPixelFormat. + if (!msl_options.enable_frag_stencil_ref_builtin) + return ""; + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Stencil export only supported in MSL 2.1 and up."); + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + case BuiltInFragDepth: + // Ditto FragDepth. + if (!msl_options.enable_frag_depth_builtin) + return ""; + /* fallthrough */ + case BuiltInSampleMask: + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + default: + return ""; + } + } + uint32_t locn = get_member_location(type.self, index); + // Metal will likely complain about missing color attachments, too. + if (locn != k_unknown_location && !(msl_options.enable_frag_output_mask & (1 << locn))) + return ""; + if (locn != k_unknown_location && has_member_decoration(type.self, index, DecorationIndex)) + return join(" [[color(", locn, "), index(", get_member_decoration(type.self, index, DecorationIndex), + ")]]"); + else if (locn != k_unknown_location) + return join(" [[color(", locn, ")]]"); + else if (has_member_decoration(type.self, index, DecorationIndex)) + return join(" [[index(", get_member_decoration(type.self, index, DecorationIndex), ")]]"); + else + return ""; + } + + // Compute function inputs + if (execution.model == ExecutionModelGLCompute && type.storage == StorageClassInput) + { + if (is_builtin) + { + switch (builtin) + { + case BuiltInNumSubgroups: + case BuiltInSubgroupId: + case BuiltInSubgroupLocalInvocationId: // FIXME: Should work in any stage + case BuiltInSubgroupSize: // FIXME: Should work in any stage + if (msl_options.emulate_subgroups) + break; + /* fallthrough */ + case BuiltInGlobalInvocationId: + case BuiltInWorkgroupId: + case BuiltInNumWorkgroups: + case BuiltInLocalInvocationId: + case BuiltInLocalInvocationIndex: + return string(" [[") + builtin_qualifier(builtin) + "]]"; + + default: + return ""; + } + } + } + + return ""; +} + +// A user-defined output variable is considered to match an input variable in the subsequent +// stage if the two variables are declared with the same Location and Component decoration and +// match in type and decoration, except that interpolation decorations are not required to match. +// For the purposes of interface matching, variables declared without a Component decoration are +// considered to have a Component decoration of zero. +string CompilerMSL::member_location_attribute_qualifier(const SPIRType &type, uint32_t index) +{ + string quals; + uint32_t comp; + uint32_t locn = get_member_location(type.self, index, &comp); + if (locn != k_unknown_location) + { + quals += "user(locn"; + quals += convert_to_string(locn); + if (comp != k_unknown_component && comp != 0) + { + quals += "_"; + quals += convert_to_string(comp); + } + quals += ")"; + } + return quals; +} + +// Returns the location decoration of the member with the specified index in the specified type. +// If the location of the member has been explicitly set, that location is used. If not, this +// function assumes the members are ordered in their location order, and simply returns the +// index as the location. +uint32_t CompilerMSL::get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp) const +{ + if (comp) + { + if (has_member_decoration(type_id, index, DecorationComponent)) + *comp = get_member_decoration(type_id, index, DecorationComponent); + else + *comp = k_unknown_component; + } + + if (has_member_decoration(type_id, index, DecorationLocation)) + return get_member_decoration(type_id, index, DecorationLocation); + else + return k_unknown_location; +} + +uint32_t CompilerMSL::get_or_allocate_builtin_input_member_location(BuiltIn builtin, + uint32_t type_id, uint32_t index, + uint32_t *comp) +{ + uint32_t loc = get_member_location(type_id, index, comp); + if (loc != k_unknown_location) + return loc; + + if (comp) + *comp = k_unknown_component; + + // Late allocation. Find a location which is unused by the application. + // This can happen for built-in inputs in tessellation which are mixed and matched with user inputs. + auto &mbr_type = get(get(type_id).member_types[index]); + uint32_t count = type_to_location_count(mbr_type); + + loc = 0; + + const auto location_range_in_use = [this](uint32_t location, uint32_t location_count) -> bool { + for (uint32_t i = 0; i < location_count; i++) + if (location_inputs_in_use.count(location + i) != 0) + return true; + return false; + }; + + while (location_range_in_use(loc, count)) + loc++; + + set_member_decoration(type_id, index, DecorationLocation, loc); + + // Triangle tess level inputs are shared in one packed float4, + // mark both builtins as sharing one location. + if (!msl_options.raw_buffer_tese_input && is_tessellating_triangles() && + (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)) + { + builtin_to_automatic_input_location[BuiltInTessLevelInner] = loc; + builtin_to_automatic_input_location[BuiltInTessLevelOuter] = loc; + } + else + builtin_to_automatic_input_location[builtin] = loc; + + mark_location_as_used_by_shader(loc, mbr_type, StorageClassInput, true); + return loc; +} + +uint32_t CompilerMSL::get_or_allocate_builtin_output_member_location(BuiltIn builtin, + uint32_t type_id, uint32_t index, + uint32_t *comp) +{ + uint32_t loc = get_member_location(type_id, index, comp); + if (loc != k_unknown_location) + return loc; + loc = 0; + + if (comp) + *comp = k_unknown_component; + + // Late allocation. Find a location which is unused by the application. + // This can happen for built-in outputs in tessellation which are mixed and matched with user inputs. + auto &mbr_type = get(get(type_id).member_types[index]); + uint32_t count = type_to_location_count(mbr_type); + + const auto location_range_in_use = [this](uint32_t location, uint32_t location_count) -> bool { + for (uint32_t i = 0; i < location_count; i++) + if (location_outputs_in_use.count(location + i) != 0) + return true; + return false; + }; + + while (location_range_in_use(loc, count)) + loc++; + + set_member_decoration(type_id, index, DecorationLocation, loc); + + // Triangle tess level inputs are shared in one packed float4; + // mark both builtins as sharing one location. + if (is_tessellating_triangles() && (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)) + { + builtin_to_automatic_output_location[BuiltInTessLevelInner] = loc; + builtin_to_automatic_output_location[BuiltInTessLevelOuter] = loc; + } + else + builtin_to_automatic_output_location[builtin] = loc; + + mark_location_as_used_by_shader(loc, mbr_type, StorageClassOutput, true); + return loc; +} + +bool CompilerMSL::entry_point_is_vertex() const +{ + // MSL vertex entrypoint is used for non-tessellation vertex stage or tessellation evaluation stage. + return (get_execution_model() == ExecutionModelVertex && !msl_options.vertex_for_tessellation) || + get_execution_model() == ExecutionModelTessellationEvaluation; +} + +bool CompilerMSL::entry_point_returns_stage_output() const +{ + if (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation) + return false; + bool ep_should_return_output = !get_is_rasterization_disabled(); + return stage_out_var_id && ep_should_return_output; +} + +bool CompilerMSL::entry_point_requires_const_device_buffers() const +{ + return !has_descriptor_side_effects_buffer && !capture_output_to_buffer; +} + +// Returns the type declaration for a function, including the +// entry type if the current function is the entry point function +string CompilerMSL::func_type_decl(SPIRType &type) +{ + // The regular function return type. If not processing the entry point function, that's all we need + string return_type = type_to_glsl(type) + type_to_array_glsl(type, 0); + if (!processing_entry_point) + return return_type; + + // If an outgoing interface block has been defined, and it should be returned, override the entry point return type + if (entry_point_returns_stage_output()) + return_type = type_to_glsl(get_stage_out_struct_type()) + type_to_array_glsl(type, 0); + + // Prepend a entry type, based on the execution model + string entry_type; + auto &execution = get_entry_point(); + switch (execution.model) + { + case ExecutionModelVertex: + if (msl_options.vertex_for_tessellation && !msl_options.supports_msl_version(1, 2)) + SPIRV_CROSS_THROW("Tessellation requires Metal 1.2."); + entry_type = msl_options.vertex_for_tessellation ? "kernel" : "vertex"; + break; + case ExecutionModelTessellationEvaluation: + if (!msl_options.supports_msl_version(1, 2)) + SPIRV_CROSS_THROW("Tessellation requires Metal 1.2."); + if (execution.flags.get(ExecutionModeIsolines)) + SPIRV_CROSS_THROW("Metal does not support isoline tessellation."); + if (msl_options.is_ios()) + entry_type = join("[[ patch(", is_tessellating_triangles() ? "triangle" : "quad", ") ]] vertex"); + else + entry_type = join("[[ patch(", is_tessellating_triangles() ? "triangle" : "quad", ", ", + execution.output_vertices, ") ]] vertex"); + break; + case ExecutionModelFragment: + entry_type = uses_explicit_early_fragment_test() ? "[[ early_fragment_tests ]] fragment" : "fragment"; + break; + case ExecutionModelTessellationControl: + if (!msl_options.supports_msl_version(1, 2)) + SPIRV_CROSS_THROW("Tessellation requires Metal 1.2."); + if (execution.flags.get(ExecutionModeIsolines)) + SPIRV_CROSS_THROW("Metal does not support isoline tessellation."); + /* fallthrough */ + case ExecutionModelGLCompute: + case ExecutionModelKernel: + entry_type = "kernel"; + break; + case ExecutionModelMeshEXT: + entry_type = "[[mesh]]"; + break; + case ExecutionModelTaskEXT: + entry_type = "[[object]]"; + break; + default: + entry_type = "unknown"; + break; + } + + return entry_type + " " + return_type; +} + +bool CompilerMSL::is_tesc_shader() const +{ + return get_execution_model() == ExecutionModelTessellationControl; +} + +bool CompilerMSL::is_tese_shader() const +{ + return get_execution_model() == ExecutionModelTessellationEvaluation; +} + +bool CompilerMSL::is_mesh_shader() const +{ + return get_execution_model() == ExecutionModelMeshEXT; +} + +bool CompilerMSL::uses_explicit_early_fragment_test() +{ + auto &ep_flags = get_entry_point().flags; + return ep_flags.get(ExecutionModeEarlyFragmentTests) || ep_flags.get(ExecutionModePostDepthCoverage); +} + +// In MSL, address space qualifiers are required for all pointer or reference variables +string CompilerMSL::get_variable_address_space(const SPIRVariable &argument) +{ + const auto &type = get(argument.basetype); + return get_type_address_space(type, argument.self, true); +} + +string CompilerMSL::get_leaf_argument_address_space(const SPIRVariable &argument) +{ + const auto &type = get(argument.basetype); + // BDA and variable buffer pointer is always passed around by (pointer) value. There is no storage class for the argument itself. + if (is_physical_or_buffer_pointer(type)) + return ""; + return get_type_address_space(type, argument.self, true); +} + +bool CompilerMSL::decoration_flags_signal_volatile(const Bitset &flags) const +{ + // Using volatile for coherent pre-3.2 is definitely not correct, but it's something. + // MSL 3.2 adds actual coherent qualifiers. + return flags.get(DecorationVolatile) || + (flags.get(DecorationCoherent) && !msl_options.supports_msl_version(3, 2)); +} + +bool CompilerMSL::decoration_flags_signal_coherent(const Bitset &flags) const +{ + return flags.get(DecorationCoherent) && msl_options.supports_msl_version(3, 2); +} + +string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id, bool argument) +{ + // This can be called for variable pointer contexts as well, so be very careful about which method we choose. + Bitset flags; + auto *var = maybe_get(id); + if (var && type.basetype == SPIRType::Struct && + (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))) + flags = get_buffer_block_flags(id); + else + { + flags = get_decoration_bitset(id); + + if (type.basetype == SPIRType::Struct && + (has_decoration(type.self, DecorationBlock) || + has_decoration(type.self, DecorationBufferBlock))) + { + flags.merge_or(ir.get_buffer_block_type_flags(type)); + } + } + + const char *addr_space = nullptr; + switch (type.storage) + { + case StorageClassWorkgroup: + addr_space = "threadgroup"; + break; + + case StorageClassStorageBuffer: + case StorageClassPhysicalStorageBuffer: + { + // When dealing with descriptor aliasing, it becomes very problematic to make use of + // readonly qualifiers. + // If rasterization is not disabled in vertex/tese, Metal does not allow side effects and refuses to compile "device", + // even if there are no writes. Just force const device. + if (entry_point_requires_const_device_buffers() && type.basetype != SPIRType::AtomicCounter) + addr_space = "const device"; + else + addr_space = "device"; + break; + } + + case StorageClassUniform: + case StorageClassUniformConstant: + case StorageClassPushConstant: + if (type.basetype == SPIRType::Struct) + { + bool ssbo = has_decoration(type.self, DecorationBufferBlock); + if (ssbo) + { + if (entry_point_requires_const_device_buffers() && type.basetype != SPIRType::AtomicCounter) + addr_space = "const device"; + else + addr_space = "device"; + } + else + addr_space = "constant"; + } + else if (!argument) + { + // This is used for helper UBOs we insert ourselves. + addr_space = "constant"; + } + else if (type_is_msl_framebuffer_fetch(type)) + { + // Subpass inputs are passed around by value. + addr_space = ""; + } + + break; + + case StorageClassFunction: + case StorageClassGeneric: + break; + + case StorageClassInput: + if (is_tesc_shader() && var && var->basevariable == stage_in_ptr_var_id) + addr_space = msl_options.multi_patch_workgroup ? "const device" : "threadgroup"; + // Don't pass tessellation levels in the device AS; we load and convert them + // to float manually. + if (is_tese_shader() && msl_options.raw_buffer_tese_input && var) + { + bool is_stage_in = var->basevariable == stage_in_ptr_var_id; + bool is_patch_stage_in = has_decoration(var->self, DecorationPatch); + bool is_builtin = has_decoration(var->self, DecorationBuiltIn); + BuiltIn builtin = (BuiltIn)get_decoration(var->self, DecorationBuiltIn); + bool is_tess_level = is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner); + if (is_stage_in || (is_patch_stage_in && !is_tess_level)) + addr_space = "const device"; + } + if (get_execution_model() == ExecutionModelFragment && var && var->basevariable == stage_in_var_id) + addr_space = "thread"; + break; + + case StorageClassOutput: + if (capture_output_to_buffer) + { + if (var && type.storage == StorageClassOutput) + { + bool is_masked = is_stage_output_variable_masked(*var); + + if (is_masked) + { + if (is_tessellation_shader()) + addr_space = "threadgroup"; + else + addr_space = "thread"; + } + else if (variable_decl_is_remapped_storage(*var, StorageClassWorkgroup)) + addr_space = "threadgroup"; + } + + // BlockIO is passed as thread and lowered on return from main. + if (get_execution_model() == ExecutionModelVertex && has_decoration(type.self, DecorationBlock)) + addr_space = "thread"; + + if (!addr_space) + addr_space = "device"; + } + + if (is_mesh_shader()) + addr_space = "threadgroup"; + break; + + case StorageClassTaskPayloadWorkgroupEXT: + if (is_mesh_shader()) + addr_space = "const object_data"; + else + addr_space = "object_data"; + break; + + default: + break; + } + + if (!addr_space && var && is_var_runtime_size_array(*var)) + addr_space = "device"; + + if (!addr_space) + { + // No address space for plain values. + addr_space = type.pointer || (argument && type.basetype == SPIRType::ControlPointArray) ? "thread" : ""; + } + + if (decoration_flags_signal_coherent(flags) && strcmp(addr_space, "device") == 0) + return join("coherent device"); + else if (decoration_flags_signal_volatile(flags) && strcmp(addr_space, "thread") != 0) + return join("volatile ", addr_space); + else + return addr_space; +} + +const char *CompilerMSL::to_restrict(uint32_t id, bool space) +{ + // This can be called for variable pointer contexts as well, so be very careful about which method we choose. + Bitset flags; + if (ir.ids[id].get_type() == TypeVariable) + { + uint32_t type_id = expression_type_id(id); + auto &type = expression_type(id); + if (type.basetype == SPIRType::Struct && + (has_decoration(type_id, DecorationBlock) || has_decoration(type_id, DecorationBufferBlock))) + flags = get_buffer_block_flags(id); + else + flags = get_decoration_bitset(id); + } + else + flags = get_decoration_bitset(id); + + return flags.get(DecorationRestrict) || flags.get(DecorationRestrictPointerEXT) ? + (space ? "__restrict " : "__restrict") : ""; +} + +string CompilerMSL::entry_point_arg_stage_in() +{ + string decl; + + if ((is_tesc_shader() && msl_options.multi_patch_workgroup) || + (is_tese_shader() && msl_options.raw_buffer_tese_input)) + return decl; + + // Stage-in structure + uint32_t stage_in_id; + if (is_tese_shader()) + stage_in_id = patch_stage_in_var_id; + else + stage_in_id = stage_in_var_id; + + if (stage_in_id) + { + auto &var = get(stage_in_id); + auto &type = get_variable_data_type(var); + + add_resource_name(var.self); + decl = join(type_to_glsl(type), " ", to_name(var.self), " [[stage_in]]"); + } + + return decl; +} + +// Returns true if this input builtin should be a direct parameter on a shader function parameter list, +// and false for builtins that should be passed or calculated some other way. +bool CompilerMSL::is_direct_input_builtin(BuiltIn bi_type) +{ + switch (bi_type) + { + // Vertex function in + case BuiltInVertexId: + case BuiltInVertexIndex: + case BuiltInBaseVertex: + case BuiltInInstanceId: + case BuiltInInstanceIndex: + case BuiltInBaseInstance: + return get_execution_model() != ExecutionModelVertex || !msl_options.vertex_for_tessellation; + // Tess. control function in + case BuiltInPosition: + case BuiltInPointSize: + case BuiltInClipDistance: + case BuiltInCullDistance: + case BuiltInPatchVertices: + return false; + case BuiltInInvocationId: + case BuiltInPrimitiveId: + return !is_tesc_shader() || !msl_options.multi_patch_workgroup; + // Tess. evaluation function in + case BuiltInTessLevelInner: + case BuiltInTessLevelOuter: + return false; + // Fragment function in + case BuiltInSamplePosition: + case BuiltInHelperInvocation: + case BuiltInBaryCoordKHR: + case BuiltInBaryCoordNoPerspKHR: + return false; + case BuiltInViewIndex: + return get_execution_model() == ExecutionModelFragment && msl_options.multiview && + msl_options.multiview_layered_rendering; + // Compute function in + case BuiltInSubgroupId: + case BuiltInNumSubgroups: + return !msl_options.emulate_subgroups; + // Any stage function in + case BuiltInDeviceIndex: + case BuiltInSubgroupEqMask: + case BuiltInSubgroupGeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupLtMask: + return false; + case BuiltInSubgroupSize: + if (msl_options.fixed_subgroup_size != 0) + return false; + /* fallthrough */ + case BuiltInSubgroupLocalInvocationId: + return !msl_options.emulate_subgroups; + default: + return true; + } +} + +// Returns true if this is a fragment shader that runs per sample, and false otherwise. +bool CompilerMSL::is_sample_rate() const +{ + auto &caps = get_declared_capabilities(); + return get_execution_model() == ExecutionModelFragment && + (msl_options.force_sample_rate_shading || + std::find(caps.begin(), caps.end(), CapabilitySampleRateShading) != caps.end() || + (msl_options.use_framebuffer_fetch_subpasses && need_subpass_input_ms)); +} + +bool CompilerMSL::is_intersection_query() const +{ + auto &caps = get_declared_capabilities(); + return std::find(caps.begin(), caps.end(), CapabilityRayQueryKHR) != caps.end(); +} + +void CompilerMSL::entry_point_args_builtin(string &ep_args) +{ + // Builtin variables + SmallVector, 8> active_builtins; + ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { + if (var.storage != StorageClassInput) + return; + + auto bi_type = BuiltIn(get_decoration(var_id, DecorationBuiltIn)); + + // Don't emit SamplePosition as a separate parameter. In the entry + // point, we get that by calling get_sample_position() on the sample ID. + if (is_builtin_variable(var) && + get_variable_data_type(var).basetype != SPIRType::Struct && + get_variable_data_type(var).basetype != SPIRType::ControlPointArray) + { + // If the builtin is not part of the active input builtin set, don't emit it. + // Relevant for multiple entry-point modules which might declare unused builtins. + if (!active_input_builtins.get(bi_type) || !interface_variable_exists_in_entry_point(var_id)) + return; + + // Remember this variable. We may need to correct its type. + active_builtins.push_back(make_pair(&var, bi_type)); + + if (is_direct_input_builtin(bi_type)) + { + if (!ep_args.empty()) + ep_args += ", "; + + // Handle HLSL-style 0-based vertex/instance index. + builtin_declaration = true; + + // Handle different MSL gl_TessCoord types. (float2, float3) + if (bi_type == BuiltInTessCoord && get_entry_point().flags.get(ExecutionModeQuads)) + ep_args += "float2 " + to_expression(var_id) + "In"; + else + ep_args += builtin_type_decl(bi_type, var_id) + " " + to_expression(var_id); + + ep_args += string(" [[") + builtin_qualifier(bi_type); + if (bi_type == BuiltInSampleMask && get_entry_point().flags.get(ExecutionModePostDepthCoverage)) + { + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Post-depth coverage requires MSL 2.0."); + if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Post-depth coverage on Mac requires MSL 2.3."); + ep_args += ", post_depth_coverage"; + } + ep_args += "]]"; + builtin_declaration = false; + } + } + + if (has_extended_decoration(var_id, SPIRVCrossDecorationBuiltInDispatchBase)) + { + // This is a special implicit builtin, not corresponding to any SPIR-V builtin, + // which holds the base that was passed to vkCmdDispatchBase() or vkCmdDrawIndexed(). If it's present, + // assume we emitted it for a good reason. + assert(msl_options.supports_msl_version(1, 2)); + if (!ep_args.empty()) + ep_args += ", "; + + ep_args += type_to_glsl(get_variable_data_type(var)) + " " + to_expression(var_id) + " [[grid_origin]]"; + } + + if (has_extended_decoration(var_id, SPIRVCrossDecorationBuiltInStageInputSize)) + { + // This is another special implicit builtin, not corresponding to any SPIR-V builtin, + // which holds the number of vertices and instances to draw. If it's present, + // assume we emitted it for a good reason. + assert(msl_options.supports_msl_version(1, 2)); + if (!ep_args.empty()) + ep_args += ", "; + + ep_args += type_to_glsl(get_variable_data_type(var)) + " " + to_expression(var_id) + " [[grid_size]]"; + } + }); + + // Correct the types of all encountered active builtins. We couldn't do this before + // because ensure_correct_builtin_type() may increase the bound, which isn't allowed + // while iterating over IDs. + for (auto &var : active_builtins) + var.first->basetype = ensure_correct_builtin_type(var.first->basetype, var.second); + + // Handle HLSL-style 0-based vertex/instance index. + if (needs_base_vertex_arg == TriState::Yes) + ep_args += built_in_func_arg(BuiltInBaseVertex, !ep_args.empty()); + + if (needs_base_instance_arg == TriState::Yes) + ep_args += built_in_func_arg(BuiltInBaseInstance, !ep_args.empty()); + + if (capture_output_to_buffer) + { + // Add parameters to hold the indirect draw parameters and the shader output. This has to be handled + // specially because it needs to be a pointer, not a reference. + if (stage_out_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("device ", type_to_glsl(get_stage_out_struct_type()), "* ", output_buffer_var_name, + " [[buffer(", msl_options.shader_output_buffer_index, ")]]"); + } + + if (is_tesc_shader()) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("constant uint* spvIndirectParams [[buffer(", msl_options.indirect_params_buffer_index, ")]]"); + } + else if (stage_out_var_id && + !(get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation)) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("device uint* spvIndirectParams [[buffer(", msl_options.indirect_params_buffer_index, ")]]"); + } + + if (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation && + (active_input_builtins.get(BuiltInVertexIndex) || active_input_builtins.get(BuiltInVertexId)) && + msl_options.vertex_index_type != Options::IndexType::None) + { + // Add the index buffer so we can set gl_VertexIndex correctly. + if (!ep_args.empty()) + ep_args += ", "; + switch (msl_options.vertex_index_type) + { + case Options::IndexType::None: + break; + case Options::IndexType::UInt16: + ep_args += join("const device ushort* ", index_buffer_var_name, " [[buffer(", + msl_options.shader_index_buffer_index, ")]]"); + break; + case Options::IndexType::UInt32: + ep_args += join("const device uint* ", index_buffer_var_name, " [[buffer(", + msl_options.shader_index_buffer_index, ")]]"); + break; + } + } + + // Tessellation control shaders get three additional parameters: + // a buffer to hold the per-patch data, a buffer to hold the per-patch + // tessellation levels, and a block of workgroup memory to hold the + // input control point data. + if (is_tesc_shader()) + { + if (patch_stage_out_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("device ", type_to_glsl(get_patch_stage_out_struct_type()), "* ", patch_output_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_patch_output_buffer_index), ")]]"); + } + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("device ", get_tess_factor_struct_name(), "* ", tess_factor_buffer_var_name, " [[buffer(", + convert_to_string(msl_options.shader_tess_factor_buffer_index), ")]]"); + + // Initializer for tess factors must be handled specially since it's never declared as a normal variable. + uint32_t outer_factor_initializer_id = 0; + uint32_t inner_factor_initializer_id = 0; + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + if (!has_decoration(var.self, DecorationBuiltIn) || var.storage != StorageClassOutput || !var.initializer) + return; + + BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn)); + if (builtin == BuiltInTessLevelInner) + inner_factor_initializer_id = var.initializer; + else if (builtin == BuiltInTessLevelOuter) + outer_factor_initializer_id = var.initializer; + }); + + const SPIRConstant *c = nullptr; + + if (outer_factor_initializer_id && (c = maybe_get(outer_factor_initializer_id))) + { + auto &entry_func = get(ir.default_entry_point); + entry_func.fixup_hooks_in.push_back( + [=]() + { + uint32_t components = is_tessellating_triangles() ? 3 : 4; + for (uint32_t i = 0; i < components; i++) + { + statement(builtin_to_glsl(BuiltInTessLevelOuter, StorageClassOutput), "[", i, + "] = ", "half(", to_expression(c->subconstants[i]), ");"); + } + }); + } + + if (inner_factor_initializer_id && (c = maybe_get(inner_factor_initializer_id))) + { + auto &entry_func = get(ir.default_entry_point); + if (is_tessellating_triangles()) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_to_glsl(BuiltInTessLevelInner, StorageClassOutput), " = ", "half(", + to_expression(c->subconstants[0]), ");"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + for (uint32_t i = 0; i < 2; i++) + { + statement(builtin_to_glsl(BuiltInTessLevelInner, StorageClassOutput), "[", i, "] = ", + "half(", to_expression(c->subconstants[i]), ");"); + } + }); + } + } + + if (stage_in_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + if (msl_options.multi_patch_workgroup) + { + ep_args += join("device ", type_to_glsl(get_stage_in_struct_type()), "* ", input_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_input_buffer_index), ")]]"); + } + else + { + ep_args += join("threadgroup ", type_to_glsl(get_stage_in_struct_type()), "* ", input_wg_var_name, + " [[threadgroup(", convert_to_string(msl_options.shader_input_wg_index), ")]]"); + } + } + } + } + // Tessellation evaluation shaders get three additional parameters: + // a buffer for the per-patch data, a buffer for the per-patch + // tessellation levels, and a buffer for the control point data. + if (is_tese_shader() && msl_options.raw_buffer_tese_input) + { + if (patch_stage_in_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += + join("const device ", type_to_glsl(get_patch_stage_in_struct_type()), "* ", patch_input_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_patch_input_buffer_index), ")]]"); + } + + if (tess_level_inner_var_id || tess_level_outer_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("const device ", get_tess_factor_struct_name(), "* ", tess_factor_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_tess_factor_buffer_index), ")]]"); + } + + if (stage_in_var_id) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("const device ", type_to_glsl(get_stage_in_struct_type()), "* ", input_buffer_var_name, + " [[buffer(", convert_to_string(msl_options.shader_input_buffer_index), ")]]"); + } + } + + if (is_mesh_shader()) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("spvMesh_t spvMesh"); + } + + if (get_execution_model() == ExecutionModelTaskEXT) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("mesh_grid_properties spvMgp"); + } +} + +string CompilerMSL::entry_point_args_argument_buffer(bool append_comma) +{ + string ep_args = entry_point_arg_stage_in(); + Bitset claimed_bindings; + + for (uint32_t i = 0; i < kMaxArgumentBuffers; i++) + { + uint32_t id = argument_buffer_ids[i]; + if (id == 0) + continue; + + add_resource_name(id); + auto &var = get(id); + auto &type = get_variable_data_type(var); + + if (!ep_args.empty()) + ep_args += ", "; + + // Check if the argument buffer binding itself has been remapped. + uint32_t buffer_binding; + auto itr = resource_bindings.find({ get_entry_point().model, i, kArgumentBufferBinding }); + if (itr != end(resource_bindings)) + { + buffer_binding = itr->second.first.msl_buffer; + itr->second.second = true; + } + else + { + // As a fallback, directly map desc set <-> binding. + // If that was taken, take the next buffer binding. + if (claimed_bindings.get(i)) + buffer_binding = next_metal_resource_index_buffer; + else + buffer_binding = i; + } + + claimed_bindings.set(buffer_binding); + + ep_args += get_variable_address_space(var) + " "; + + if (recursive_inputs.count(type.self)) + ep_args += string("void* ") + to_restrict(id, true) + to_name(id) + "_vp"; + else + ep_args += type_to_glsl(type) + "& " + to_restrict(id, true) + to_name(id); + + ep_args += " [[buffer(" + convert_to_string(buffer_binding) + ")]]"; + + next_metal_resource_index_buffer = max(next_metal_resource_index_buffer, buffer_binding + 1); + } + + entry_point_args_discrete_descriptors(ep_args); + entry_point_args_builtin(ep_args); + + if (!ep_args.empty() && append_comma) + ep_args += ", "; + + return ep_args; +} + +const MSLConstexprSampler *CompilerMSL::find_constexpr_sampler(uint32_t id) const +{ + // Try by ID. + { + auto itr = constexpr_samplers_by_id.find(id); + if (itr != end(constexpr_samplers_by_id)) + return &itr->second; + } + + // Try by binding. + { + uint32_t desc_set = get_decoration(id, DecorationDescriptorSet); + uint32_t binding = get_decoration(id, DecorationBinding); + + auto itr = constexpr_samplers_by_binding.find({ desc_set, binding }); + if (itr != end(constexpr_samplers_by_binding)) + return &itr->second; + } + + return nullptr; +} + +void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args) +{ + // Output resources, sorted by resource index & type + // We need to sort to work around a bug on macOS 10.13 with NVidia drivers where switching between shaders + // with different order of buffers can result in issues with buffer assignments inside the driver. + struct Resource + { + SPIRVariable *var; + SPIRVariable *discrete_descriptor_alias; + string name; + SPIRType::BaseType basetype; + uint32_t index; + uint32_t plane; + uint32_t secondary_index; + }; + + SmallVector resources; + + entry_point_bindings.clear(); + ir.for_each_typed_id([&](uint32_t var_id, SPIRVariable &var) { + if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || + var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer) && + !is_hidden_variable(var)) + { + auto &type = get_variable_data_type(var); + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + + if (is_supported_argument_buffer_type(type) && var.storage != StorageClassPushConstant) + { + if (descriptor_set_is_argument_buffer(desc_set)) + { + if (is_var_runtime_size_array(var)) + { + // Runtime arrays need to be wrapped in spvDescriptorArray from argument buffer payload. + entry_point_bindings.push_back(&var); + // We'll wrap this, so to_name() will always use non-qualified name. + // We'll need the qualified name to create temporary variable instead. + ir.meta[var_id].decoration.qualified_alias_explicit_override = true; + } + return; + } + } + + // Handle descriptor aliasing of simple discrete cases. + // We can handle aliasing of buffers by casting pointers. + // The amount of aliasing we can perform for discrete descriptors is very limited. + // For fully mutable-style aliasing, we need argument buffers where we can exploit the fact + // that descriptors are all 8 bytes. + SPIRVariable *discrete_descriptor_alias = nullptr; + + const auto resource_is_aliasing_candidate = [this](const SPIRVariable &var_) { + return is_var_runtime_size_array(var_) || var_.storage == StorageClassUniform || + var_.storage == StorageClassStorageBuffer; + }; + + if (resource_is_aliasing_candidate(var)) + { + for (auto &resource : resources) + { + if (resource_is_aliasing_candidate(*resource.var) && + get_decoration(resource.var->self, DecorationDescriptorSet) == + get_decoration(var_id, DecorationDescriptorSet) && + get_decoration(resource.var->self, DecorationBinding) == + get_decoration(var_id, DecorationBinding)) + { + discrete_descriptor_alias = resource.var; + // Self-reference marks that we should declare the resource, + // and it's being used as an alias (so we can emit void* instead). + resource.discrete_descriptor_alias = resource.var; + // Need to promote interlocked usage so that the primary declaration is correct. + if (interlocked_resources.count(var_id)) + interlocked_resources.insert(resource.var->self); + + // Aliasing with unroll just gets too messy to deal with. I sure hope this never comes up ... + if ((is_array(get_variable_data_type(*resource.var)) && !is_var_runtime_size_array(*resource.var)) || + (is_array(get_variable_data_type(var)) && !is_var_runtime_size_array(var))) + { + SPIRV_CROSS_THROW("Attempting to alias same binding with a descriptor array which is not implemented through argument buffers. This is unsupported."); + } + break; + } + } + } + + const MSLConstexprSampler *constexpr_sampler = nullptr; + if (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Sampler) + { + constexpr_sampler = find_constexpr_sampler(var_id); + if (constexpr_sampler) + { + // Mark this ID as a constexpr sampler for later in case it came from set/bindings. + constexpr_samplers_by_id[var_id] = *constexpr_sampler; + } + } + + // Emulate texture2D atomic operations + uint32_t secondary_index = 0; + if (atomic_image_vars_emulated.count(var.self)) + { + secondary_index = get_metal_resource_index(var, SPIRType::AtomicCounter, 0); + } + + if (type.basetype == SPIRType::SampledImage) + { + add_resource_name(var_id); + + uint32_t plane_count = 1; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + plane_count = constexpr_sampler->planes; + + entry_point_bindings.push_back(&var); + for (uint32_t i = 0; i < plane_count; i++) + resources.push_back({&var, discrete_descriptor_alias, to_name(var_id), SPIRType::Image, + get_metal_resource_index(var, SPIRType::Image, i), i, secondary_index }); + + if (type.image.dim != DimBuffer && !constexpr_sampler) + { + resources.push_back({&var, discrete_descriptor_alias, to_sampler_expression(var_id), SPIRType::Sampler, + get_metal_resource_index(var, SPIRType::Sampler), 0, 0 }); + } + } + else if (!constexpr_sampler) + { + // constexpr samplers are not declared as resources. + add_resource_name(var_id); + + // Don't allocate resource indices for aliases. + uint32_t resource_index = ~0u; + if (!discrete_descriptor_alias) + resource_index = get_metal_resource_index(var, type.basetype); + + entry_point_bindings.push_back(&var); + resources.push_back({&var, discrete_descriptor_alias, to_name(var_id), type.basetype, + resource_index, 0, secondary_index }); + } + } + }); + + stable_sort(resources.begin(), resources.end(), + [](const Resource &lhs, const Resource &rhs) + { return tie(lhs.basetype, lhs.index) < tie(rhs.basetype, rhs.index); }); + + for (auto &r : resources) + { + auto &var = *r.var; + auto &type = get_variable_data_type(var); + + uint32_t var_id = var.self; + + if (is_var_runtime_size_array(var)) + { + add_spv_func_and_recompile(SPVFuncImplVariableDescriptorArray); + const bool ssbo = has_decoration(type.self, DecorationBufferBlock); + if ((var.storage == StorageClassStorageBuffer || ssbo) && msl_options.runtime_array_rich_descriptor) + add_spv_func_and_recompile(SPVFuncImplVariableSizedDescriptor); + else + add_spv_func_and_recompile(SPVFuncImplVariableDescriptor); + } + + if (r.discrete_descriptor_alias) + { + if (r.var == r.discrete_descriptor_alias) + { + auto primary_name = join("spvBufferAliasSet", + get_decoration(var_id, DecorationDescriptorSet), + "Binding", + get_decoration(var_id, DecorationBinding)); + + // Declare the primary alias as void* + if (!ep_args.empty()) + ep_args += ", "; + ep_args += get_variable_address_space(var) + " void* " + primary_name; + ep_args += " [[buffer(" + convert_to_string(r.index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + + buffer_aliases_discrete.push_back(r.var->self); + continue; + } + + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + uint32_t desc_binding = get_decoration(var_id, DecorationBinding); + + if (is_var_runtime_size_array(var)) + { + // This must be implemented as an argument buffer. Cast to intended descriptor array type on-demand. + if (!ep_args.empty()) + ep_args += ", "; + ep_args += join("device const void* spvDescriptorSet", desc_set, "Binding", desc_binding); + if (type.basetype == SPIRType::SampledImage && r.basetype == SPIRType::Sampler) + ep_args += "Smplr"; + ep_args += " [[buffer(" + convert_to_string(r.index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + continue; + } + + switch (r.basetype) + { + case SPIRType::Struct: + { + auto &m = ir.meta[type.self]; + if (m.members.size() == 0) + break; + + if (!type.array.empty()) + { + if (type.array.size() > 1) + SPIRV_CROSS_THROW("Arrays of arrays of buffers are not supported."); + + is_using_builtin_array = true; + uint32_t array_size = get_resource_array_size(type, var_id); + for (uint32_t i = 0; i < array_size; ++i) + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += get_variable_address_space(var) + " " + type_to_glsl(type) + "* " + + to_restrict(var_id, true) + r.name + "_" + convert_to_string(i); + ep_args += " [[buffer(" + convert_to_string(r.index + i) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + is_using_builtin_array = false; + } + else + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += get_variable_address_space(var) + " "; + + if (recursive_inputs.count(type.self)) + ep_args += string("void* ") + to_restrict(var_id, true) + r.name + "_vp"; + else + ep_args += type_to_glsl(type) + "& " + to_restrict(var_id, true) + r.name; + + ep_args += " [[buffer(" + convert_to_string(r.index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + break; + } + case SPIRType::Sampler: + if (!ep_args.empty()) + ep_args += ", "; + ep_args += sampler_type(type, var_id, false) + " " + r.name; + ep_args += " [[sampler(" + convert_to_string(r.index) + ")]]"; + break; + case SPIRType::Image: + { + if (!ep_args.empty()) + ep_args += ", "; + + // Use Metal's native frame-buffer fetch API for subpass inputs. + const auto &basetype = get(var.basetype); + if (!type_is_msl_framebuffer_fetch(basetype)) + { + ep_args += image_type_glsl(type, var_id, false) + " " + r.name; + if (r.plane > 0) + ep_args += join(plane_name_suffix, r.plane); + + ep_args += " [[texture(" + convert_to_string(r.index) + ")"; + + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + else + { + if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Framebuffer fetch on Mac is not supported before MSL 2.3."); + ep_args += image_type_glsl(type, var_id, false) + " " + r.name; + ep_args += " [[color(" + convert_to_string(r.index) + ")]]"; + } + + // Emulate texture2D atomic operations + if (atomic_image_vars_emulated.count(var.self)) + { + auto &flags = ir.get_decoration_bitset(var.self); + const char *cv_flags = decoration_flags_signal_volatile(flags) ? "volatile " : ""; + ep_args += join(", ", cv_flags, "device atomic_", type_to_glsl(get(basetype.image.type), 0)); + ep_args += "* " + r.name + "_atomic"; + ep_args += " [[buffer(" + convert_to_string(r.secondary_index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + } + break; + } + case SPIRType::AccelerationStructure: + { + if (!ep_args.empty()) + ep_args += ", "; + ep_args += type_to_glsl(type, var_id) + " " + r.name; + ep_args += " [[buffer(" + convert_to_string(r.index) + ")]]"; + break; + } + default: + if (!ep_args.empty()) + ep_args += ", "; + if (!type.pointer) + ep_args += get_type_address_space(get(var.basetype), var_id) + " " + + type_to_glsl(type, var_id) + "& " + r.name; + else + ep_args += type_to_glsl(type, var_id) + " " + r.name; + ep_args += " [[buffer(" + convert_to_string(r.index) + ")"; + if (interlocked_resources.count(var_id)) + ep_args += ", raster_order_group(0)"; + ep_args += "]]"; + break; + } + } +} + +// Returns a string containing a comma-delimited list of args for the entry point function +// This is the "classic" method of MSL 1 when we don't have argument buffer support. +string CompilerMSL::entry_point_args_classic(bool append_comma) +{ + string ep_args = entry_point_arg_stage_in(); + entry_point_args_discrete_descriptors(ep_args); + entry_point_args_builtin(ep_args); + + if (!ep_args.empty() && append_comma) + ep_args += ", "; + + return ep_args; +} + +void CompilerMSL::fix_up_shader_inputs_outputs() +{ + auto &entry_func = this->get(ir.default_entry_point); + + // Emit a guard to ensure we don't execute beyond the last vertex. + // Vertex shaders shouldn't have the problems with barriers in non-uniform control flow that + // tessellation control shaders do, so early returns should be OK. We may need to revisit this + // if it ever becomes possible to use barriers from a vertex shader. + if (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation) + { + entry_func.fixup_hooks_in.push_back([this]() { + statement("if (any(", to_expression(builtin_invocation_id_id), + " >= ", to_expression(builtin_stage_input_size_id), "))"); + statement(" return;"); + }); + } + + if (is_mesh_shader()) + { + // If shader doesn't call SetMeshOutputsEXT, nothing should be rendered. + // No need to barrier after this, because only thread 0 writes to this later. + entry_func.fixup_hooks_in.push_back([this]() { statement("if (gl_LocalInvocationIndex == 0) spvMeshSizes.y = 0u;"); }); + entry_func.fixup_hooks_out.push_back([this]() { emit_mesh_outputs(); }); + } + + // Look for sampled images and buffer. Add hooks to set up the swizzle constants or array lengths. + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + auto &type = get_variable_data_type(var); + uint32_t var_id = var.self; + bool ssbo = has_decoration(type.self, DecorationBufferBlock); + + if (var.storage == StorageClassUniformConstant && !is_hidden_variable(var)) + { + if (msl_options.swizzle_texture_samples && has_sampled_images && is_sampled_image_type(type)) + { + entry_func.fixup_hooks_in.push_back([this, &type, &var, var_id]() { + bool is_array_type = !type.array.empty(); + + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + { + statement("constant uint", is_array_type ? "* " : "& ", to_swizzle_expression(var_id), + is_array_type ? " = &" : " = ", to_name(argument_buffer_ids[desc_set]), + ".spvSwizzleConstants", "[", + convert_to_string(get_metal_resource_index(var, SPIRType::Image)), "];"); + } + else + { + // If we have an array of images, we need to be able to index into it, so take a pointer instead. + statement("constant uint", is_array_type ? "* " : "& ", to_swizzle_expression(var_id), + is_array_type ? " = &" : " = ", to_name(swizzle_buffer_id), "[", + convert_to_string(get_metal_resource_index(var, SPIRType::Image)), "];"); + } + }); + } + } + else if ((var.storage == StorageClassStorageBuffer || (var.storage == StorageClassUniform && ssbo)) && + !is_hidden_variable(var)) + { + if (buffer_requires_array_length(var.self)) + { + entry_func.fixup_hooks_in.push_back( + [this, &type, &var, var_id]() + { + bool is_array_type = !type.array.empty() && !is_var_runtime_size_array(var); + + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + { + statement("constant uint", is_array_type ? "* " : "& ", to_buffer_size_expression(var_id), + is_array_type ? " = &" : " = ", to_name(argument_buffer_ids[desc_set]), + ".spvBufferSizeConstants", "[", + convert_to_string(get_metal_resource_index(var, SPIRType::UInt)), "];"); + } + else + { + // If we have an array of images, we need to be able to index into it, so take a pointer instead. + statement("constant uint", is_array_type ? "* " : "& ", to_buffer_size_expression(var_id), + is_array_type ? " = &" : " = ", to_name(buffer_size_buffer_id), "[", + convert_to_string(get_metal_resource_index(var, type.basetype)), "];"); + } + }); + } + } + + if (!msl_options.argument_buffers && + msl_options.replace_recursive_inputs && type_contains_recursion(type) && + (var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || + var.storage == StorageClassPushConstant || var.storage == StorageClassStorageBuffer)) + { + recursive_inputs.insert(type.self); + entry_func.fixup_hooks_in.push_back([this, &type, &var, var_id]() { + auto addr_space = get_variable_address_space(var); + auto var_name = to_name(var_id); + statement(addr_space, " auto& ", to_restrict(var_id, true), var_name, + " = *(", addr_space, " ", type_to_glsl(type), "*)", var_name, "_vp;"); + }); + } + }); + + // Builtin variables + ir.for_each_typed_id([this, &entry_func](uint32_t, SPIRVariable &var) { + uint32_t var_id = var.self; + BuiltIn bi_type = ir.meta[var_id].decoration.builtin_type; + + if (var.storage != StorageClassInput && var.storage != StorageClassOutput) + return; + if (!interface_variable_exists_in_entry_point(var.self)) + return; + + if (var.storage == StorageClassInput && is_builtin_variable(var) && active_input_builtins.get(bi_type)) + { + switch (bi_type) + { + case BuiltInSamplePosition: + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = get_sample_position(", + to_expression(builtin_sample_id_id), ");"); + }); + break; + case BuiltInFragCoord: + if (is_sample_rate()) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement(to_expression(var_id), ".xy += get_sample_position(", + to_expression(builtin_sample_id_id), ") - 0.5;"); + }); + } + break; + case BuiltInInvocationId: + // This is direct-mapped without multi-patch workgroups. + if (!is_tesc_shader() || !msl_options.multi_patch_workgroup) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_invocation_id_id), ".x % ", this->get_entry_point().output_vertices, + ";"); + }); + break; + case BuiltInPrimitiveId: + // This is natively supported by fragment and tessellation evaluation shaders. + // In tessellation control shaders, this is direct-mapped without multi-patch workgroups. + if (!is_tesc_shader() || !msl_options.multi_patch_workgroup) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = min(", + to_expression(builtin_invocation_id_id), ".x / ", this->get_entry_point().output_vertices, + ", spvIndirectParams[1] - 1);"); + }); + break; + case BuiltInPatchVertices: + if (is_tese_shader()) + { + if (msl_options.raw_buffer_tese_input) + { + entry_func.fixup_hooks_in.push_back( + [=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + get_entry_point().output_vertices, ";"); + }); + } + else + { + entry_func.fixup_hooks_in.push_back( + [=]() + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(patch_stage_in_var_id), ".gl_in.size();"); + }); + } + } + else + { + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = spvIndirectParams[0];"); + }); + } + break; + case BuiltInTessCoord: + if (get_entry_point().flags.get(ExecutionModeQuads)) + { + // The entry point will only have a float2 TessCoord variable. + // Pad to float3. + entry_func.fixup_hooks_in.push_back([=]() { + auto name = builtin_to_glsl(BuiltInTessCoord, StorageClassInput); + statement("float3 " + name + " = float3(" + name + "In.x, " + name + "In.y, 0.0);"); + }); + } + + // Emit a fixup to account for the shifted domain. Don't do this for triangles; + // MoltenVK will just reverse the winding order instead. + if (msl_options.tess_domain_origin_lower_left && !is_tessellating_triangles()) + { + string tc = to_expression(var_id); + entry_func.fixup_hooks_in.push_back([=]() { statement(tc, ".y = 1.0 - ", tc, ".y;"); }); + } + break; + case BuiltInSubgroupId: + if (!msl_options.emulate_subgroups) + break; + // For subgroup emulation, this is the same as the local invocation index. + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_local_invocation_index_id), ";"); + }); + break; + case BuiltInNumSubgroups: + if (!msl_options.emulate_subgroups) + break; + // For subgroup emulation, this is the same as the workgroup size. + entry_func.fixup_hooks_in.push_back([=]() { + auto &type = expression_type(builtin_workgroup_size_id); + string size_expr = to_expression(builtin_workgroup_size_id); + if (type.vecsize >= 3) + size_expr = join(size_expr, ".x * ", size_expr, ".y * ", size_expr, ".z"); + else if (type.vecsize == 2) + size_expr = join(size_expr, ".x * ", size_expr, ".y"); + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", size_expr, ";"); + }); + break; + case BuiltInSubgroupLocalInvocationId: + if (!msl_options.emulate_subgroups) + break; + // For subgroup emulation, assume subgroups of size 1. + entry_func.fixup_hooks_in.push_back( + [=]() { statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = 0;"); }); + break; + case BuiltInSubgroupSize: + if (msl_options.emulate_subgroups) + { + // For subgroup emulation, assume subgroups of size 1. + entry_func.fixup_hooks_in.push_back( + [=]() { statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = 1;"); }); + } + else if (msl_options.fixed_subgroup_size != 0) + { + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + msl_options.fixed_subgroup_size, ";"); + }); + } + break; + case BuiltInSubgroupEqMask: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.2 on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + entry_func.fixup_hooks_in.push_back([=]() { + if (msl_options.is_ios()) + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", "uint4(1 << ", + to_expression(builtin_subgroup_invocation_id_id), ", uint3(0));"); + } + else + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_subgroup_invocation_id_id), " >= 32 ? uint4(0, (1 << (", + to_expression(builtin_subgroup_invocation_id_id), " - 32)), uint2(0)) : uint4(1 << ", + to_expression(builtin_subgroup_invocation_id_id), ", uint3(0));"); + } + }); + break; + case BuiltInSubgroupGeMask: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.2 on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + if (msl_options.fixed_subgroup_size != 0) + add_spv_func_and_recompile(SPVFuncImplSubgroupBallot); + entry_func.fixup_hooks_in.push_back([=]() { + // Case where index < 32, size < 32: + // mask0 = bfi(0, 0xFFFFFFFF, index, size - index); + // mask1 = bfi(0, 0xFFFFFFFF, 0, 0); // Gives 0 + // Case where index < 32 but size >= 32: + // mask0 = bfi(0, 0xFFFFFFFF, index, 32 - index); + // mask1 = bfi(0, 0xFFFFFFFF, 0, size - 32); + // Case where index >= 32: + // mask0 = bfi(0, 0xFFFFFFFF, 32, 0); // Gives 0 + // mask1 = bfi(0, 0xFFFFFFFF, index - 32, size - index); + // This is expressed without branches to avoid divergent + // control flow--hence the complicated min/max expressions. + // This is further complicated by the fact that if you attempt + // to bfi/bfe out-of-bounds on Metal, undefined behavior is the + // result. + if (msl_options.fixed_subgroup_size > 32) + { + // Don't use the subgroup size variable with fixed subgroup sizes, + // since the variables could be defined in the wrong order. + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, min(", + to_expression(builtin_subgroup_invocation_id_id), ", 32u), (uint)max(32 - (int)", + to_expression(builtin_subgroup_invocation_id_id), + ", 0)), insert_bits(0u, 0xFFFFFFFF," + " (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " - 32, 0), ", + msl_options.fixed_subgroup_size, " - max(", + to_expression(builtin_subgroup_invocation_id_id), + ", 32u)), uint2(0));"); + } + else if (msl_options.fixed_subgroup_size != 0) + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, ", + to_expression(builtin_subgroup_invocation_id_id), ", ", + msl_options.fixed_subgroup_size, " - ", + to_expression(builtin_subgroup_invocation_id_id), + "), uint3(0));"); + } + else if (msl_options.is_ios()) + { + // On iOS, the SIMD-group size will currently never exceed 32. + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, ", + to_expression(builtin_subgroup_invocation_id_id), ", ", + to_expression(builtin_subgroup_size_id), " - ", + to_expression(builtin_subgroup_invocation_id_id), "), uint3(0));"); + } + else + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, min(", + to_expression(builtin_subgroup_invocation_id_id), ", 32u), (uint)max(min((int)", + to_expression(builtin_subgroup_size_id), ", 32) - (int)", + to_expression(builtin_subgroup_invocation_id_id), + ", 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " - 32, 0), (uint)max((int)", + to_expression(builtin_subgroup_size_id), " - (int)max(", + to_expression(builtin_subgroup_invocation_id_id), ", 32u), 0)), uint2(0));"); + } + }); + break; + case BuiltInSubgroupGtMask: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.2 on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + add_spv_func_and_recompile(SPVFuncImplSubgroupBallot); + entry_func.fixup_hooks_in.push_back([=]() { + // The same logic applies here, except now the index is one + // more than the subgroup invocation ID. + if (msl_options.fixed_subgroup_size > 32) + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, min(", + to_expression(builtin_subgroup_invocation_id_id), " + 1, 32u), (uint)max(32 - (int)", + to_expression(builtin_subgroup_invocation_id_id), + " - 1, 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " + 1 - 32, 0), ", + msl_options.fixed_subgroup_size, " - max(", + to_expression(builtin_subgroup_invocation_id_id), + " + 1, 32u)), uint2(0));"); + } + else if (msl_options.fixed_subgroup_size != 0) + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, ", + to_expression(builtin_subgroup_invocation_id_id), " + 1, ", + msl_options.fixed_subgroup_size, " - ", + to_expression(builtin_subgroup_invocation_id_id), + " - 1), uint3(0));"); + } + else if (msl_options.is_ios()) + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, ", + to_expression(builtin_subgroup_invocation_id_id), " + 1, ", + to_expression(builtin_subgroup_size_id), " - ", + to_expression(builtin_subgroup_invocation_id_id), " - 1), uint3(0));"); + } + else + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(insert_bits(0u, 0xFFFFFFFF, min(", + to_expression(builtin_subgroup_invocation_id_id), " + 1, 32u), (uint)max(min((int)", + to_expression(builtin_subgroup_size_id), ", 32) - (int)", + to_expression(builtin_subgroup_invocation_id_id), + " - 1, 0)), insert_bits(0u, 0xFFFFFFFF, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " + 1 - 32, 0), (uint)max((int)", + to_expression(builtin_subgroup_size_id), " - (int)max(", + to_expression(builtin_subgroup_invocation_id_id), " + 1, 32u), 0)), uint2(0));"); + } + }); + break; + case BuiltInSubgroupLeMask: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.2 on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + add_spv_func_and_recompile(SPVFuncImplSubgroupBallot); + entry_func.fixup_hooks_in.push_back([=]() { + if (msl_options.is_ios()) + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(extract_bits(0xFFFFFFFF, 0, ", + to_expression(builtin_subgroup_invocation_id_id), " + 1), uint3(0));"); + } + else + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(extract_bits(0xFFFFFFFF, 0, min(", + to_expression(builtin_subgroup_invocation_id_id), + " + 1, 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " + 1 - 32, 0)), uint2(0));"); + } + }); + break; + case BuiltInSubgroupLtMask: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.2 on iOS."); + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Subgroup ballot functionality requires Metal 2.1."); + add_spv_func_and_recompile(SPVFuncImplSubgroupBallot); + entry_func.fixup_hooks_in.push_back([=]() { + if (msl_options.is_ios()) + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(extract_bits(0xFFFFFFFF, 0, ", + to_expression(builtin_subgroup_invocation_id_id), "), uint3(0));"); + } + else + { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), + " = uint4(extract_bits(0xFFFFFFFF, 0, min(", + to_expression(builtin_subgroup_invocation_id_id), + ", 32u)), extract_bits(0xFFFFFFFF, 0, (uint)max((int)", + to_expression(builtin_subgroup_invocation_id_id), " - 32, 0)), uint2(0));"); + } + }); + break; + case BuiltInViewIndex: + if (!msl_options.multiview) + { + // According to the Vulkan spec, when not running under a multiview + // render pass, ViewIndex is 0. + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = 0;"); + }); + } + else if (msl_options.view_index_from_device_index) + { + // In this case, we take the view index from that of the device we're running on. + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + msl_options.device_index, ";"); + }); + // We actually don't want to set the render_target_array_index here. + // Since every physical device is rendering a different view, + // there's no need for layered rendering here. + } + else if (!msl_options.multiview_layered_rendering) + { + // In this case, the views are rendered one at a time. The view index, then, + // is just the first part of the "view mask". + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(view_mask_buffer_id), "[0];"); + }); + } + else if (get_execution_model() == ExecutionModelFragment) + { + // Because we adjusted the view index in the vertex shader, we have to + // adjust it back here. + entry_func.fixup_hooks_in.push_back([=]() { + statement(to_expression(var_id), " += ", to_expression(view_mask_buffer_id), "[0];"); + }); + } + else if (get_execution_model() == ExecutionModelVertex) + { + // Metal provides no special support for multiview, so we smuggle + // the view index in the instance index. + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(view_mask_buffer_id), "[0] + (", to_expression(builtin_instance_idx_id), + " - ", to_expression(builtin_base_instance_id), ") % ", + to_expression(view_mask_buffer_id), "[1];"); + statement(to_expression(builtin_instance_idx_id), " = (", + to_expression(builtin_instance_idx_id), " - ", + to_expression(builtin_base_instance_id), ") / ", to_expression(view_mask_buffer_id), + "[1] + ", to_expression(builtin_base_instance_id), ";"); + }); + // In addition to setting the variable itself, we also need to + // set the render_target_array_index with it on output. We have to + // offset this by the base view index, because Metal isn't in on + // our little game here. + entry_func.fixup_hooks_out.push_back([=]() { + statement(to_expression(builtin_layer_id), " = ", to_expression(var_id), " - ", + to_expression(view_mask_buffer_id), "[0];"); + }); + } + break; + case BuiltInDeviceIndex: + // Metal pipelines belong to the devices which create them, so we'll + // need to create a MTLPipelineState for every MTLDevice in a grouped + // VkDevice. We can assume, then, that the device index is constant. + entry_func.fixup_hooks_in.push_back([=]() { + statement("const ", builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + msl_options.device_index, ";"); + }); + break; + case BuiltInWorkgroupId: + if (!msl_options.dispatch_base || !active_input_builtins.get(BuiltInWorkgroupId)) + break; + + // The vkCmdDispatchBase() command lets the client set the base value + // of WorkgroupId. Metal has no direct equivalent; we must make this + // adjustment ourselves. + entry_func.fixup_hooks_in.push_back([=]() { + statement(to_expression(var_id), " += ", to_dereferenced_expression(builtin_dispatch_base_id), ";"); + }); + break; + case BuiltInGlobalInvocationId: + if (!msl_options.dispatch_base || !active_input_builtins.get(BuiltInGlobalInvocationId)) + break; + + // GlobalInvocationId is defined as LocalInvocationId + WorkgroupId * WorkgroupSize. + // This needs to be adjusted too. + entry_func.fixup_hooks_in.push_back([=]() { + auto &execution = this->get_entry_point(); + uint32_t workgroup_size_id = execution.workgroup_size.constant; + if (workgroup_size_id) + statement(to_expression(var_id), " += ", to_dereferenced_expression(builtin_dispatch_base_id), + " * ", to_expression(workgroup_size_id), ";"); + else + statement(to_expression(var_id), " += ", to_dereferenced_expression(builtin_dispatch_base_id), + " * uint3(", execution.workgroup_size.x, ", ", execution.workgroup_size.y, ", ", + execution.workgroup_size.z, ");"); + }); + break; + case BuiltInVertexId: + case BuiltInVertexIndex: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + builtin_declaration = true; + switch (msl_options.vertex_index_type) + { + case Options::IndexType::None: + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_invocation_id_id), ".x + ", + to_expression(builtin_dispatch_base_id), ".x;"); + break; + case Options::IndexType::UInt16: + case Options::IndexType::UInt32: + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", index_buffer_var_name, + "[", to_expression(builtin_invocation_id_id), ".x] + ", + to_expression(builtin_dispatch_base_id), ".x;"); + break; + } + builtin_declaration = false; + }); + break; + case BuiltInBaseVertex: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_dispatch_base_id), ".x;"); + }); + break; + case BuiltInInstanceId: + case BuiltInInstanceIndex: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + builtin_declaration = true; + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_invocation_id_id), ".y + ", to_expression(builtin_dispatch_base_id), + ".y;"); + builtin_declaration = false; + }); + break; + case BuiltInBaseInstance: + // This is direct-mapped normally. + if (!msl_options.vertex_for_tessellation) + break; + + entry_func.fixup_hooks_in.push_back([=]() { + statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ", + to_expression(builtin_dispatch_base_id), ".y;"); + }); + break; + default: + break; + } + } + else if (var.storage == StorageClassOutput && get_execution_model() == ExecutionModelFragment && + is_builtin_variable(var) && active_output_builtins.get(bi_type)) + { + switch (bi_type) + { + case BuiltInSampleMask: + if (has_additional_fixed_sample_mask()) + { + // If the additional fixed sample mask was set, we need to adjust the sample_mask + // output to reflect that. If the shader outputs the sample_mask itself too, we need + // to AND the two masks to get the final one. + string op_str = does_shader_write_sample_mask ? " &= " : " = "; + entry_func.fixup_hooks_out.push_back([=]() { + statement(to_expression(builtin_sample_mask_id), op_str, additional_fixed_sample_mask_str(), ";"); + }); + } + break; + case BuiltInFragDepth: + if (msl_options.input_attachment_is_ds_attachment && !writes_to_depth) + { + entry_func.fixup_hooks_out.push_back([=]() { + statement(to_expression(builtin_frag_depth_id), " = ", to_expression(builtin_frag_coord_id), ".z;"); + }); + } + break; + default: + break; + } + } + }); +} + +// Returns the Metal index of the resource of the specified type as used by the specified variable. +uint32_t CompilerMSL::get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane) +{ + auto &execution = get_entry_point(); + auto &var_dec = ir.meta[var.self].decoration; + auto &var_type = get(var.basetype); + uint32_t var_desc_set = (var.storage == StorageClassPushConstant) ? kPushConstDescSet : var_dec.set; + uint32_t var_binding = (var.storage == StorageClassPushConstant) ? kPushConstBinding : var_dec.binding; + + // If a matching binding has been specified, find and use it. + auto itr = resource_bindings.find({ execution.model, var_desc_set, var_binding }); + + // Atomic helper buffers for image atomics need to use secondary bindings as well. + bool use_secondary_binding = (var_type.basetype == SPIRType::SampledImage && basetype == SPIRType::Sampler) || + basetype == SPIRType::AtomicCounter; + + auto resource_decoration = + use_secondary_binding ? SPIRVCrossDecorationResourceIndexSecondary : SPIRVCrossDecorationResourceIndexPrimary; + + if (plane == 1) + resource_decoration = SPIRVCrossDecorationResourceIndexTertiary; + if (plane == 2) + resource_decoration = SPIRVCrossDecorationResourceIndexQuaternary; + + if (itr != end(resource_bindings)) + { + auto &remap = itr->second; + remap.second = true; + switch (basetype) + { + case SPIRType::Image: + set_extended_decoration(var.self, resource_decoration, remap.first.msl_texture + plane); + return remap.first.msl_texture + plane; + case SPIRType::Sampler: + set_extended_decoration(var.self, resource_decoration, remap.first.msl_sampler); + return remap.first.msl_sampler; + default: + set_extended_decoration(var.self, resource_decoration, remap.first.msl_buffer); + return remap.first.msl_buffer; + } + } + + // If we have already allocated an index, keep using it. + if (has_extended_decoration(var.self, resource_decoration)) + return get_extended_decoration(var.self, resource_decoration); + + auto &type = get(var.basetype); + + if (type_is_msl_framebuffer_fetch(type)) + { + // Frame-buffer fetch gets its fallback resource index from the input attachment index, + // which is then treated as color index. + return get_decoration(var.self, DecorationInputAttachmentIndex); + } + else if (msl_options.enable_decoration_binding) + { + // Allow user to enable decoration binding. + // If there is no explicit mapping of bindings to MSL, use the declared binding as a fallback. + if (has_decoration(var.self, DecorationBinding)) + { + var_binding = get_decoration(var.self, DecorationBinding); + // Avoid emitting sentinel bindings. + if (var_binding < 0x80000000u) + return var_binding; + } + } + + // If we did not explicitly remap, allocate bindings on demand. + // We cannot reliably use Binding decorations since SPIR-V and MSL's binding models are very different. + + bool allocate_argument_buffer_ids = false; + + if (var.storage != StorageClassPushConstant) + allocate_argument_buffer_ids = descriptor_set_is_argument_buffer(var_desc_set); + + uint32_t binding_stride = 1; + for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) + binding_stride *= to_array_size_literal(type, i); + + // If a binding has not been specified, revert to incrementing resource indices. + uint32_t resource_index; + + if (allocate_argument_buffer_ids) + { + // Allocate from a flat ID binding space. + resource_index = next_metal_resource_ids[var_desc_set]; + next_metal_resource_ids[var_desc_set] += binding_stride; + } + else + { + if (is_var_runtime_size_array(var)) + { + basetype = SPIRType::Struct; + binding_stride = 1; + } + // Allocate from plain bindings which are allocated per resource type. + switch (basetype) + { + case SPIRType::Image: + resource_index = next_metal_resource_index_texture; + next_metal_resource_index_texture += binding_stride; + break; + case SPIRType::Sampler: + resource_index = next_metal_resource_index_sampler; + next_metal_resource_index_sampler += binding_stride; + break; + default: + resource_index = next_metal_resource_index_buffer; + next_metal_resource_index_buffer += binding_stride; + break; + } + } + + set_extended_decoration(var.self, resource_decoration, resource_index); + return resource_index; +} + +bool CompilerMSL::type_is_msl_framebuffer_fetch(const SPIRType &type) const +{ + return type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && + msl_options.use_framebuffer_fetch_subpasses; +} + +const char *CompilerMSL::descriptor_address_space(uint32_t id, StorageClass storage, const char *plain_address_space) const +{ + if (msl_options.argument_buffers) + { + bool storage_class_is_descriptor = storage == StorageClassUniform || + storage == StorageClassStorageBuffer || + storage == StorageClassUniformConstant; + + uint32_t desc_set = get_decoration(id, DecorationDescriptorSet); + if (storage_class_is_descriptor && descriptor_set_is_argument_buffer(desc_set)) + { + // An awkward case where we need to emit *more* address space declarations (yay!). + // An example is where we pass down an array of buffer pointers to leaf functions. + // It's a constant array containing pointers to constants. + // The pointer array is always constant however. E.g. + // device SSBO * constant (&array)[N]. + // const device SSBO * constant (&array)[N]. + // constant SSBO * constant (&array)[N]. + // However, this only matters for argument buffers, since for MSL 1.0 style codegen, + // we emit the buffer array on stack instead, and that seems to work just fine apparently. + + // If the argument was marked as being in device address space, any pointer to member would + // be const device, not constant. + if (argument_buffer_device_storage_mask & (1u << desc_set)) + return "const device"; + else + return "constant"; + } + } + + return plain_address_space; +} + +string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg) +{ + auto &var = get(arg.id); + auto &var_type = get(arg.type); + StorageClass type_storage = var_type.storage; + + // Physical pointer types are passed by pointer, not reference. + auto &data_type = get_variable_data_type(var); + bool passed_by_value = arg.alias_global_variable ? false : is_physical_or_buffer_pointer(var_type); + auto &type = passed_by_value ? var_type : data_type; + + // If we need to modify the name of the variable, make sure we use the original variable. + // Our alias is just a shadow variable. + uint32_t name_id = var.self; + if (arg.alias_global_variable && var.basevariable) + name_id = var.basevariable; + + bool constref = !arg.alias_global_variable && !passed_by_value && is_pointer(var_type) && arg.write_count == 0; + // Framebuffer fetch is plain value, const looks out of place, but it is not wrong. + // readonly coming from glslang is not reliable in all cases. + // For UBOs, readonly is implied, and for SSBOs we use global check. + if (type_is_msl_framebuffer_fetch(type) || + type_storage == StorageClassStorageBuffer || + type_storage == StorageClassUniform || + type_storage == StorageClassPhysicalStorageBuffer) + { + constref = false; + } + else if (type_storage == StorageClassUniformConstant) + { + constref = true; + } + + bool type_is_image = type.basetype == SPIRType::Image || type.basetype == SPIRType::SampledImage || + type.basetype == SPIRType::Sampler; + bool type_is_tlas = type.basetype == SPIRType::AccelerationStructure; + + // For opaque types we handle const later due to descriptor address spaces. + const char *cv_qualifier = (constref && !type_is_image) ? "const " : ""; + string decl; + + // If this is a combined image-sampler for a 2D image with floating-point type, + // we emitted the 'spvDynamicImageSampler' type, and this is *not* an alias parameter + // for a global, then we need to emit a "dynamic" combined image-sampler. + // Unfortunately, this is necessary to properly support passing around + // combined image-samplers with Y'CbCr conversions on them. + bool is_dynamic_img_sampler = !arg.alias_global_variable && type.basetype == SPIRType::SampledImage && + type.image.dim == Dim2D && type_is_floating_point(get(type.image.type)) && + spv_function_implementations.count(SPVFuncImplDynamicImageSampler); + + // Allow Metal to use the array template to make arrays a value type + string address_space = arg.alias_global_variable ? get_variable_address_space(var) : get_leaf_argument_address_space(var); + bool builtin = has_decoration(var.self, DecorationBuiltIn); + auto builtin_type = BuiltIn(get_decoration(arg.id, DecorationBuiltIn)); + + if (var.basevariable && (var.basevariable == stage_in_ptr_var_id || var.basevariable == stage_out_ptr_var_id)) + decl = join(cv_qualifier, type_to_glsl(type, arg.id)); + else if (builtin && !is_mesh_shader()) + { + // Only use templated array for Clip/Cull distance when feasible. + // In other scenarios, we need need to override array length for tess levels (if used as outputs), + // or we need to emit the expected type for builtins (uint vs int). + auto storage = get(var.basetype).storage; + + if (storage == StorageClassInput && + (builtin_type == BuiltInTessLevelInner || builtin_type == BuiltInTessLevelOuter)) + { + is_using_builtin_array = false; + } + else if (builtin_type != BuiltInClipDistance && builtin_type != BuiltInCullDistance) + { + is_using_builtin_array = true; + } + + if (storage == StorageClassOutput && variable_storage_requires_stage_io(storage) && + !is_stage_output_builtin_masked(builtin_type)) + is_using_builtin_array = true; + + if (is_using_builtin_array) + decl = join(cv_qualifier, builtin_type_decl(builtin_type, arg.id)); + else + decl = join(cv_qualifier, type_to_glsl(type, arg.id)); + } + else if (is_var_runtime_size_array(var)) + { + const auto *parent_type = &get(type.parent_type); + auto type_name = type_to_glsl(*parent_type, arg.id); + if (type.basetype == SPIRType::AccelerationStructure) + decl = join("spvDescriptorArray<", type_name, ">"); + else if (type_is_image) + decl = join("spvDescriptorArray<", cv_qualifier, type_name, ">"); + else + decl = join("spvDescriptorArray<", address_space, " ", type_name, "*>"); + address_space = "const"; + } + else if ((type_storage == StorageClassUniform || type_storage == StorageClassStorageBuffer) && is_array(type)) + { + is_using_builtin_array = true; + decl += join(cv_qualifier, type_to_glsl(type, arg.id), "*"); + } + else if (is_dynamic_img_sampler) + { + decl = join(cv_qualifier, "spvDynamicImageSampler<", type_to_glsl(get(type.image.type)), ">"); + // Mark the variable so that we can handle passing it to another function. + set_extended_decoration(arg.id, SPIRVCrossDecorationDynamicImageSampler); + } + else + { + // The type is a pointer type we need to emit cv_qualifier late. + if (is_pointer(data_type)) + { + decl = type_to_glsl(type, arg.id); + if (*cv_qualifier != '\0') + decl += join(" ", cv_qualifier); + } + else + { + decl = join(cv_qualifier, type_to_glsl(type, arg.id)); + } + } + + if (passed_by_value || (!builtin && !is_pointer(var_type) && + (type_storage == StorageClassFunction || type_storage == StorageClassGeneric))) + { + // If the argument is a pure value and not an opaque type, we will pass by value. + if (msl_options.force_native_arrays && is_array(type)) + { + // We are receiving an array by value. This is problematic. + // We cannot be sure of the target address space since we are supposed to receive a copy, + // but this is not possible with MSL without some extra work. + // We will have to assume we're getting a reference in thread address space. + // If we happen to get a reference in constant address space, the caller must emit a copy and pass that. + // Thread const therefore becomes the only logical choice, since we cannot "create" a constant array from + // non-constant arrays, but we can create thread const from constant. + decl = string("thread const ") + decl; + decl += " (&"; + const char *restrict_kw = to_restrict(name_id, true); + if (*restrict_kw) + { + decl += " "; + decl += restrict_kw; + } + decl += to_expression(name_id); + decl += ")"; + decl += type_to_array_glsl(type, name_id); + } + else + { + // Variable pointer to array is kinda awkward ... + bool pointer_to_logical_buffer_array = + !is_physical_pointer(type) && is_pointer(type) && + has_decoration(type.parent_type, DecorationArrayStride); + + if (pointer_to_logical_buffer_array) + { + decl.pop_back(); + decl += " (*"; + decl += to_expression(name_id); + decl += ")"; + bool old_is_using_builtin_array = is_using_builtin_array; + is_using_builtin_array = true; + decl += type_to_array_glsl(type, name_id); + is_using_builtin_array = old_is_using_builtin_array; + } + else + { + if (!address_space.empty()) + decl = join(address_space, " ", decl); + decl += " "; + decl += to_expression(name_id); + } + } + } + else if (is_array(type) && !type_is_image) + { + // Arrays of opaque types are special cased. + if (!address_space.empty()) + decl = join(address_space, " ", decl); + + // spvDescriptorArray absorbs the address space inside the template. + if (!is_var_runtime_size_array(var)) + { + const char *argument_buffer_space = descriptor_address_space(name_id, type_storage, nullptr); + if (argument_buffer_space) + { + decl += " "; + decl += argument_buffer_space; + } + } + + // Special case, need to override the array size here if we're using tess level as an argument. + if (is_tesc_shader() && builtin && + (builtin_type == BuiltInTessLevelInner || builtin_type == BuiltInTessLevelOuter)) + { + uint32_t array_size = get_physical_tess_level_array_size(builtin_type); + if (array_size == 1) + { + decl += " &"; + decl += to_expression(name_id); + } + else + { + decl += " (&"; + decl += to_expression(name_id); + decl += ")"; + decl += join("[", array_size, "]"); + } + } + else if (is_var_runtime_size_array(var)) + { + decl += " " + to_expression(name_id); + } + else + { + auto array_size_decl = type_to_array_glsl(type, name_id); + if (array_size_decl.empty()) + decl += "& "; + else + decl += " (&"; + + const char *restrict_kw = to_restrict(name_id, true); + if (*restrict_kw) + { + decl += " "; + decl += restrict_kw; + } + decl += to_expression(name_id); + + if (!array_size_decl.empty()) + { + decl += ")"; + decl += array_size_decl; + } + } + } + else if (!type_is_image && !type_is_tlas && + (!pull_model_inputs.count(var.basevariable) || type.basetype == SPIRType::Struct)) + { + // If this is going to be a reference to a variable pointer, the address space + // for the reference has to go before the '&', but after the '*'. + if (!address_space.empty()) + { + if (is_pointer(data_type)) + { + if (*cv_qualifier == '\0') + decl += ' '; + decl += join(address_space, " "); + } + else + decl = join(address_space, " ", decl); + } + + decl += "&"; + decl += " "; + decl += to_restrict(name_id, true); + decl += to_expression(name_id); + } + else if (type_is_image || type_is_tlas) + { + if (is_var_runtime_size_array(var)) + { + decl = address_space + " " + decl + " " + to_expression(name_id); + } + else if (type.array.empty()) + { + // For non-arrayed types we can just pass opaque descriptors by value. + // This fixes problems if descriptors are passed by value from argument buffers and plain descriptors + // in same shader. + // There is no address space we can actually use, but value will work. + // This will break if applications attempt to pass down descriptor arrays as arguments, but + // fortunately that is extremely unlikely ... + decl += " "; + decl += to_expression(name_id); + } + else + { + const char *img_address_space = descriptor_address_space(name_id, type_storage, "thread const"); + decl = join(img_address_space, " ", decl); + decl += "& "; + decl += to_expression(name_id); + } + } + else + { + if (!address_space.empty()) + decl = join(address_space, " ", decl); + decl += " "; + decl += to_expression(name_id); + } + + // Emulate texture2D atomic operations + auto *backing_var = maybe_get_backing_variable(name_id); + if (backing_var && atomic_image_vars_emulated.count(backing_var->self)) + { + auto &flags = ir.get_decoration_bitset(backing_var->self); + const char *cv_flags = decoration_flags_signal_volatile(flags) ? "volatile " : ""; + decl += join(", ", cv_flags, "device atomic_", type_to_glsl(get(var_type.image.type), 0)); + decl += "* " + to_expression(name_id) + "_atomic"; + } + + is_using_builtin_array = false; + + return decl; +} + +// If we're currently in the entry point function, and the object +// has a qualified name, use it, otherwise use the standard name. +string CompilerMSL::to_name(uint32_t id, bool allow_alias) const +{ + if (current_function && (current_function->self == ir.default_entry_point)) + { + auto *m = ir.find_meta(id); + if (m && !m->decoration.qualified_alias_explicit_override && !m->decoration.qualified_alias.empty()) + return m->decoration.qualified_alias; + } + return Compiler::to_name(id, allow_alias); +} + +// Appends the name of the member to the variable qualifier string, except for Builtins. +string CompilerMSL::append_member_name(const string &qualifier, const SPIRType &type, uint32_t index) +{ + // Don't qualify Builtin names because they are unique and are treated as such when building expressions + BuiltIn builtin = BuiltInMax; + if (is_member_builtin(type, index, &builtin)) + return builtin_to_glsl(builtin, type.storage); + + // Strip any underscore prefix from member name + string mbr_name = to_member_name(type, index); + size_t startPos = mbr_name.find_first_not_of("_"); + mbr_name = (startPos != string::npos) ? mbr_name.substr(startPos) : ""; + return join(qualifier, "_", mbr_name); +} + +// Ensures that the specified name is permanently usable by prepending a prefix +// if the first chars are _ and a digit, which indicate a transient name. +string CompilerMSL::ensure_valid_name(string name, string pfx) +{ + return (name.size() >= 2 && name[0] == '_' && isdigit(name[1])) ? (pfx + name) : name; +} + +const std::unordered_set &CompilerMSL::get_reserved_keyword_set() +{ + static const unordered_set keywords = { + "kernel", + "vertex", + "fragment", + "compute", + "constant", + "device", + "bias", + "level", + "gradient2d", + "gradientcube", + "gradient3d", + "min_lod_clamp", + "assert", + "VARIABLE_TRACEPOINT", + "STATIC_DATA_TRACEPOINT", + "STATIC_DATA_TRACEPOINT_V", + "METAL_ALIGN", + "METAL_ASM", + "METAL_CONST", + "METAL_DEPRECATED", + "METAL_ENABLE_IF", + "METAL_FUNC", + "METAL_INTERNAL", + "METAL_NON_NULL_RETURN", + "METAL_NORETURN", + "METAL_NOTHROW", + "METAL_PURE", + "METAL_UNAVAILABLE", + "METAL_IMPLICIT", + "METAL_EXPLICIT", + "METAL_CONST_ARG", + "METAL_ARG_UNIFORM", + "METAL_ZERO_ARG", + "METAL_VALID_LOD_ARG", + "METAL_VALID_LEVEL_ARG", + "METAL_VALID_STORE_ORDER", + "METAL_VALID_LOAD_ORDER", + "METAL_VALID_COMPARE_EXCHANGE_FAILURE_ORDER", + "METAL_COMPATIBLE_COMPARE_EXCHANGE_ORDERS", + "METAL_VALID_RENDER_TARGET", + "is_function_constant_defined", + "CHAR_BIT", + "SCHAR_MAX", + "SCHAR_MIN", + "UCHAR_MAX", + "CHAR_MAX", + "CHAR_MIN", + "USHRT_MAX", + "SHRT_MAX", + "SHRT_MIN", + "UINT_MAX", + "INT_MAX", + "INT_MIN", + "FLT_DIG", + "FLT_MANT_DIG", + "FLT_MAX_10_EXP", + "FLT_MAX_EXP", + "FLT_MIN_10_EXP", + "FLT_MIN_EXP", + "FLT_RADIX", + "FLT_MAX", + "FLT_MIN", + "FLT_EPSILON", + "FP_ILOGB0", + "FP_ILOGBNAN", + "MAXFLOAT", + "HUGE_VALF", + "INFINITY", + "NAN", + "M_E_F", + "M_LOG2E_F", + "M_LOG10E_F", + "M_LN2_F", + "M_LN10_F", + "M_PI_F", + "M_PI_2_F", + "M_PI_4_F", + "M_1_PI_F", + "M_2_PI_F", + "M_2_SQRTPI_F", + "M_SQRT2_F", + "M_SQRT1_2_F", + "HALF_DIG", + "HALF_MANT_DIG", + "HALF_MAX_10_EXP", + "HALF_MAX_EXP", + "HALF_MIN_10_EXP", + "HALF_MIN_EXP", + "HALF_RADIX", + "HALF_MAX", + "HALF_MIN", + "HALF_EPSILON", + "MAXHALF", + "HUGE_VALH", + "M_E_H", + "M_LOG2E_H", + "M_LOG10E_H", + "M_LN2_H", + "M_LN10_H", + "M_PI_H", + "M_PI_2_H", + "M_PI_4_H", + "M_1_PI_H", + "M_2_PI_H", + "M_2_SQRTPI_H", + "M_SQRT2_H", + "M_SQRT1_2_H", + "DBL_DIG", + "DBL_MANT_DIG", + "DBL_MAX_10_EXP", + "DBL_MAX_EXP", + "DBL_MIN_10_EXP", + "DBL_MIN_EXP", + "DBL_RADIX", + "DBL_MAX", + "DBL_MIN", + "DBL_EPSILON", + "HUGE_VAL", + "M_E", + "M_LOG2E", + "M_LOG10E", + "M_LN2", + "M_LN10", + "M_PI", + "M_PI_2", + "M_PI_4", + "M_1_PI", + "M_2_PI", + "M_2_SQRTPI", + "M_SQRT2", + "M_SQRT1_2", + "quad_broadcast", + "thread", + "threadgroup", + "signed", + }; + + return keywords; +} + +const std::unordered_set &CompilerMSL::get_illegal_func_names() +{ + static const unordered_set illegal_func_names = { + "main", + "fragment", + "vertex", + "kernel", + "saturate", + "assert", + "fmin3", + "fmax3", + "divide", + "fmod", + "median3", + "VARIABLE_TRACEPOINT", + "STATIC_DATA_TRACEPOINT", + "STATIC_DATA_TRACEPOINT_V", + "METAL_ALIGN", + "METAL_ASM", + "METAL_CONST", + "METAL_DEPRECATED", + "METAL_ENABLE_IF", + "METAL_FUNC", + "METAL_INTERNAL", + "METAL_NON_NULL_RETURN", + "METAL_NORETURN", + "METAL_NOTHROW", + "METAL_PURE", + "METAL_UNAVAILABLE", + "METAL_IMPLICIT", + "METAL_EXPLICIT", + "METAL_CONST_ARG", + "METAL_ARG_UNIFORM", + "METAL_ZERO_ARG", + "METAL_VALID_LOD_ARG", + "METAL_VALID_LEVEL_ARG", + "METAL_VALID_STORE_ORDER", + "METAL_VALID_LOAD_ORDER", + "METAL_VALID_COMPARE_EXCHANGE_FAILURE_ORDER", + "METAL_COMPATIBLE_COMPARE_EXCHANGE_ORDERS", + "METAL_VALID_RENDER_TARGET", + "is_function_constant_defined", + "CHAR_BIT", + "SCHAR_MAX", + "SCHAR_MIN", + "UCHAR_MAX", + "CHAR_MAX", + "CHAR_MIN", + "USHRT_MAX", + "SHRT_MAX", + "SHRT_MIN", + "UINT_MAX", + "INT_MAX", + "INT_MIN", + "FLT_DIG", + "FLT_MANT_DIG", + "FLT_MAX_10_EXP", + "FLT_MAX_EXP", + "FLT_MIN_10_EXP", + "FLT_MIN_EXP", + "FLT_RADIX", + "FLT_MAX", + "FLT_MIN", + "FLT_EPSILON", + "FP_ILOGB0", + "FP_ILOGBNAN", + "MAXFLOAT", + "HUGE_VALF", + "INFINITY", + "NAN", + "M_E_F", + "M_LOG2E_F", + "M_LOG10E_F", + "M_LN2_F", + "M_LN10_F", + "M_PI_F", + "M_PI_2_F", + "M_PI_4_F", + "M_1_PI_F", + "M_2_PI_F", + "M_2_SQRTPI_F", + "M_SQRT2_F", + "M_SQRT1_2_F", + "HALF_DIG", + "HALF_MANT_DIG", + "HALF_MAX_10_EXP", + "HALF_MAX_EXP", + "HALF_MIN_10_EXP", + "HALF_MIN_EXP", + "HALF_RADIX", + "HALF_MAX", + "HALF_MIN", + "HALF_EPSILON", + "MAXHALF", + "HUGE_VALH", + "M_E_H", + "M_LOG2E_H", + "M_LOG10E_H", + "M_LN2_H", + "M_LN10_H", + "M_PI_H", + "M_PI_2_H", + "M_PI_4_H", + "M_1_PI_H", + "M_2_PI_H", + "M_2_SQRTPI_H", + "M_SQRT2_H", + "M_SQRT1_2_H", + "DBL_DIG", + "DBL_MANT_DIG", + "DBL_MAX_10_EXP", + "DBL_MAX_EXP", + "DBL_MIN_10_EXP", + "DBL_MIN_EXP", + "DBL_RADIX", + "DBL_MAX", + "DBL_MIN", + "DBL_EPSILON", + "HUGE_VAL", + "M_E", + "M_LOG2E", + "M_LOG10E", + "M_LN2", + "M_LN10", + "M_PI", + "M_PI_2", + "M_PI_4", + "M_1_PI", + "M_2_PI", + "M_2_SQRTPI", + "M_SQRT2", + "M_SQRT1_2", + "int8", + "uint8", + "int16", + "uint16", + "float8", + "float16", + "signed", + }; + + return illegal_func_names; +} + +// Replace all names that match MSL keywords or Metal Standard Library functions. +void CompilerMSL::replace_illegal_names() +{ + // FIXME: MSL and GLSL are doing two different things here. + // Agree on convention and remove this override. + auto &keywords = get_reserved_keyword_set(); + auto &illegal_func_names = get_illegal_func_names(); + + ir.for_each_typed_id([&](uint32_t self, SPIRVariable &) { + auto *meta = ir.find_meta(self); + if (!meta) + return; + + auto &dec = meta->decoration; + if (keywords.find(dec.alias) != end(keywords)) + dec.alias += "0"; + }); + + ir.for_each_typed_id([&](uint32_t self, SPIRFunction &) { + auto *meta = ir.find_meta(self); + if (!meta) + return; + + auto &dec = meta->decoration; + if (illegal_func_names.find(dec.alias) != end(illegal_func_names)) + dec.alias += "0"; + }); + + ir.for_each_typed_id([&](uint32_t self, SPIRType &) { + auto *meta = ir.find_meta(self); + if (!meta) + return; + + for (auto &mbr_dec : meta->members) + if (keywords.find(mbr_dec.alias) != end(keywords)) + mbr_dec.alias += "0"; + }); + + CompilerGLSL::replace_illegal_names(); +} + +void CompilerMSL::replace_illegal_entry_point_names() +{ + auto &illegal_func_names = get_illegal_func_names(); + + // It is important to this before we fixup identifiers, + // since if ep_name is reserved, we will need to fix that up, + // and then copy alias back into entry.name after the fixup. + for (auto &entry : ir.entry_points) + { + // Change both the entry point name and the alias, to keep them synced. + string &ep_name = entry.second.name; + if (illegal_func_names.find(ep_name) != end(illegal_func_names)) + ep_name += "0"; + + ir.meta[entry.first].decoration.alias = ep_name; + } +} + +void CompilerMSL::sync_entry_point_aliases_and_names() +{ + for (auto &entry : ir.entry_points) + entry.second.name = ir.meta[entry.first].decoration.alias; +} + +string CompilerMSL::to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain_is_resolved) +{ + auto *var = maybe_get_backing_variable(base); + // If this is a buffer array, we have to dereference the buffer pointers. + // Otherwise, if this is a pointer expression, dereference it. + + bool declared_as_pointer = false; + + if (var) + { + // Only allow -> dereference for block types. This is so we get expressions like + // buffer[i]->first_member.second_member, rather than buffer[i]->first->second. + const bool is_block = + has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock); + + bool is_buffer_variable = + is_block && (var->storage == StorageClassUniform || var->storage == StorageClassStorageBuffer); + declared_as_pointer = is_buffer_variable && is_array(get_pointee_type(var->basetype)); + } + + if (declared_as_pointer || (!ptr_chain_is_resolved && should_dereference(base))) + return join("->", to_member_name(type, index)); + else + return join(".", to_member_name(type, index)); +} + +string CompilerMSL::to_qualifiers_glsl(uint32_t id) +{ + string quals; + + auto *var = maybe_get(id); + auto &type = expression_type(id); + + if (type.storage == StorageClassTaskPayloadWorkgroupEXT) + quals += "object_data "; + + if (type.storage == StorageClassWorkgroup || (var && variable_decl_is_remapped_storage(*var, StorageClassWorkgroup))) + quals += "threadgroup "; + + return quals; +} + +// The optional id parameter indicates the object whose type we are trying +// to find the description for. It is optional. Most type descriptions do not +// depend on a specific object's use of that type. +string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id, bool member) +{ + string type_name; + + // Pointer? + if (is_pointer(type) || type_is_array_of_pointers(type)) + { + assert(type.pointer_depth > 0); + + const char *restrict_kw; + + auto type_address_space = get_type_address_space(type, id); + const auto *p_parent_type = &get(type.parent_type); + + // If we're wrapping buffer descriptors in a spvDescriptorArray, we'll have to handle it as a special case. + if (member && id) + { + auto &var = get(id); + if (is_var_runtime_size_array(var) && is_runtime_size_array(*p_parent_type)) + { + const bool ssbo = has_decoration(p_parent_type->self, DecorationBufferBlock); + bool buffer_desc = + (var.storage == StorageClassStorageBuffer || ssbo) && + msl_options.runtime_array_rich_descriptor; + + const char *wrapper_type = buffer_desc ? "spvBufferDescriptor" : "spvDescriptor"; + add_spv_func_and_recompile(SPVFuncImplVariableDescriptorArray); + add_spv_func_and_recompile(buffer_desc ? SPVFuncImplVariableSizedDescriptor : SPVFuncImplVariableDescriptor); + + type_name = join(wrapper_type, "<", type_address_space, " ", type_to_glsl(*p_parent_type, id), " *>"); + return type_name; + } + } + + // Work around C pointer qualifier rules. If glsl_type is a pointer type as well + // we'll need to emit the address space to the right. + // We could always go this route, but it makes the code unnatural. + // Prefer emitting thread T *foo over T thread* foo since it's more readable, + // but we'll have to emit thread T * thread * T constant bar; for example. + if (is_pointer(type) && is_pointer(*p_parent_type)) + type_name = join(type_to_glsl(*p_parent_type, id), " ", type_address_space, " "); + else + { + // Since this is not a pointer-to-pointer, ensure we've dug down to the base type. + // Some situations chain pointers even though they are not formally pointers-of-pointers. + while (is_pointer(*p_parent_type)) + p_parent_type = &get(p_parent_type->parent_type); + + // If we're emitting BDA, just use the templated type. + // Emitting builtin arrays need a lot of cooperation with other code to ensure + // the C-style nesting works right. + // FIXME: This is somewhat of a hack. + bool old_is_using_builtin_array = is_using_builtin_array; + bool pointer_to_buffer_array = is_pointer(type) && has_decoration(type.parent_type, DecorationArrayStride); + if (is_physical_pointer(type)) + is_using_builtin_array = false; + else if (pointer_to_buffer_array) + is_using_builtin_array = true; + + type_name = join(type_address_space, " ", type_to_glsl(*p_parent_type, id)); + + is_using_builtin_array = old_is_using_builtin_array; + } + + switch (type.basetype) + { + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + // These are handles. + break; + default: + // Anything else can be a raw pointer. + type_name += "*"; + restrict_kw = to_restrict(id, false); + if (*restrict_kw) + { + type_name += " "; + type_name += restrict_kw; + } + break; + } + return type_name; + } + + // Cooperative matrix -> Metal simdgroup matrix type + { + const SPIRType *coop_type = &type; + while (coop_type && (is_pointer(*coop_type) || is_array(*coop_type))) + coop_type = maybe_get(coop_type->parent_type); + + if (coop_type && coop_type->op == OpTypeCooperativeMatrixKHR) + { + if (!msl_options.supports_msl_version(3, 1)) + SPIRV_CROSS_THROW("Cooperative matrices require MSL 3.1 or later."); + + // Only Subgroup scope + auto &scope_c = get(coop_type->ext.cooperative.scope_id); + if (scope_c.specialization) + SPIRV_CROSS_THROW("MSL does not support spec-constant scope for cooperative matrices."); + if (scope_c.scalar() != ScopeSubgroup) + SPIRV_CROSS_THROW("MSL cooperative matrices only support Subgroup scope."); + + // Only 8x8 + auto &rows_c = get(coop_type->ext.cooperative.rows_id); + auto &cols_c = get(coop_type->ext.cooperative.columns_id); + if (rows_c.specialization || cols_c.specialization) + SPIRV_CROSS_THROW("MSL does not support spec-constant dimensions for cooperative matrices."); + if (rows_c.scalar() != 8 || cols_c.scalar() != 8) + SPIRV_CROSS_THROW("MSL cooperative matrices only support 8x8 dimensions."); + + // Map component type to simdgroup_*8x8 + auto &comp = get(coop_type->parent_type); + switch (comp.basetype) + { + case SPIRType::Float: + return "simdgroup_float8x8"; + case SPIRType::Half: + return "simdgroup_half8x8"; + case SPIRType::BFloat16: + return "simdgroup_bfloat8x8"; + default: + SPIRV_CROSS_THROW("Unsupported component type for MSL cooperative matrix."); + } + } + } + + switch (type.basetype) + { + case SPIRType::Struct: + // Need OpName lookup here to get a "sensible" name for a struct. + // Allow Metal to use the array template to make arrays a value type + type_name = to_name(type.self); + break; + + case SPIRType::Image: + case SPIRType::SampledImage: + return image_type_glsl(type, id, member); + + case SPIRType::Sampler: + return sampler_type(type, id, member); + + case SPIRType::Void: + return "void"; + + case SPIRType::AtomicCounter: + return "atomic_uint"; + + case SPIRType::ControlPointArray: + return join("patch_control_point<", type_to_glsl(get(type.parent_type), id), ">"); + + case SPIRType::Interpolant: + return join("interpolant<", type_to_glsl(get(type.parent_type), id), ", interpolation::", + has_decoration(type.self, DecorationNoPerspective) ? "no_perspective" : "perspective", ">"); + + // Scalars + case SPIRType::Boolean: + { + auto *var = maybe_get_backing_variable(id); + if (var && var->basevariable) + var = &get(var->basevariable); + + // Need to special-case threadgroup booleans. They are supposed to be logical + // storage, but MSL compilers will sometimes crash if you use threadgroup bool. + // Workaround this by using 16-bit types instead and fixup on load-store to this data. + if ((var && var->storage == StorageClassWorkgroup) || type.storage == StorageClassWorkgroup || member) + type_name = "short"; + else + type_name = "bool"; + break; + } + + case SPIRType::Char: + case SPIRType::SByte: + type_name = "char"; + break; + case SPIRType::UByte: + type_name = "uchar"; + break; + case SPIRType::Short: + type_name = "short"; + break; + case SPIRType::UShort: + type_name = "ushort"; + break; + case SPIRType::Int: + type_name = "int"; + break; + case SPIRType::UInt: + type_name = "uint"; + break; + case SPIRType::Int64: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("64-bit integers are only supported in MSL 2.2 and above."); + type_name = "long"; + break; + case SPIRType::UInt64: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("64-bit integers are only supported in MSL 2.2 and above."); + type_name = "ulong"; + break; + case SPIRType::Half: + type_name = "half"; + break; + case SPIRType::Float: + type_name = "float"; + break; + case SPIRType::Double: + type_name = "double"; // Currently unsupported + break; + case SPIRType::BFloat16: + if (!msl_options.supports_msl_version(3, 1)) + SPIRV_CROSS_THROW("bfloat16 requires MSL 3.1 or later."); + type_name = "bfloat"; + break; + case SPIRType::AccelerationStructure: + if (msl_options.supports_msl_version(2, 4)) + type_name = "raytracing::acceleration_structure"; + else if (msl_options.supports_msl_version(2, 3)) + type_name = "raytracing::instance_acceleration_structure"; + else + SPIRV_CROSS_THROW("Acceleration Structure Type is supported in MSL 2.3 and above."); + break; + case SPIRType::RayQuery: + return "raytracing::intersection_query"; + case SPIRType::MeshGridProperties: + return "mesh_grid_properties"; + + default: + return "unknown_type"; + } + + // Matrix? + if (type.columns > 1) + { + auto *var = maybe_get_backing_variable(id); + if (var && var->basevariable) + var = &get(var->basevariable); + + // Need to special-case threadgroup matrices. Due to an oversight, Metal's + // matrix struct prior to Metal 3 lacks constructors in the threadgroup AS, + // preventing us from default-constructing or initializing matrices in threadgroup storage. + // Work around this by using our own type as storage. + if (((var && var->storage == StorageClassWorkgroup) || type.storage == StorageClassWorkgroup) && + !msl_options.supports_msl_version(3, 0)) + { + add_spv_func_and_recompile(SPVFuncImplStorageMatrix); + type_name = "spvStorage_" + type_name; + } + + type_name += to_string(type.columns) + "x"; + } + + // Vector or Matrix? + if (type.vecsize > 1) + type_name += to_string(type.vecsize); + + if (type.array.empty() || using_builtin_array()) + { + return type_name; + } + else + { + // Allow Metal to use the array template to make arrays a value type + add_spv_func_and_recompile(SPVFuncImplUnsafeArray); + string res; + string sizes; + + for (uint32_t i = 0; i < uint32_t(type.array.size()); i++) + { + res += "spvUnsafeArray<"; + sizes += ", "; + sizes += to_array_size(type, i); + sizes += ">"; + } + + res += type_name + sizes; + return res; + } +} + +string CompilerMSL::type_to_glsl(const SPIRType &type, uint32_t id) +{ + return type_to_glsl(type, id, false); +} + +string CompilerMSL::type_to_array_glsl(const SPIRType &type, uint32_t variable_id) +{ + // Allow Metal to use the array template to make arrays a value type + switch (type.basetype) + { + case SPIRType::AtomicCounter: + case SPIRType::ControlPointArray: + case SPIRType::RayQuery: + return CompilerGLSL::type_to_array_glsl(type, variable_id); + + default: + if (type_is_array_of_pointers(type) || using_builtin_array()) + { + const SPIRVariable *var = variable_id ? maybe_get(variable_id) : nullptr; + if (var && (var->storage == StorageClassUniform || var->storage == StorageClassStorageBuffer) && + is_array(get_variable_data_type(*var))) + { + return join("[", get_resource_array_size(type, variable_id), "]"); + } + else + return CompilerGLSL::type_to_array_glsl(type, variable_id); + } + else + return ""; + } +} + +string CompilerMSL::constant_op_expression(const SPIRConstantOp &cop) +{ + switch (cop.opcode) + { + case OpSMod: + add_spv_func_and_recompile(SPVFuncImplSMod); + return join("spvSMod(", to_expression(cop.arguments[0]), ", ", to_expression(cop.arguments[1]), ")"); + case OpQuantizeToF16: + add_spv_func_and_recompile(SPVFuncImplQuantizeToF16); + return join("spvQuantizeToF16(", to_expression(cop.arguments[0]), ")"); + default: + return CompilerGLSL::constant_op_expression(cop); + } +} + +bool CompilerMSL::variable_decl_is_remapped_storage(const SPIRVariable &variable, StorageClass storage) const +{ + if (variable.storage == storage) + return true; + + if (storage == StorageClassWorkgroup) + { + // Specially masked IO block variable. + // Normally, we will never access IO blocks directly here. + // The only scenario which that should occur is with a masked IO block. + if (is_tesc_shader() && variable.storage == StorageClassOutput && + has_decoration(get(variable.basetype).self, DecorationBlock)) + { + return true; + } + + if (is_mesh_shader()) + return variable.storage == StorageClassOutput; + + return variable.storage == StorageClassOutput && is_tesc_shader() && is_stage_output_variable_masked(variable); + } + else if (storage == StorageClassStorageBuffer) + { + // These builtins are passed directly; we don't want to use remapping + // for them. + auto builtin = (BuiltIn)get_decoration(variable.self, DecorationBuiltIn); + if (is_tese_shader() && is_builtin_variable(variable) && (builtin == BuiltInTessCoord || builtin == BuiltInPrimitiveId)) + return false; + + // We won't be able to catch writes to control point outputs here since variable + // refers to a function local pointer. + // This is fine, as there cannot be concurrent writers to that memory anyways, + // so we just ignore that case. + + return (variable.storage == StorageClassOutput || variable.storage == StorageClassInput) && + !variable_storage_requires_stage_io(variable.storage) && + (variable.storage != StorageClassOutput || !is_stage_output_variable_masked(variable)); + } + else + { + return false; + } +} + +// GCC workaround of lambdas calling protected funcs +std::string CompilerMSL::variable_decl(const SPIRType &type, const std::string &name, uint32_t id) +{ + return CompilerGLSL::variable_decl(type, name, id); +} + +std::string CompilerMSL::sampler_type(const SPIRType &type, uint32_t id, bool member) +{ + auto *var = maybe_get(id); + if (var && var->basevariable) + { + // Check against the base variable, and not a fake ID which might have been generated for this variable. + id = var->basevariable; + } + + if (!type.array.empty()) + { + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of samplers."); + + if (type.array.size() > 1) + SPIRV_CROSS_THROW("Arrays of arrays of samplers are not supported in MSL."); + + // Arrays of samplers in MSL must be declared with a special array syntax ala C++11 std::array. + // If we have a runtime array, it could be a variable-count descriptor set binding. + auto &parent = get(get_pointee_type(type).parent_type); + uint32_t array_size = get_resource_array_size(type, id); + + if (array_size == 0) + { + add_spv_func_and_recompile(SPVFuncImplVariableDescriptor); + add_spv_func_and_recompile(SPVFuncImplVariableDescriptorArray); + + const char *descriptor_wrapper = processing_entry_point ? "const device spvDescriptor" : "const spvDescriptorArray"; + if (member) + descriptor_wrapper = "spvDescriptor"; + return join(descriptor_wrapper, "<", sampler_type(parent, id, false), ">", + processing_entry_point ? "*" : ""); + } + else + { + return join("array<", sampler_type(parent, id, false), ", ", array_size, ">"); + } + } + else + return "sampler"; +} + +// Returns an MSL string describing the SPIR-V image type +string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id, bool member) +{ + auto *var = maybe_get(id); + if (var && var->basevariable) + { + // For comparison images, check against the base variable, + // and not the fake ID which might have been generated for this variable. + id = var->basevariable; + } + + if (!type.array.empty()) + { + uint32_t major = 2, minor = 0; + if (msl_options.is_ios()) + { + major = 1; + minor = 2; + } + if (!msl_options.supports_msl_version(major, minor)) + { + if (msl_options.is_ios()) + SPIRV_CROSS_THROW("MSL 1.2 or greater is required for arrays of textures."); + else + SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of textures."); + } + + if (type.array.size() > 1) + SPIRV_CROSS_THROW("Arrays of arrays of textures are not supported in MSL."); + + // Arrays of images in MSL must be declared with a special array syntax ala C++11 std::array. + // If we have a runtime array, it could be a variable-count descriptor set binding. + auto &parent = get(get_pointee_type(type).parent_type); + uint32_t array_size = get_resource_array_size(type, id); + + if (array_size == 0) + { + add_spv_func_and_recompile(SPVFuncImplVariableDescriptor); + add_spv_func_and_recompile(SPVFuncImplVariableDescriptorArray); + const char *descriptor_wrapper = processing_entry_point ? "const device spvDescriptor" : "const spvDescriptorArray"; + if (member) + { + descriptor_wrapper = "spvDescriptor"; + // This requires a specialized wrapper type that packs image and sampler side by side. + // It is possible in theory. + if (type.basetype == SPIRType::SampledImage) + SPIRV_CROSS_THROW("Argument buffer runtime array currently not supported for combined image sampler."); + } + return join(descriptor_wrapper, "<", image_type_glsl(parent, id, false), ">", + processing_entry_point ? "*" : ""); + } + else + { + return join("array<", image_type_glsl(parent, id, false), ", ", array_size, ">"); + } + } + + string img_type_name; + + auto &img_type = type.image; + + if (is_depth_image(type, id)) + { + switch (img_type.dim) + { + case Dim1D: + case Dim2D: + if (img_type.dim == Dim1D && !msl_options.texture_1D_as_2D) + { + // Use a native Metal 1D texture + img_type_name += "depth1d_unsupported_by_metal"; + break; + } + + if (img_type.ms && img_type.arrayed) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Multisampled array textures are supported from 2.1."); + img_type_name += "depth2d_ms_array"; + } + else if (img_type.ms) + img_type_name += "depth2d_ms"; + else if (img_type.arrayed) + img_type_name += "depth2d_array"; + else + img_type_name += "depth2d"; + break; + case Dim3D: + img_type_name += "depth3d_unsupported_by_metal"; + break; + case DimCube: + if (!msl_options.emulate_cube_array) + img_type_name += (img_type.arrayed ? "depthcube_array" : "depthcube"); + else + img_type_name += (img_type.arrayed ? "depth2d_array" : "depthcube"); + break; + default: + img_type_name += "unknown_depth_texture_type"; + break; + } + } + else + { + switch (img_type.dim) + { + case DimBuffer: + if (img_type.ms || img_type.arrayed) + SPIRV_CROSS_THROW("Cannot use texel buffers with multisampling or array layers."); + + if (msl_options.texture_buffer_native) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Native texture_buffer type is only supported in MSL 2.1."); + img_type_name = "texture_buffer"; + } + else + img_type_name += "texture2d"; + break; + case Dim1D: + case Dim2D: + case DimSubpassData: + { + bool subpass_array = + img_type.dim == DimSubpassData && (msl_options.multiview || msl_options.arrayed_subpass_input); + if (img_type.dim == Dim1D && !msl_options.texture_1D_as_2D) + { + // Use a native Metal 1D texture + img_type_name += (img_type.arrayed ? "texture1d_array" : "texture1d"); + break; + } + + // Use Metal's native frame-buffer fetch API for subpass inputs. + if (type_is_msl_framebuffer_fetch(type)) + { + auto img_type_4 = get(img_type.type); + img_type_4.vecsize = 4; + return type_to_glsl(img_type_4); + } + if (img_type.ms && (img_type.arrayed || subpass_array)) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Multisampled array textures are supported from 2.1."); + img_type_name += "texture2d_ms_array"; + } + else if (img_type.ms) + img_type_name += "texture2d_ms"; + else if (img_type.arrayed || subpass_array) + img_type_name += "texture2d_array"; + else + img_type_name += "texture2d"; + break; + } + case Dim3D: + img_type_name += "texture3d"; + break; + case DimCube: + if (!msl_options.emulate_cube_array) + img_type_name += (img_type.arrayed ? "texturecube_array" : "texturecube"); + else + img_type_name += (img_type.arrayed ? "texture2d_array" : "texturecube"); + break; + default: + img_type_name += "unknown_texture_type"; + break; + } + } + + // Append the pixel type + img_type_name += "<"; + img_type_name += type_to_glsl(get(img_type.type)); + + // For unsampled images, append the sample/read/write access qualifier. + // For kernel images, the access qualifier my be supplied directly by SPIR-V. + // Otherwise it may be set based on whether the image is read from or written to within the shader. + if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.dim != DimSubpassData) + { + auto *p_var = maybe_get_backing_variable(id); + if (p_var && p_var->basevariable) + p_var = maybe_get(p_var->basevariable); + + bool has_access_qualifier = true; + + switch (img_type.access) + { + case AccessQualifierReadOnly: + img_type_name += ", access::read"; + break; + + case AccessQualifierWriteOnly: + img_type_name += ", access::write"; + break; + + case AccessQualifierReadWrite: + img_type_name += ", access::read_write"; + break; + + default: + { + if (p_var && !has_decoration(p_var->self, DecorationNonWritable)) + { + img_type_name += ", access::"; + + if (!has_decoration(p_var->self, DecorationNonReadable)) + img_type_name += "read_"; + + img_type_name += "write"; + } + else + { + has_access_qualifier = false; + } + break; + } + } + + if (p_var && has_decoration(p_var->self, DecorationCoherent) && msl_options.supports_msl_version(3, 2)) + { + // Cannot declare memory_coherence_device without access qualifier. + if (!has_access_qualifier) + img_type_name += ", access::read"; + img_type_name += ", memory_coherence_device"; + } + } + + img_type_name += ">"; + + return img_type_name; +} + +void CompilerMSL::emit_subgroup_op(const Instruction &i) +{ + const uint32_t *ops = stream(i); + auto op = static_cast(i.op); + + if (msl_options.emulate_subgroups) + { + // In this mode, only the GroupNonUniform cap is supported. The only op + // we need to handle, then, is OpGroupNonUniformElect. + if (op != OpGroupNonUniformElect) + SPIRV_CROSS_THROW("Subgroup emulation does not support operations other than Elect."); + // In this mode, the subgroup size is assumed to be one, so every invocation + // is elected. + emit_op(ops[0], ops[1], "true", true); + return; + } + + // Metal 2.0 is required. iOS only supports quad ops on 11.0 (2.0), with + // full support in 13.0 (2.2). macOS only supports broadcast and shuffle on + // 10.13 (2.0), with full support in 10.14 (2.1). + // Note that Apple GPUs before A13 make no distinction between a quad-group + // and a SIMD-group; all SIMD-groups are quad-groups on those. + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroups are only supported in Metal 2.0 and up."); + + // If we need to do implicit bitcasts, make sure we do it with the correct type. + uint32_t integer_width = get_integer_width_for_instruction(i); + auto int_type = to_signed_basetype(integer_width); + auto uint_type = to_unsigned_basetype(integer_width); + + if (msl_options.is_ios() && (!msl_options.supports_msl_version(2, 3) || !msl_options.ios_use_simdgroup_functions)) + { + switch (op) + { + default: + SPIRV_CROSS_THROW("Subgroup ops beyond broadcast, ballot, and shuffle on iOS require Metal 2.3 and up."); + case OpGroupNonUniformBroadcastFirst: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("BroadcastFirst on iOS requires Metal 2.2 and up."); + break; + case OpGroupNonUniformElect: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Elect on iOS requires Metal 2.2 and up."); + break; + case OpGroupNonUniformAny: + case OpGroupNonUniformAll: + case OpGroupNonUniformAllEqual: + case OpGroupNonUniformBallot: + case OpGroupNonUniformInverseBallot: + case OpGroupNonUniformBallotBitExtract: + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + case OpGroupNonUniformBallotBitCount: + case OpSubgroupBallotKHR: + case OpSubgroupAllKHR: + case OpSubgroupAnyKHR: + case OpSubgroupAllEqualKHR: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Ballot ops on iOS requires Metal 2.2 and up."); + break; + case OpGroupNonUniformRotateKHR: + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Rotate on iOS requires Metal 2.2 and up."); + break; + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + case OpGroupNonUniformQuadSwap: + case OpGroupNonUniformQuadBroadcast: + case OpSubgroupReadInvocationKHR: + break; + } + } + + if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1)) + { + switch (op) + { + default: + SPIRV_CROSS_THROW("Subgroup ops beyond broadcast and shuffle on macOS require Metal 2.1 and up."); + case OpGroupNonUniformBroadcast: + case OpGroupNonUniformShuffle: + case OpGroupNonUniformShuffleXor: + case OpGroupNonUniformShuffleUp: + case OpGroupNonUniformShuffleDown: + case OpSubgroupReadInvocationKHR: + break; + } + } + + uint32_t op_idx = 0; + uint32_t result_type = ops[op_idx++]; + uint32_t id = ops[op_idx++]; + + Scope scope; + switch (op) + { + // These earlier instructions don't have the scope operand. + case OpSubgroupBallotKHR: + case OpSubgroupFirstInvocationKHR: + case OpSubgroupReadInvocationKHR: + case OpSubgroupAllKHR: + case OpSubgroupAnyKHR: + case OpSubgroupAllEqualKHR: + // These instructions are always quad-scoped and thus do not have a scope operand. + case OpGroupNonUniformQuadAllKHR: + case OpGroupNonUniformQuadAnyKHR: + scope = ScopeSubgroup; + break; + default: + scope = static_cast(evaluate_constant_u32(ops[op_idx++])); + break; + } + if (scope != ScopeSubgroup) + SPIRV_CROSS_THROW("Only subgroup scope is supported."); + + switch (op) + { + case OpGroupNonUniformElect: + if (msl_options.use_quadgroup_operation()) + emit_op(result_type, id, "quad_is_first()", false); + else + emit_op(result_type, id, "simd_is_first()", false); + break; + + case OpGroupNonUniformBroadcast: + case OpSubgroupReadInvocationKHR: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvSubgroupBroadcast"); + break; + + case OpGroupNonUniformBroadcastFirst: + case OpSubgroupFirstInvocationKHR: + emit_unary_func_op(result_type, id, ops[op_idx], "spvSubgroupBroadcastFirst"); + break; + + case OpGroupNonUniformBallot: + case OpSubgroupBallotKHR: + emit_unary_func_op(result_type, id, ops[op_idx], "spvSubgroupBallot"); + break; + + case OpGroupNonUniformInverseBallot: + emit_binary_func_op(result_type, id, ops[op_idx], builtin_subgroup_invocation_id_id, "spvSubgroupBallotBitExtract"); + break; + + case OpGroupNonUniformBallotBitExtract: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvSubgroupBallotBitExtract"); + break; + + case OpGroupNonUniformBallotFindLSB: + emit_binary_func_op(result_type, id, ops[op_idx], builtin_subgroup_size_id, "spvSubgroupBallotFindLSB"); + break; + + case OpGroupNonUniformBallotFindMSB: + emit_binary_func_op(result_type, id, ops[op_idx], builtin_subgroup_size_id, "spvSubgroupBallotFindMSB"); + break; + + case OpGroupNonUniformBallotBitCount: + { + auto operation = static_cast(ops[op_idx++]); + switch (operation) + { + case GroupOperationReduce: + emit_binary_func_op(result_type, id, ops[op_idx], builtin_subgroup_size_id, "spvSubgroupBallotBitCount"); + break; + case GroupOperationInclusiveScan: + emit_binary_func_op(result_type, id, ops[op_idx], builtin_subgroup_invocation_id_id, + "spvSubgroupBallotInclusiveBitCount"); + break; + case GroupOperationExclusiveScan: + emit_binary_func_op(result_type, id, ops[op_idx], builtin_subgroup_invocation_id_id, + "spvSubgroupBallotExclusiveBitCount"); + break; + default: + SPIRV_CROSS_THROW("Invalid BitCount operation."); + } + break; + } + + case OpGroupNonUniformShuffle: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvSubgroupShuffle"); + break; + + case OpGroupNonUniformShuffleXor: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvSubgroupShuffleXor"); + break; + + case OpGroupNonUniformShuffleUp: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvSubgroupShuffleUp"); + break; + + case OpGroupNonUniformShuffleDown: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvSubgroupShuffleDown"); + break; + + case OpGroupNonUniformRotateKHR: + { + if (i.length > 5) + { + // MSL does not have a cluster size parameter, so calculate the invocation ID manually and using a shuffle. + auto delta_expr = enclose_expression(to_unpacked_expression(ops[op_idx + 1])); + auto cluster_size_minus_one = evaluate_constant_u32(ops[op_idx + 2]) - 1; + auto local_id_expr = to_unpacked_expression(scope == ScopeSubgroup + ? builtin_subgroup_invocation_id_id : builtin_local_invocation_index_id); + auto shuffle_idx = join("((", local_id_expr, " + ", delta_expr, ")", " & ", std::to_string(cluster_size_minus_one), + ") + (", local_id_expr, " & ", std::to_string(~cluster_size_minus_one), ")"); + emit_op(result_type, id, join("spvSubgroupShuffle(", to_unpacked_expression(ops[op_idx]), ", ", shuffle_idx, ")"), false); + } else + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvSubgroupRotate"); + break; + } + + case OpGroupNonUniformAll: + case OpSubgroupAllKHR: + if (msl_options.use_quadgroup_operation()) + emit_unary_func_op(result_type, id, ops[op_idx], "quad_all"); + else + emit_unary_func_op(result_type, id, ops[op_idx], "simd_all"); + break; + + case OpGroupNonUniformAny: + case OpSubgroupAnyKHR: + if (msl_options.use_quadgroup_operation()) + emit_unary_func_op(result_type, id, ops[op_idx], "quad_any"); + else + emit_unary_func_op(result_type, id, ops[op_idx], "simd_any"); + break; + + case OpGroupNonUniformAllEqual: + case OpSubgroupAllEqualKHR: + emit_unary_func_op(result_type, id, ops[op_idx], "spvSubgroupAllEqual"); + break; + + // clang-format off +#define MSL_GROUP_OP(op, msl_op) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[op_idx++]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[op_idx], "simd_" #msl_op); \ + else if (operation == GroupOperationInclusiveScan) \ + emit_unary_func_op(result_type, id, ops[op_idx], "simd_prefix_inclusive_" #msl_op); \ + else if (operation == GroupOperationExclusiveScan) \ + emit_unary_func_op(result_type, id, ops[op_idx], "simd_prefix_exclusive_" #msl_op); \ + else if (operation == GroupOperationClusteredReduce) \ + { \ + uint32_t cluster_size = evaluate_constant_u32(ops[op_idx + 1]); \ + if (get_execution_model() != ExecutionModelFragment || msl_options.supports_msl_version(2, 2)) \ + add_spv_func_and_recompile(SPVFuncImplSubgroupClustered##op); \ + emit_subgroup_cluster_op(result_type, id, cluster_size, ops[op_idx], #msl_op); \ + } \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + MSL_GROUP_OP(FAdd, sum) + MSL_GROUP_OP(FMul, product) + MSL_GROUP_OP(IAdd, sum) + MSL_GROUP_OP(IMul, product) +#undef MSL_GROUP_OP + // The others, unfortunately, don't support InclusiveScan or ExclusiveScan. + +#define MSL_GROUP_OP(op, msl_op) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[op_idx++]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op(result_type, id, ops[op_idx], "simd_" #msl_op); \ + else if (operation == GroupOperationInclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support InclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationExclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support ExclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationClusteredReduce) \ + { \ + uint32_t cluster_size = evaluate_constant_u32(ops[op_idx + 1]); \ + if (get_execution_model() != ExecutionModelFragment || msl_options.supports_msl_version(2, 2)) \ + add_spv_func_and_recompile(SPVFuncImplSubgroupClustered##op); \ + emit_subgroup_cluster_op(result_type, id, cluster_size, ops[op_idx], #msl_op); \ + } \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + +#define MSL_GROUP_OP_CAST(op, msl_op, type) \ +case OpGroupNonUniform##op: \ + { \ + auto operation = static_cast(ops[op_idx++]); \ + if (operation == GroupOperationReduce) \ + emit_unary_func_op_cast(result_type, id, ops[op_idx], "simd_" #msl_op, type, type); \ + else if (operation == GroupOperationInclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support InclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationExclusiveScan) \ + SPIRV_CROSS_THROW("Metal doesn't support ExclusiveScan for OpGroupNonUniform" #op "."); \ + else if (operation == GroupOperationClusteredReduce) \ + { \ + uint32_t cluster_size = evaluate_constant_u32(ops[op_idx + 1]); \ + if (get_execution_model() != ExecutionModelFragment || msl_options.supports_msl_version(2, 2)) \ + add_spv_func_and_recompile(SPVFuncImplSubgroupClustered##op); \ + emit_subgroup_cluster_op_cast(result_type, id, cluster_size, ops[op_idx], #msl_op, type, type); \ + } \ + else \ + SPIRV_CROSS_THROW("Invalid group operation."); \ + break; \ + } + + MSL_GROUP_OP(FMin, min) + MSL_GROUP_OP(FMax, max) + MSL_GROUP_OP_CAST(SMin, min, int_type) + MSL_GROUP_OP_CAST(SMax, max, int_type) + MSL_GROUP_OP_CAST(UMin, min, uint_type) + MSL_GROUP_OP_CAST(UMax, max, uint_type) + MSL_GROUP_OP(BitwiseAnd, and) + MSL_GROUP_OP(BitwiseOr, or) + MSL_GROUP_OP(BitwiseXor, xor) + // Metal doesn't support boolean types in SIMD-group operations, so we + // have to emit some casts. + MSL_GROUP_OP_CAST(LogicalAnd, and, SPIRType::UShort) + MSL_GROUP_OP_CAST(LogicalOr, or, SPIRType::UShort) + MSL_GROUP_OP_CAST(LogicalXor, xor, SPIRType::UShort) + // clang-format on +#undef MSL_GROUP_OP +#undef MSL_GROUP_OP_CAST + + case OpGroupNonUniformQuadSwap: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvQuadSwap"); + break; + + case OpGroupNonUniformQuadBroadcast: + emit_binary_func_op(result_type, id, ops[op_idx], ops[op_idx + 1], "spvQuadBroadcast"); + break; + + case OpGroupNonUniformQuadAllKHR: + emit_unary_func_op(result_type, id, ops[op_idx], "quad_all"); + break; + + case OpGroupNonUniformQuadAnyKHR: + emit_unary_func_op(result_type, id, ops[op_idx], "quad_any"); + break; + + default: + SPIRV_CROSS_THROW("Invalid opcode for subgroup."); + } + + register_control_dependent_expression(id); +} + +void CompilerMSL::emit_subgroup_cluster_op(uint32_t result_type, uint32_t result_id, uint32_t cluster_size, + uint32_t op0, const char *op) +{ + if (get_execution_model() == ExecutionModelFragment && !msl_options.supports_msl_version(2, 2)) + { + if (cluster_size == 4) + { + emit_unary_func_op(result_type, result_id, op0, join("quad_", op).c_str()); + return; + } + SPIRV_CROSS_THROW("Cluster sizes other than 4 in fragment shaders require MSL 2.2."); + } + bool forward = should_forward(op0); + emit_op(result_type, result_id, + join("spvClustered_", op, "<", cluster_size, ">(", to_unpacked_expression(op0), ", ", + to_expression(builtin_subgroup_invocation_id_id), ")"), + forward); + inherit_expression_dependencies(result_id, op0); +} + +void CompilerMSL::emit_subgroup_cluster_op_cast(uint32_t result_type, uint32_t result_id, uint32_t cluster_size, + uint32_t op0, const char *op, SPIRType::BaseType input_type, + SPIRType::BaseType expected_result_type) +{ + if (get_execution_model() == ExecutionModelFragment && !msl_options.supports_msl_version(2, 2)) + { + if (cluster_size == 4) + { + emit_unary_func_op_cast(result_type, result_id, op0, join("quad_", op).c_str(), input_type, + expected_result_type); + return; + } + SPIRV_CROSS_THROW("Cluster sizes other than 4 in fragment shaders require MSL 2.2."); + } + + auto &out_type = get(result_type); + auto &expr_type = expression_type(op0); + auto expected_type = out_type; + + // Bit-widths might be different in unary cases because we use it for SConvert/UConvert and friends. + expected_type.basetype = input_type; + expected_type.width = expr_type.width; + + string cast_op; + if (expr_type.basetype != input_type) + { + if (expr_type.basetype == SPIRType::Boolean) + cast_op = join(type_to_glsl(expected_type), "(", to_unpacked_expression(op0), ")"); + else + cast_op = bitcast_glsl(expected_type, op0); + } + else + cast_op = to_unpacked_expression(op0); + + string sg_op = join("spvClustered_", op, "<", cluster_size, ">"); + string expr; + if (out_type.basetype != expected_result_type) + { + expected_type.basetype = expected_result_type; + expected_type.width = out_type.width; + if (out_type.basetype == SPIRType::Boolean) + expr = type_to_glsl(out_type); + else + expr = bitcast_glsl_op(out_type, expected_type); + expr += '('; + expr += join(sg_op, "(", cast_op, ", ", to_expression(builtin_subgroup_invocation_id_id), ")"); + expr += ')'; + } + else + { + expr += join(sg_op, "(", cast_op, ", ", to_expression(builtin_subgroup_invocation_id_id), ")"); + } + + emit_op(result_type, result_id, expr, should_forward(op0)); + inherit_expression_dependencies(result_id, op0); +} + +// Note: Metal forbids bitcasting to/from 'bool' using as_type. This function is used widely +// for generating casts in the backend. To avoid generating illegal MSL when the canonical +// function constant type (from deduplicated SpecId) is Boolean, fall back to value-cast in +// that case by returning type_to_glsl(out_type) instead of as_type<...>. +string CompilerMSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type) +{ + if (out_type.basetype == in_type.basetype) + return ""; + + // Avoid bitcasting to/from booleans in MSL; use value cast instead. + if (out_type.basetype == SPIRType::Boolean || in_type.basetype == SPIRType::Boolean) + return type_to_glsl(out_type); + + bool integral_cast = type_is_integral(out_type) && type_is_integral(in_type) && (out_type.vecsize == in_type.vecsize); + bool same_size_cast = (out_type.width * out_type.vecsize) == (in_type.width * in_type.vecsize); + + // Bitcasting can only be used between types of the same overall size. + // And always formally cast between integers, because it's trivial, and also + // because Metal can internally cast the results of some integer ops to a larger + // size (eg. short shift right becomes int), which means chaining integer ops + // together may introduce size variations that SPIR-V doesn't know about. + if (same_size_cast && !integral_cast) + return "as_type<" + type_to_glsl(out_type) + ">"; + else + return type_to_glsl(out_type); +} + +bool CompilerMSL::emit_complex_bitcast(uint32_t, uint32_t, uint32_t) +{ + // This is handled from the outside where we deal with PtrToU/UToPtr and friends. + return false; +} + +// Returns an MSL string identifying the name of a SPIR-V builtin. +// Output builtins are qualified with the name of the stage out structure. +string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage) +{ + switch (builtin) + { + // Handle HLSL-style 0-based vertex/instance index. + // Override GLSL compiler strictness + case BuiltInVertexId: + ensure_builtin(StorageClassInput, BuiltInVertexId); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_vertex_arg != TriState::No) + needs_base_vertex_arg = TriState::Yes; + return "gl_VertexID"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseVertex); + return "(gl_VertexID - gl_BaseVertex)"; + } + } + else + { + return "gl_VertexID"; + } + case BuiltInInstanceId: + ensure_builtin(StorageClassInput, BuiltInInstanceId); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_instance_arg != TriState::No) + needs_base_instance_arg = TriState::Yes; + return "gl_InstanceID"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseInstance); + return "(gl_InstanceID - gl_BaseInstance)"; + } + } + else + { + return "gl_InstanceID"; + } + case BuiltInVertexIndex: + ensure_builtin(StorageClassInput, BuiltInVertexIndex); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_vertex_arg != TriState::No) + needs_base_vertex_arg = TriState::Yes; + return "gl_VertexIndex"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseVertex); + return "(gl_VertexIndex - gl_BaseVertex)"; + } + } + else + { + return "gl_VertexIndex"; + } + case BuiltInInstanceIndex: + ensure_builtin(StorageClassInput, BuiltInInstanceIndex); + if (msl_options.enable_base_index_zero && msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + if (builtin_declaration) + { + if (needs_base_instance_arg != TriState::No) + needs_base_instance_arg = TriState::Yes; + return "gl_InstanceIndex"; + } + else + { + ensure_builtin(StorageClassInput, BuiltInBaseInstance); + return "(gl_InstanceIndex - gl_BaseInstance)"; + } + } + else + { + return "gl_InstanceIndex"; + } + case BuiltInBaseVertex: + if (msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + needs_base_vertex_arg = TriState::No; + return "gl_BaseVertex"; + } + else + { + SPIRV_CROSS_THROW("BaseVertex requires Metal 1.1 and Mac or Apple A9+ hardware."); + } + case BuiltInBaseInstance: + if (msl_options.supports_msl_version(1, 1) && + (msl_options.ios_support_base_vertex_instance || msl_options.is_macos())) + { + needs_base_instance_arg = TriState::No; + return "gl_BaseInstance"; + } + else + { + SPIRV_CROSS_THROW("BaseInstance requires Metal 1.1 and Mac or Apple A9+ hardware."); + } + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + // When used in the entry function, output builtins are qualified with output struct name. + // Test storage class as NOT Input, as output builtins might be part of generic type. + // Also don't do this for tessellation control shaders. + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + /* fallthrough */ + case BuiltInFragDepth: + case BuiltInFragStencilRefEXT: + if ((builtin == BuiltInFragDepth && !msl_options.enable_frag_depth_builtin) || + (builtin == BuiltInFragStencilRefEXT && !msl_options.enable_frag_stencil_ref_builtin)) + break; + /* fallthrough */ + case BuiltInPosition: + case BuiltInPointSize: + case BuiltInClipDistance: + case BuiltInCullDistance: + case BuiltInLayer: + if (is_tesc_shader()) + break; + if (is_mesh_shader()) + break; + if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point) && + !is_stage_output_builtin_masked(builtin)) + return stage_out_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage); + break; + + case BuiltInSampleMask: + if (storage == StorageClassInput && current_function && (current_function->self == ir.default_entry_point) && + (has_additional_fixed_sample_mask() || needs_sample_id)) + { + string samp_mask_in; + samp_mask_in += "(" + CompilerGLSL::builtin_to_glsl(builtin, storage); + if (has_additional_fixed_sample_mask()) + samp_mask_in += " & " + additional_fixed_sample_mask_str(); + if (needs_sample_id) + samp_mask_in += " & (1 << gl_SampleID)"; + samp_mask_in += ")"; + return samp_mask_in; + } + if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point) && + !is_stage_output_builtin_masked(builtin)) + return stage_out_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage); + break; + + case BuiltInBaryCoordKHR: + case BuiltInBaryCoordNoPerspKHR: + if (storage == StorageClassInput && current_function && (current_function->self == ir.default_entry_point)) + return stage_in_var_name + "." + CompilerGLSL::builtin_to_glsl(builtin, storage); + break; + + case BuiltInTessLevelOuter: + if (is_tesc_shader() && storage != StorageClassInput && current_function && + (current_function->self == ir.default_entry_point)) + { + return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "].edgeTessellationFactor"); + } + break; + + case BuiltInTessLevelInner: + if (is_tesc_shader() && storage != StorageClassInput && current_function && + (current_function->self == ir.default_entry_point)) + { + return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), + "].insideTessellationFactor"); + } + break; + + case BuiltInHelperInvocation: + if (needs_manual_helper_invocation_updates()) + break; + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.3 on iOS."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("simd_is_helper_thread() requires version 2.1 on macOS."); + // In SPIR-V 1.6 with Volatile HelperInvocation, we cannot emit a fixup early. + return "simd_is_helper_thread()"; + + case BuiltInPrimitiveId: + return "gl_PrimitiveID"; + + default: + break; + } + + return CompilerGLSL::builtin_to_glsl(builtin, storage); +} + +// Returns an MSL string attribute qualifer for a SPIR-V builtin +string CompilerMSL::builtin_qualifier(BuiltIn builtin) +{ + auto &execution = get_entry_point(); + + switch (builtin) + { + // Vertex function in + case BuiltInVertexId: + return "vertex_id"; + case BuiltInVertexIndex: + return "vertex_id"; + case BuiltInBaseVertex: + return "base_vertex"; + case BuiltInInstanceId: + return "instance_id"; + case BuiltInInstanceIndex: + return "instance_id"; + case BuiltInBaseInstance: + return "base_instance"; + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + // Vertex function out + case BuiltInClipDistance: + return "clip_distance"; + case BuiltInCullDistance: + return "cull_distance"; + case BuiltInPointSize: + return "point_size"; + case BuiltInPosition: + if (position_invariant) + { + if (!msl_options.supports_msl_version(2, 1)) + SPIRV_CROSS_THROW("Invariant position is only supported on MSL 2.1 and up."); + return "position, invariant"; + } + else + return "position"; + case BuiltInLayer: + return "render_target_array_index"; + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + return "viewport_array_index"; + + // Tess. control function in + case BuiltInInvocationId: + if (msl_options.multi_patch_workgroup) + { + // Shouldn't be reached. + SPIRV_CROSS_THROW("InvocationId is computed manually with multi-patch workgroups in MSL."); + } + return "thread_index_in_threadgroup"; + case BuiltInPatchVertices: + // Shouldn't be reached. + SPIRV_CROSS_THROW("PatchVertices is derived from the auxiliary buffer in MSL."); + case BuiltInPrimitiveId: + switch (execution.model) + { + case ExecutionModelTessellationControl: + if (msl_options.multi_patch_workgroup) + { + // Shouldn't be reached. + SPIRV_CROSS_THROW("PrimitiveId is computed manually with multi-patch workgroups in MSL."); + } + return "threadgroup_position_in_grid"; + case ExecutionModelTessellationEvaluation: + return "patch_id"; + case ExecutionModelFragment: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("PrimitiveId on iOS requires MSL 2.3."); + else if (msl_options.is_macos() && !msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("PrimitiveId on macOS requires MSL 2.2."); + return "primitive_id"; + case ExecutionModelMeshEXT: + return "primitive_id"; + default: + SPIRV_CROSS_THROW("PrimitiveId is not supported in this execution model."); + } + + // Tess. control function out + case BuiltInTessLevelOuter: + case BuiltInTessLevelInner: + // Shouldn't be reached. + SPIRV_CROSS_THROW("Tessellation levels are handled specially in MSL."); + + // Tess. evaluation function in + case BuiltInTessCoord: + return "position_in_patch"; + + // Fragment function in + case BuiltInFrontFacing: + return "front_facing"; + case BuiltInPointCoord: + return "point_coord"; + case BuiltInFragCoord: + return "position"; + case BuiltInSampleId: + return "sample_id"; + case BuiltInSampleMask: + return "sample_mask"; + case BuiltInSamplePosition: + // Shouldn't be reached. + SPIRV_CROSS_THROW("Sample position is retrieved by a function in MSL."); + case BuiltInViewIndex: + if (execution.model != ExecutionModelFragment && execution.model != ExecutionModelMeshEXT) + SPIRV_CROSS_THROW("ViewIndex is handled specially outside fragment shaders."); + // The ViewIndex was implicitly used in the prior stages to set the render_target_array_index, + // so we can get it from there. + return "render_target_array_index"; + + // Fragment function out + case BuiltInFragDepth: + if (execution.flags.get(ExecutionModeDepthGreater)) + return "depth(greater)"; + else if (execution.flags.get(ExecutionModeDepthLess)) + return "depth(less)"; + else + return "depth(any)"; + + case BuiltInFragStencilRefEXT: + return "stencil"; + + // Compute function in + case BuiltInGlobalInvocationId: + return "thread_position_in_grid"; + + case BuiltInWorkgroupSize: + return "threads_per_threadgroup"; + + case BuiltInWorkgroupId: + return "threadgroup_position_in_grid"; + + case BuiltInNumWorkgroups: + return "threadgroups_per_grid"; + + case BuiltInLocalInvocationId: + return "thread_position_in_threadgroup"; + + case BuiltInLocalInvocationIndex: + return "thread_index_in_threadgroup"; + + case BuiltInSubgroupSize: + if (msl_options.emulate_subgroups || msl_options.fixed_subgroup_size != 0) + // Shouldn't be reached. + SPIRV_CROSS_THROW("Emitting threads_per_simdgroup attribute with fixed subgroup size??"); + if (execution.model == ExecutionModelFragment) + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("threads_per_simdgroup requires Metal 2.2 in fragment shaders."); + return "threads_per_simdgroup"; + } + else + { + // thread_execution_width is an alias for threads_per_simdgroup, and it's only available since 1.0, + // but not in fragment. + if (msl_options.supports_msl_version(3, 0)) + return "threads_per_simdgroup"; + else + return "thread_execution_width"; + } + + case BuiltInNumSubgroups: + if (msl_options.emulate_subgroups) + // Shouldn't be reached. + SPIRV_CROSS_THROW("NumSubgroups is handled specially with emulation."); + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroup builtins require Metal 2.0."); + return msl_options.use_quadgroup_operation() ? "quadgroups_per_threadgroup" : "simdgroups_per_threadgroup"; + + case BuiltInSubgroupId: + if (msl_options.emulate_subgroups) + // Shouldn't be reached. + SPIRV_CROSS_THROW("SubgroupId is handled specially with emulation."); + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroup builtins require Metal 2.0."); + return msl_options.use_quadgroup_operation() ? "quadgroup_index_in_threadgroup" : "simdgroup_index_in_threadgroup"; + + case BuiltInSubgroupLocalInvocationId: + if (msl_options.emulate_subgroups) + // Shouldn't be reached. + SPIRV_CROSS_THROW("SubgroupLocalInvocationId is handled specially with emulation."); + if (execution.model == ExecutionModelFragment) + { + if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("thread_index_in_simdgroup requires Metal 2.2 in fragment shaders."); + return "thread_index_in_simdgroup"; + } + else if (execution.model == ExecutionModelKernel || execution.model == ExecutionModelGLCompute || + execution.model == ExecutionModelTaskEXT || execution.model == ExecutionModelMeshEXT || + execution.model == ExecutionModelTessellationControl || + (execution.model == ExecutionModelVertex && msl_options.vertex_for_tessellation)) + { + // We are generating a Metal kernel function. + if (!msl_options.supports_msl_version(2)) + SPIRV_CROSS_THROW("Subgroup builtins in kernel functions require Metal 2.0."); + return msl_options.use_quadgroup_operation() ? "thread_index_in_quadgroup" : "thread_index_in_simdgroup"; + } + else + SPIRV_CROSS_THROW("Subgroup builtins are not available in this type of function."); + + case BuiltInSubgroupEqMask: + case BuiltInSubgroupGeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupLtMask: + // Shouldn't be reached. + SPIRV_CROSS_THROW("Subgroup ballot masks are handled specially in MSL."); + + case BuiltInBaryCoordKHR: + case BuiltInBaryCoordNoPerspKHR: + if (msl_options.is_ios() && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.3 and above on iOS."); + else if (!msl_options.supports_msl_version(2, 2)) + SPIRV_CROSS_THROW("Barycentrics are only supported in MSL 2.2 and above on macOS."); + return "barycentric_coord"; + + case BuiltInCullPrimitiveEXT: + return "primitive_culled"; + + default: + return "unsupported-built-in"; + } +} + +// Returns an MSL string type declaration for a SPIR-V builtin +string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id) +{ + switch (builtin) + { + // Vertex function in + case BuiltInVertexId: + return "uint"; + case BuiltInVertexIndex: + return "uint"; + case BuiltInBaseVertex: + return "uint"; + case BuiltInInstanceId: + return "uint"; + case BuiltInInstanceIndex: + return "uint"; + case BuiltInBaseInstance: + return "uint"; + case BuiltInDrawIndex: + SPIRV_CROSS_THROW("DrawIndex is not supported in MSL."); + + // Vertex function out + case BuiltInClipDistance: + case BuiltInCullDistance: + return "float"; + case BuiltInPointSize: + return "float"; + case BuiltInPosition: + return "float4"; + case BuiltInLayer: + return "uint"; + case BuiltInViewportIndex: + if (!msl_options.supports_msl_version(2, 0)) + SPIRV_CROSS_THROW("ViewportIndex requires Metal 2.0."); + return "uint"; + + // Tess. control function in + case BuiltInInvocationId: + return "uint"; + case BuiltInPatchVertices: + return "uint"; + case BuiltInPrimitiveId: + return "uint"; + + // Tess. control function out + case BuiltInTessLevelInner: + if (is_tese_shader()) + return (msl_options.raw_buffer_tese_input || is_tessellating_triangles()) ? "float" : "float2"; + return "half"; + case BuiltInTessLevelOuter: + if (is_tese_shader()) + return (msl_options.raw_buffer_tese_input || is_tessellating_triangles()) ? "float" : "float4"; + return "half"; + + // Tess. evaluation function in + case BuiltInTessCoord: + return "float3"; + + // Fragment function in + case BuiltInFrontFacing: + return "bool"; + case BuiltInPointCoord: + return "float2"; + case BuiltInFragCoord: + return "float4"; + case BuiltInSampleId: + return "uint"; + case BuiltInSampleMask: + return "uint"; + case BuiltInSamplePosition: + return "float2"; + case BuiltInViewIndex: + return "uint"; + + case BuiltInHelperInvocation: + return "bool"; + + case BuiltInBaryCoordKHR: + case BuiltInBaryCoordNoPerspKHR: + // Use the type as declared, can be 1, 2 or 3 components. + return type_to_glsl(get_variable_data_type(get(id))); + + // Fragment function out + case BuiltInFragDepth: + return "float"; + + case BuiltInFragStencilRefEXT: + return "uint"; + + // Compute function in + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInNumWorkgroups: + case BuiltInWorkgroupId: + case BuiltInWorkgroupSize: + return "uint3"; + case BuiltInLocalInvocationIndex: + case BuiltInNumSubgroups: + case BuiltInSubgroupId: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + return "uint"; + case BuiltInSubgroupEqMask: + case BuiltInSubgroupGeMask: + case BuiltInSubgroupGtMask: + case BuiltInSubgroupLeMask: + case BuiltInSubgroupLtMask: + return "uint4"; + + case BuiltInDeviceIndex: + return "int"; + + case BuiltInPrimitivePointIndicesEXT: + return "uint"; + case BuiltInPrimitiveLineIndicesEXT: + return "uint2"; + case BuiltInPrimitiveTriangleIndicesEXT: + return "uint3"; + + default: + return "unsupported-built-in-type"; + } +} + +// Returns the declaration of a built-in argument to a function +string CompilerMSL::built_in_func_arg(BuiltIn builtin, bool prefix_comma) +{ + string bi_arg; + if (prefix_comma) + bi_arg += ", "; + + // Handle HLSL-style 0-based vertex/instance index. + builtin_declaration = true; + bi_arg += builtin_type_decl(builtin); + bi_arg += string(" ") + builtin_to_glsl(builtin, StorageClassInput); + bi_arg += string(" [[") + builtin_qualifier(builtin) + string("]]"); + builtin_declaration = false; + + return bi_arg; +} + +TypeID CompilerMSL::get_physical_member_type_id(const SPIRType &type, uint32_t index) const +{ + if (member_is_remapped_physical_type(type, index)) + return get_extended_member_decoration(type.self, index, SPIRVCrossDecorationPhysicalTypeID); + else + return type.member_types[index]; +} + +SPIRType CompilerMSL::get_presumed_input_type(const SPIRType &ib_type, uint32_t index) const +{ + SPIRType type = get(get_physical_member_type_id(ib_type, index)); + uint32_t loc = get_member_decoration(ib_type.self, index, DecorationLocation); + uint32_t cmp = get_member_decoration(ib_type.self, index, DecorationComponent); + auto p_va = inputs_by_location.find({loc, cmp}); + if (p_va != end(inputs_by_location) && p_va->second.vecsize > type.vecsize) + type.vecsize = p_va->second.vecsize; + + return type; +} + +uint32_t CompilerMSL::get_declared_type_array_stride_msl(TypeID type_id, const SPIRType *special_type, bool is_packed, bool row_major) const +{ + // Array stride in MSL is always size * array_size. sizeof(float3) == 16, + // unlike GLSL and HLSL where array stride would be 16 and size 12. + + // We could use parent type here and recurse, but that makes creating physical type remappings + // far more complicated. We'd rather just create the final type, and ignore having to create the entire type + // hierarchy in order to compute this value, so make a temporary type on the stack. + + uint32_t value_size; + + // We don't always use proper type hierarchy for synthesized types, so be robust. + if (type_id && get(type_id).parent_type) + { + bool uses_declared_array_stride = false; + + uint32_t array_stride = 0; + TypeID basic_type_id = type_id; + while (is_array(get(basic_type_id))) + { + array_stride = get_decoration(basic_type_id, DecorationArrayStride); + auto parent_type_id = get(basic_type_id).parent_type; + // If the base struct itself has ArrayStride decoration, it will be padded on-demand. + uses_declared_array_stride = has_decoration(parent_type_id, DecorationArrayStride); + if (parent_type_id) + basic_type_id = parent_type_id; + else + break; + } + + if (array_stride && uses_declared_array_stride) + value_size = array_stride; + else + value_size = get_declared_type_size_msl(basic_type_id, nullptr, is_packed, row_major); + } + else + { + // Old, broken path. + auto basic_type = type_id ? get(type_id) : *special_type; + basic_type.array.clear(); + basic_type.array_size_literal.clear(); + value_size = get_declared_type_size_msl(0, &basic_type, is_packed, row_major); + } + + auto &type = type_id ? get(type_id) : *special_type; + uint32_t dimensions = uint32_t(type.array.size()); + assert(dimensions > 0); + dimensions--; + + // Multiply together every dimension, except the last one. + for (uint32_t dim = 0; dim < dimensions; dim++) + { + uint32_t array_size = to_array_size_literal(type, dim); + value_size *= max(array_size, 1u); + } + + return value_size; +} + +uint32_t CompilerMSL::get_declared_struct_member_array_stride_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_array_stride_msl(get_physical_member_type_id(type, index), nullptr, + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_array_stride_msl(const SPIRType &type, uint32_t index) const +{ + auto presumed_type = get_presumed_input_type(type, index); + return get_declared_type_array_stride_msl(0, &presumed_type, false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_type_matrix_stride_msl(TypeID type_id, const SPIRType *special_type, + bool packed, bool row_major) const +{ + auto &type = type_id ? get(type_id) : *special_type; + + // For packed matrices, we just use the size of the vector type. + // Otherwise, MatrixStride == alignment, which is the size of the underlying vector type. + if (packed) + return (type.width / 8) * ((row_major && type.columns > 1) ? type.columns : type.vecsize); + else + return get_declared_type_alignment_msl(type_id, special_type, false, row_major); +} + +uint32_t CompilerMSL::get_declared_struct_member_matrix_stride_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_matrix_stride_msl(get_physical_member_type_id(type, index), nullptr, + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_matrix_stride_msl(const SPIRType &type, uint32_t index) const +{ + auto presumed_type = get_presumed_input_type(type, index); + return get_declared_type_matrix_stride_msl(0, &presumed_type, false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_struct_size_msl(const SPIRType &struct_type) const +{ + if (struct_type.member_types.empty()) + return 0; + + uint32_t mbr_cnt = uint32_t(struct_type.member_types.size()); + + // In MSL, a struct's alignment is equal to the maximum alignment of any of its members. + uint32_t alignment = 1; + + for (uint32_t i = 0; i < mbr_cnt; i++) + { + uint32_t mbr_alignment = get_declared_struct_member_alignment_msl(struct_type, i); + alignment = max(alignment, mbr_alignment); + } + + // Last member will always be matched to the final Offset decoration, but size of struct in MSL now depends + // on physical size in MSL, and the size of the struct itself is then aligned to struct alignment. + uint32_t spirv_offset = type_struct_member_offset(struct_type, mbr_cnt - 1); + uint32_t msl_size = spirv_offset + get_declared_struct_member_size_msl(struct_type, mbr_cnt - 1); + msl_size = (msl_size + alignment - 1) & ~(alignment - 1); + return msl_size; +} + +uint32_t CompilerMSL::get_physical_type_id_stride(TypeID type_id) const +{ + // This should only be relevant for plain types such as scalars and vectors? + // If we're pointing to a struct, it will recursively pick up packed/row-major state. + return get_declared_type_size_msl(type_id, nullptr, false, false); +} + +// Returns the byte size of a struct member. +uint32_t CompilerMSL::get_declared_type_size_msl(TypeID type_id, const SPIRType *special_type, + bool is_packed, bool row_major) const +{ + auto &type = type_id ? get(type_id) : *special_type; + + // Pointers take 8 bytes each + // Match both pointer and array-of-pointer here. + if (type.pointer && type.storage == StorageClassPhysicalStorageBuffer) + { + uint32_t type_size = 8; + + // Work our way through potentially layered arrays, + // stopping when we hit a pointer that is not also an array. + int32_t dim_idx = (int32_t)type.array.size() - 1; + auto *p_type = &type; + while (!is_pointer(*p_type) && dim_idx >= 0) + { + type_size *= to_array_size_literal(*p_type, dim_idx); + p_type = &get(p_type->parent_type); + dim_idx--; + } + + return type_size; + } + + switch (type.basetype) + { + case SPIRType::Unknown: + case SPIRType::Void: + case SPIRType::AtomicCounter: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + SPIRV_CROSS_THROW("Querying size of opaque object."); + + default: + { + if ((!type.parent_type || special_type) && !type.array.empty()) + { + // Special case where the type hierarchy is not set up properly. + // Don't want to have to allocate a bunch of dummy type IDs just to make it work. + uint32_t array_size = to_array_size_literal(type); + return get_declared_type_array_stride_msl(type_id, special_type, is_packed, row_major) * max(array_size, 1u); + } + else if (is_array(type) && type.parent_type) + { + // For the proper case. Ideally all code paths should go through here, but + // would need a lot of cleanup to make that work ... + auto &parent_type = get(type.parent_type); + uint32_t effective_stride; + + if (parent_type.op == OpTypeStruct && has_decoration(parent_type.self, DecorationArrayStride)) + effective_stride = get_decoration(type_id, DecorationArrayStride); + else + effective_stride = get_declared_type_array_stride_msl(type_id, special_type, is_packed, row_major); + + uint32_t array_size = to_array_size_literal(type); + return effective_stride * max(array_size, 1u); + } + + if (type.basetype == SPIRType::Struct) + return get_declared_struct_size_msl(type); + + if (is_packed) + { + return type.vecsize * type.columns * (type.width / 8); + } + else + { + // An unpacked 3-element vector or matrix column is the same memory size as a 4-element. + uint32_t vecsize = type.vecsize; + uint32_t columns = type.columns; + + if (row_major && columns > 1) + swap(vecsize, columns); + + if (vecsize == 3) + vecsize = 4; + + return vecsize * columns * (type.width / 8); + } + } + } +} + +uint32_t CompilerMSL::get_declared_struct_member_size_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_size_msl(get_physical_member_type_id(type, index), nullptr, + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_size_msl(const SPIRType &type, uint32_t index) const +{ + auto presumed_type = get_presumed_input_type(type, index); + return get_declared_type_size_msl(0, &presumed_type, false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +// Returns the byte alignment of a type. +uint32_t CompilerMSL::get_declared_type_alignment_msl(TypeID type_id, const SPIRType *special_type, + bool is_packed, bool row_major) const +{ + auto &type = type_id ? get(type_id) : *special_type; + + // Pointers align on multiples of 8 bytes. + // Deliberately ignore array-ness here. It's not relevant for alignment. + if (type.pointer && type.storage == StorageClassPhysicalStorageBuffer) + return 8; + + switch (type.basetype) + { + case SPIRType::Unknown: + case SPIRType::Void: + case SPIRType::AtomicCounter: + case SPIRType::Image: + case SPIRType::SampledImage: + case SPIRType::Sampler: + SPIRV_CROSS_THROW("Querying alignment of opaque object."); + + case SPIRType::Double: + SPIRV_CROSS_THROW("double types are not supported in buffers in MSL."); + + case SPIRType::Struct: + { + // In MSL, a struct's alignment is equal to the maximum alignment of any of its members. + uint32_t alignment = 1; + for (uint32_t i = 0; i < type.member_types.size(); i++) + alignment = max(alignment, uint32_t(get_declared_struct_member_alignment_msl(type, i))); + return alignment; + } + + default: + { + if (type.basetype == SPIRType::Int64 && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("long types in buffers are only supported in MSL 2.3 and above."); + if (type.basetype == SPIRType::UInt64 && !msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("ulong types in buffers are only supported in MSL 2.3 and above."); + // Alignment of packed type is the same as the underlying component or column size. + // Alignment of unpacked type is the same as the vector size. + // Alignment of 3-elements vector is the same as 4-elements (including packed using column). + if (is_packed) + { + // If we have packed_T and friends, the alignment is always scalar. + return type.width / 8; + } + else + { + // This is the general rule for MSL. Size == alignment. + uint32_t vecsize = (row_major && type.columns > 1) ? type.columns : type.vecsize; + return (type.width / 8) * (vecsize == 3 ? 4 : vecsize); + } + } + } +} + +uint32_t CompilerMSL::get_declared_struct_member_alignment_msl(const SPIRType &type, uint32_t index) const +{ + return get_declared_type_alignment_msl(get_physical_member_type_id(type, index), nullptr, + member_is_packed_physical_type(type, index), + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +uint32_t CompilerMSL::get_declared_input_alignment_msl(const SPIRType &type, uint32_t index) const +{ + auto presumed_type = get_presumed_input_type(type, index); + return get_declared_type_alignment_msl(0, &presumed_type, false, + has_member_decoration(type.self, index, DecorationRowMajor)); +} + +bool CompilerMSL::skip_argument(uint32_t) const +{ + return false; +} + +void CompilerMSL::analyze_sampled_image_usage() +{ + if (msl_options.swizzle_texture_samples) + { + SampledImageScanner scanner(*this); + traverse_all_reachable_opcodes(get(ir.default_entry_point), scanner); + } +} + +void CompilerMSL::analyze_workgroup_variables() +{ + ir.for_each_typed_id([&](uint32_t, SPIRVariable &var) { + // If workgroup variables have initializer, it can only be ConstantNull (zero init) + if (var.storage == StorageClassWorkgroup && var.initializer) + { + needs_workgroup_zero_init = true; + + // MSL compiler does not like the routine to initialize simple threadgroup variables, + // falsely claiming it is "sometimes uninitialized". Suppress it. + auto &type = get_variable_data_type(var); + if (type.array.empty() && type.member_types.empty()) + suppress_sometimes_unitialized = true; + } + }); +} + +bool CompilerMSL::SampledImageScanner::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + switch (opcode) + { + case OpLoad: + case OpImage: + case OpSampledImage: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + auto &type = get(result_type); + if ((type.basetype != SPIRType::Image && type.basetype != SPIRType::SampledImage) || type.image.sampled != 1) + return true; + + uint32_t id = args[1]; + set(id, "", result_type, true); + break; + } + case OpImageSampleExplicitLod: + case OpImageSampleProjExplicitLod: + case OpImageSampleDrefExplicitLod: + case OpImageSampleProjDrefExplicitLod: + case OpImageSampleImplicitLod: + case OpImageSampleProjImplicitLod: + case OpImageSampleDrefImplicitLod: + case OpImageSampleProjDrefImplicitLod: + case OpImageFetch: + case OpImageGather: + case OpImageDrefGather: + self.has_sampled_images = + self.has_sampled_images || self.is_sampled_image_type(self.expression_type(args[2])); + self.needs_swizzle_buffer_def = self.needs_swizzle_buffer_def || self.has_sampled_images; + break; + default: + break; + } + return true; +} + +// If a needed custom function wasn't added before, add it and force a recompile. +void CompilerMSL::add_spv_func_and_recompile(SPVFuncImpl spv_func) +{ + if (spv_function_implementations.count(spv_func) == 0) + { + spv_function_implementations.insert(spv_func); + suppress_missing_prototypes = true; + force_recompile(); + } +} + +bool CompilerMSL::OpCodePreprocessor::handle(Op opcode, const uint32_t *args, uint32_t length) +{ + // Since MSL exists in a single execution scope, function prototype declarations are not + // needed, and clutter the output. If secondary functions are output (either as a SPIR-V + // function implementation or as indicated by the presence of OpFunctionCall), then set + // suppress_missing_prototypes to suppress compiler warnings of missing function prototypes. + + // Mark if the input requires the implementation of an SPIR-V function that does not exist in Metal. + SPVFuncImpl spv_func = get_spv_func_impl(opcode, args, length); + if (spv_func != SPVFuncImplNone) + { + self.spv_function_implementations.insert(spv_func); + suppress_missing_prototypes = true; + } + + switch (opcode) + { + + case OpFunctionCall: + suppress_missing_prototypes = true; + break; + + case OpDemoteToHelperInvocationEXT: + uses_discard = true; + break; + + // Emulate texture2D atomic operations + case OpImageTexelPointer: + { + if (!self.msl_options.supports_msl_version(3, 1)) + { + auto *var = self.maybe_get_backing_variable(args[2]); + image_pointers_emulated[args[1]] = var ? var->self : ID(0); + } + break; + } + + case OpImageWrite: + uses_image_write = true; + break; + + case OpStore: + check_resource_write(args[0]); + break; + + // Emulate texture2D atomic operations + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicFAddEXT: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + { + uses_atomics = true; + auto it = image_pointers_emulated.find(args[2]); + if (it != image_pointers_emulated.end()) + { + uses_image_write = true; + self.atomic_image_vars_emulated.insert(it->second); + } + else + check_resource_write(args[2]); + break; + } + + case OpAtomicStore: + { + uses_atomics = true; + auto it = image_pointers_emulated.find(args[0]); + if (it != image_pointers_emulated.end()) + { + self.atomic_image_vars_emulated.insert(it->second); + uses_image_write = true; + } + else + check_resource_write(args[0]); + break; + } + + case OpAtomicLoad: + { + uses_atomics = true; + auto it = image_pointers_emulated.find(args[2]); + if (it != image_pointers_emulated.end()) + { + self.atomic_image_vars_emulated.insert(it->second); + } + break; + } + + case OpGroupNonUniformInverseBallot: + needs_subgroup_invocation_id = true; + break; + + case OpGroupNonUniformBallotFindLSB: + case OpGroupNonUniformBallotFindMSB: + needs_subgroup_size = true; + break; + + case OpGroupNonUniformBallotBitCount: + if (args[3] == GroupOperationReduce) + needs_subgroup_size = true; + else + needs_subgroup_invocation_id = true; + break; + + case OpGroupNonUniformRotateKHR: + // Add the correct invocation ID for calculating clustered rotate case. + if (length > 5) + { + if (static_cast(self.evaluate_constant_u32(args[2])) == ScopeSubgroup) + needs_subgroup_invocation_id = true; + else + needs_local_invocation_index = true; + } + break; + + case OpGroupNonUniformFAdd: + case OpGroupNonUniformFMul: + case OpGroupNonUniformFMin: + case OpGroupNonUniformFMax: + case OpGroupNonUniformIAdd: + case OpGroupNonUniformIMul: + case OpGroupNonUniformSMin: + case OpGroupNonUniformSMax: + case OpGroupNonUniformUMin: + case OpGroupNonUniformUMax: + case OpGroupNonUniformBitwiseAnd: + case OpGroupNonUniformBitwiseOr: + case OpGroupNonUniformBitwiseXor: + case OpGroupNonUniformLogicalAnd: + case OpGroupNonUniformLogicalOr: + case OpGroupNonUniformLogicalXor: + if ((compiler.get_execution_model() != ExecutionModelFragment || + self.msl_options.supports_msl_version(2, 2)) && + args[3] == GroupOperationClusteredReduce) + needs_subgroup_invocation_id = true; + break; + + case OpArrayLength: + { + auto *var = self.maybe_get_backing_variable(args[2]); + if (var != nullptr) + { + if (!self.is_var_runtime_size_array(*var)) + self.buffers_requiring_array_length.insert(var->self); + } + break; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + case OpPtrAccessChain: + { + // OpArrayLength might want to know if taking ArrayLength of an array of SSBOs. + uint32_t result_type = args[0]; + uint32_t id = args[1]; + uint32_t ptr = args[2]; + + set(id, "", result_type, true); + self.register_read(id, ptr, true); + self.ir.ids[id].set_allow_type_rewrite(); + break; + } + + case OpBitcast: + case OpConvertPtrToU: + case OpConvertUToPtr: + { + if (length < 3) + break; + + auto &result_type = self.get(args[0]); + auto *arg_type = get_expression_result_type(args[2]); + if (!arg_type) + arg_type = &self.expression_type(args[2]); + + if (opcode != OpBitcast || self.is_pointer(result_type) || (arg_type && self.is_pointer(*arg_type))) + { + uint32_t id = args[1]; + set(id, "", args[0], true); + self.register_read(id, args[2], true); + self.ir.ids[id].set_allow_type_rewrite(); + } + break; + } + + case OpExtInst: + { + uint32_t extension_set = args[2]; + SPIRExtension::Extension ext = get(extension_set).ext; + if (ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(args[3]); + switch (op_450) + { + case GLSLstd450InterpolateAtCentroid: + case GLSLstd450InterpolateAtSample: + case GLSLstd450InterpolateAtOffset: + { + if (!self.msl_options.supports_msl_version(2, 3)) + SPIRV_CROSS_THROW("Pull-model interpolation requires MSL 2.3."); + // Fragment varyings used with pull-model interpolation need special handling, + // due to the way pull-model interpolation works in Metal. + auto *var = self.maybe_get_backing_variable(args[4]); + if (var) + { + self.pull_model_inputs.insert(var->self); + auto &var_type = self.get_variable_element_type(*var); + // In addition, if this variable has a 'Sample' decoration, we need the sample ID + // in order to do default interpolation. + if (compiler.has_decoration(var->self, DecorationSample)) + { + needs_sample_id = true; + } + else if (var_type.basetype == SPIRType::Struct) + { + // Now we need to check each member and see if it has this decoration. + for (uint32_t i = 0; i < var_type.member_types.size(); ++i) + { + if (compiler.has_member_decoration(var_type.self, i, DecorationSample)) + { + needs_sample_id = true; + break; + } + } + } + } + break; + } + default: + break; + } + } + else if (ext == SPIRExtension::NonSemanticDebugPrintf) + { + // Operation 1 is printf. + if (args[3] == 1 && !self.msl_options.supports_msl_version(3, 2)) + SPIRV_CROSS_THROW("Debug printf requires MSL 3.2."); + } + break; + } + + case OpIsHelperInvocationEXT: + if (self.needs_manual_helper_invocation_updates()) + needs_helper_invocation = true; + break; + + case OpCooperativeMatrixLoadKHR: + case OpCooperativeMatrixMulAddKHR: + case OpCooperativeMatrixLengthKHR: + uses_cooperative_matrix = true; + break; + + case OpCooperativeMatrixStoreKHR: + uses_cooperative_matrix = true; + check_resource_write(args[0]); + break; + + default: + break; + } + + return true; +} + +// If the variable is a Uniform or StorageBuffer, mark that a resource has been written to. +void CompilerMSL::OpCodePreprocessor::check_resource_write(uint32_t var_id) +{ + auto *type = get_expression_result_type(var_id); + auto sc = StorageClassMax; + + if (type) + { + sc = type->storage; + } + else + { + auto *var = self.maybe_get_backing_variable(var_id); + if (var) + sc = var->storage; + } + + if (sc == StorageClassUniform || sc == StorageClassStorageBuffer || sc == StorageClassPhysicalStorageBuffer) + uses_buffer_write = true; +} + +// Returns an enumeration of a SPIR-V function that needs to be output for certain Op codes. +CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op opcode, const uint32_t *args, uint32_t length) +{ + switch (opcode) + { + case OpSMod: + return SPVFuncImplSMod; + + case OpFMod: + return SPVFuncImplMod; + + case OpFAdd: + case OpFSub: + if (self.msl_options.invariant_float_math || self.has_legacy_nocontract(args[0], args[1])) + return opcode == OpFAdd ? SPVFuncImplFAdd : SPVFuncImplFSub; + break; + + case OpFMul: + case OpOuterProduct: + case OpMatrixTimesVector: + case OpVectorTimesMatrix: + case OpMatrixTimesMatrix: + if (self.msl_options.invariant_float_math || self.has_legacy_nocontract(args[0], args[1])) + return SPVFuncImplFMul; + break; + + case OpQuantizeToF16: + return SPVFuncImplQuantizeToF16; + + case OpTypeArray: + { + // Allow Metal to use the array template to make arrays a value type + return SPVFuncImplUnsafeArray; + } + + // Emulate texture2D atomic operations + case OpAtomicExchange: + case OpAtomicCompareExchange: + case OpAtomicCompareExchangeWeak: + case OpAtomicIIncrement: + case OpAtomicIDecrement: + case OpAtomicIAdd: + case OpAtomicFAddEXT: + case OpAtomicISub: + case OpAtomicSMin: + case OpAtomicUMin: + case OpAtomicSMax: + case OpAtomicUMax: + case OpAtomicAnd: + case OpAtomicOr: + case OpAtomicXor: + case OpAtomicLoad: + case OpAtomicStore: + { + auto it = image_pointers_emulated.find(args[opcode == OpAtomicStore ? 0 : 2]); + if (it != image_pointers_emulated.end()) + { + uint32_t tid = get(it->second).basetype; + if (tid && get(tid).image.dim == Dim2D) + return SPVFuncImplImage2DAtomicCoords; + } + break; + } + + case OpImageFetch: + case OpImageRead: + case OpImageWrite: + { + // Retrieve the image type, and if it's a Buffer, emit a texel coordinate function + uint32_t tid = result_types[args[opcode == OpImageWrite ? 0 : 2]]; + if (tid && get(tid).image.dim == DimBuffer && !self.msl_options.texture_buffer_native) + return SPVFuncImplTexelBufferCoords; + break; + } + + case OpExtInst: + { + uint32_t extension_set = args[2]; + if (get(extension_set).ext == SPIRExtension::GLSL) + { + auto op_450 = static_cast(args[3]); + switch (op_450) + { + case GLSLstd450Radians: + return SPVFuncImplRadians; + case GLSLstd450Degrees: + return SPVFuncImplDegrees; + case GLSLstd450FindILsb: + return SPVFuncImplFindILsb; + case GLSLstd450FindSMsb: + return SPVFuncImplFindSMsb; + case GLSLstd450FindUMsb: + return SPVFuncImplFindUMsb; + case GLSLstd450SSign: + return SPVFuncImplSSign; + case GLSLstd450Reflect: + { + auto &type = get(args[0]); + if (type.vecsize == 1) + return SPVFuncImplReflectScalar; + break; + } + case GLSLstd450Refract: + { + auto &type = get(args[0]); + if (type.vecsize == 1) + return SPVFuncImplRefractScalar; + break; + } + case GLSLstd450FaceForward: + { + auto &type = get(args[0]); + if (type.vecsize == 1) + return SPVFuncImplFaceForwardScalar; + break; + } + case GLSLstd450MatrixInverse: + { + auto &mat_type = get(args[0]); + switch (mat_type.columns) + { + case 2: + return SPVFuncImplInverse2x2; + case 3: + return SPVFuncImplInverse3x3; + case 4: + return SPVFuncImplInverse4x4; + default: + break; + } + break; + } + default: + break; + } + } + break; + } + + case OpGroupNonUniformBroadcast: + case OpSubgroupReadInvocationKHR: + return SPVFuncImplSubgroupBroadcast; + + case OpGroupNonUniformBroadcastFirst: + case OpSubgroupFirstInvocationKHR: + return SPVFuncImplSubgroupBroadcastFirst; + + case OpGroupNonUniformBallot: + case OpSubgroupBallotKHR: + return SPVFuncImplSubgroupBallot; + + case OpGroupNonUniformInverseBallot: + case OpGroupNonUniformBallotBitExtract: + return SPVFuncImplSubgroupBallotBitExtract; + + case OpGroupNonUniformBallotFindLSB: + return SPVFuncImplSubgroupBallotFindLSB; + + case OpGroupNonUniformBallotFindMSB: + return SPVFuncImplSubgroupBallotFindMSB; + + case OpGroupNonUniformBallotBitCount: + return SPVFuncImplSubgroupBallotBitCount; + + case OpGroupNonUniformAllEqual: + case OpSubgroupAllEqualKHR: + return SPVFuncImplSubgroupAllEqual; + + case OpGroupNonUniformShuffle: + return SPVFuncImplSubgroupShuffle; + + case OpGroupNonUniformShuffleXor: + return SPVFuncImplSubgroupShuffleXor; + + case OpGroupNonUniformShuffleUp: + return SPVFuncImplSubgroupShuffleUp; + + case OpGroupNonUniformShuffleDown: + return SPVFuncImplSubgroupShuffleDown; + + case OpGroupNonUniformRotateKHR: + // Clustered rotate is performed using shuffle. + if (length > 5) + return SPVFuncImplSubgroupShuffle; + return SPVFuncImplSubgroupRotate; + + case OpGroupNonUniformQuadBroadcast: + return SPVFuncImplQuadBroadcast; + + case OpGroupNonUniformQuadSwap: + return SPVFuncImplQuadSwap; + + case OpSDot: + case OpUDot: + case OpSUDot: + case OpSDotAccSat: + case OpUDotAccSat: + case OpSUDotAccSat: + return SPVFuncImplReduceAdd; + + case OpSMulExtended: + case OpUMulExtended: + return SPVFuncImplMulExtended; + + case OpAssumeTrueKHR: + case OpExpectKHR: + return SPVFuncImplAssume; + + default: + break; + } + return SPVFuncImplNone; +} + +// Sort both type and meta member content based on builtin status (put builtins at end), +// then by the required sorting aspect. +void CompilerMSL::MemberSorter::sort() +{ + // Create a temporary array of consecutive member indices and sort it based on how + // the members should be reordered, based on builtin and sorting aspect meta info. + size_t mbr_cnt = type.member_types.size(); + SmallVector mbr_idxs(mbr_cnt); + std::iota(mbr_idxs.begin(), mbr_idxs.end(), 0); // Fill with consecutive indices + std::stable_sort(mbr_idxs.begin(), mbr_idxs.end(), *this); // Sort member indices based on sorting aspect + + bool sort_is_identity = true; + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + if (mbr_idx != mbr_idxs[mbr_idx]) + { + sort_is_identity = false; + break; + } + } + + if (sort_is_identity) + return; + + if (meta.members.size() < type.member_types.size()) + { + // This should never trigger in normal circumstances, but to be safe. + meta.members.resize(type.member_types.size()); + } + + // Move type and meta member info to the order defined by the sorted member indices. + // This is done by creating temporary copies of both member types and meta, and then + // copying back to the original content at the sorted indices. + auto mbr_types_cpy = type.member_types; + auto mbr_meta_cpy = meta.members; + for (uint32_t mbr_idx = 0; mbr_idx < mbr_cnt; mbr_idx++) + { + type.member_types[mbr_idx] = mbr_types_cpy[mbr_idxs[mbr_idx]]; + meta.members[mbr_idx] = mbr_meta_cpy[mbr_idxs[mbr_idx]]; + } + + // If we're sorting by Offset, this might affect user code which accesses a buffer block. + // We will need to redirect member indices from defined index to sorted index using reverse lookup. + if (sort_aspect == SortAspect::Offset) + { + type.member_type_index_redirection.resize(mbr_cnt); + for (uint32_t map_idx = 0; map_idx < mbr_cnt; map_idx++) + type.member_type_index_redirection[mbr_idxs[map_idx]] = map_idx; + } +} + +bool CompilerMSL::MemberSorter::operator()(uint32_t mbr_idx1, uint32_t mbr_idx2) +{ + auto &mbr_meta1 = meta.members[mbr_idx1]; + auto &mbr_meta2 = meta.members[mbr_idx2]; + + if (sort_aspect == LocationThenBuiltInType) + { + // Sort first by builtin status (put builtins at end), then by the sorting aspect. + if (mbr_meta1.builtin != mbr_meta2.builtin) + return mbr_meta2.builtin; + else if (mbr_meta1.builtin) + return mbr_meta1.builtin_type < mbr_meta2.builtin_type; + else if (mbr_meta1.location == mbr_meta2.location) + return mbr_meta1.component < mbr_meta2.component; + else + return mbr_meta1.location < mbr_meta2.location; + } + else + return mbr_meta1.offset < mbr_meta2.offset; +} + +CompilerMSL::MemberSorter::MemberSorter(SPIRType &t, Meta &m, SortAspect sa) + : type(t) + , meta(m) + , sort_aspect(sa) +{ + // Ensure enough meta info is available + meta.members.resize(max(type.member_types.size(), meta.members.size())); +} + +void CompilerMSL::remap_constexpr_sampler(VariableID id, const MSLConstexprSampler &sampler) +{ + auto &type = get(get(id).basetype); + if (type.basetype != SPIRType::SampledImage && type.basetype != SPIRType::Sampler) + SPIRV_CROSS_THROW("Can only remap SampledImage and Sampler type."); + if (!type.array.empty()) + SPIRV_CROSS_THROW("Can not remap array of samplers."); + constexpr_samplers_by_id[id] = sampler; +} + +void CompilerMSL::remap_constexpr_sampler_by_binding(uint32_t desc_set, uint32_t binding, + const MSLConstexprSampler &sampler) +{ + constexpr_samplers_by_binding[{ desc_set, binding }] = sampler; +} + +void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) +{ + bool is_packed = has_extended_decoration(source_id, SPIRVCrossDecorationPhysicalTypePacked); + auto *source_expr = maybe_get(source_id); + auto *var = maybe_get_backing_variable(source_id); + const SPIRType *var_type = nullptr, *phys_type = nullptr; + + if (uint32_t phys_id = get_extended_decoration(source_id, SPIRVCrossDecorationPhysicalTypeID)) + phys_type = &get(phys_id); + else + phys_type = &expr_type; + + if (var) + { + source_id = var->self; + var_type = &get_variable_data_type(*var); + } + + bool rewrite_boolean_load = + expr_type.basetype == SPIRType::Boolean && + (var && (var->storage == StorageClassWorkgroup || var_type->basetype == SPIRType::Struct)); + + // Type fixups for workgroup variables if they are booleans. + if (rewrite_boolean_load) + { + if (is_array(expr_type)) + expr = to_rerolled_array_expression(expr_type, expr, expr_type); + else + expr = join(type_to_glsl(expr_type), "(", expr, ")"); + } + + // Type fixups for workgroup variables if they are matrices. + // Don't do fixup for packed types; those are handled specially. + // FIXME: Maybe use a type like spvStorageMatrix for packed matrices? + if (!msl_options.supports_msl_version(3, 0) && var && + (var->storage == StorageClassWorkgroup || + (var_type->basetype == SPIRType::Struct && + has_extended_decoration(var_type->self, SPIRVCrossDecorationWorkgroupStruct) && !is_packed)) && + expr_type.columns > 1) + { + SPIRType matrix_type = *phys_type; + if (source_expr && source_expr->need_transpose) + swap(matrix_type.vecsize, matrix_type.columns); + matrix_type.array.clear(); + matrix_type.array_size_literal.clear(); + expr = join(type_to_glsl(matrix_type), "(", expr, ")"); + } + + // Only interested in standalone builtin variables in the switch below. + if (!has_decoration(source_id, DecorationBuiltIn)) + { + // If the backing variable does not match our expected sign, we can fix it up here. + // See ensure_correct_input_type(). + if (var && var->storage == StorageClassInput) + { + auto &base_type = get(var->basetype); + if (base_type.basetype != SPIRType::Struct && expr_type.basetype != base_type.basetype) + expr = join(type_to_glsl(expr_type), "(", expr, ")"); + } + return; + } + + auto builtin = static_cast(get_decoration(source_id, DecorationBuiltIn)); + auto expected_type = expr_type.basetype; + auto expected_width = expr_type.width; + switch (builtin) + { + case BuiltInGlobalInvocationId: + case BuiltInLocalInvocationId: + case BuiltInWorkgroupId: + case BuiltInLocalInvocationIndex: + case BuiltInWorkgroupSize: + case BuiltInNumWorkgroups: + case BuiltInLayer: + case BuiltInViewportIndex: + case BuiltInFragStencilRefEXT: + case BuiltInPrimitiveId: + case BuiltInSubgroupSize: + case BuiltInSubgroupLocalInvocationId: + case BuiltInViewIndex: + case BuiltInVertexIndex: + case BuiltInInstanceIndex: + case BuiltInBaseInstance: + case BuiltInBaseVertex: + case BuiltInSampleMask: + expected_type = SPIRType::UInt; + expected_width = 32; + break; + + case BuiltInTessLevelInner: + case BuiltInTessLevelOuter: + if (is_tesc_shader()) + { + expected_type = SPIRType::Half; + expected_width = 16; + } + break; + + default: + break; + } + + if (is_array(expr_type) && builtin == BuiltInSampleMask) + { + // Needs special handling. + auto wrap_expr = join(type_to_glsl(expr_type), "({ "); + wrap_expr += join(type_to_glsl(get(expr_type.parent_type)), "(", expr, ")"); + wrap_expr += " })"; + expr = std::move(wrap_expr); + } + else if (expected_type != expr_type.basetype) + { + if (is_array(expr_type) && (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter)) + { + // Triggers when loading TessLevel directly as an array. + // Need explicit padding + cast. + auto wrap_expr = join(type_to_glsl(expr_type), "({ "); + + uint32_t array_size = get_physical_tess_level_array_size(builtin); + for (uint32_t i = 0; i < array_size; i++) + { + if (array_size > 1) + wrap_expr += join("float(", expr, "[", i, "])"); + else + wrap_expr += join("float(", expr, ")"); + if (i + 1 < array_size) + wrap_expr += ", "; + } + + if (is_tessellating_triangles()) + wrap_expr += ", 0.0"; + + wrap_expr += " })"; + expr = std::move(wrap_expr); + } + else + { + // These are of different widths, so we cannot do a straight bitcast. + if (expected_width != expr_type.width) + expr = join(type_to_glsl(expr_type), "(", expr, ")"); + else + expr = bitcast_expression(expr_type, expected_type, expr); + } + } +} + +void CompilerMSL::cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) +{ + bool is_packed = has_extended_decoration(target_id, SPIRVCrossDecorationPhysicalTypePacked); + auto *target_expr = maybe_get(target_id); + auto *var = maybe_get_backing_variable(target_id); + const SPIRType *var_type = nullptr, *phys_type = nullptr; + + if (uint32_t phys_id = get_extended_decoration(target_id, SPIRVCrossDecorationPhysicalTypeID)) + phys_type = &get(phys_id); + else + phys_type = &expr_type; + + if (var) + { + target_id = var->self; + var_type = &get_variable_data_type(*var); + } + + bool rewrite_boolean_store = + expr_type.basetype == SPIRType::Boolean && + (var && (var->storage == StorageClassWorkgroup || var_type->basetype == SPIRType::Struct)); + + // Type fixups for workgroup variables or struct members if they are booleans. + if (rewrite_boolean_store) + { + if (is_array(expr_type)) + { + expr = to_rerolled_array_expression(*var_type, expr, expr_type); + } + else + { + auto short_type = expr_type; + short_type.basetype = SPIRType::Short; + expr = join(type_to_glsl(short_type), "(", expr, ")"); + } + } + + // Type fixups for workgroup variables if they are matrices. + // Don't do fixup for packed types; those are handled specially. + // FIXME: Maybe use a type like spvStorageMatrix for packed matrices? + if (!msl_options.supports_msl_version(3, 0) && var && + (var->storage == StorageClassWorkgroup || + (var_type->basetype == SPIRType::Struct && + has_extended_decoration(var_type->self, SPIRVCrossDecorationWorkgroupStruct) && !is_packed)) && + expr_type.columns > 1) + { + SPIRType matrix_type = *phys_type; + if (target_expr && target_expr->need_transpose) + swap(matrix_type.vecsize, matrix_type.columns); + expr = join("spvStorage_", type_to_glsl(matrix_type), "(", expr, ")"); + } + + // 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; + auto expected_width = expr_type.width; + switch (builtin) + { + case BuiltInLayer: + case BuiltInViewportIndex: + case BuiltInFragStencilRefEXT: + case BuiltInPrimitiveId: + case BuiltInViewIndex: + expected_type = SPIRType::UInt; + expected_width = 32; + break; + + case BuiltInTessLevelInner: + case BuiltInTessLevelOuter: + expected_type = SPIRType::Half; + expected_width = 16; + break; + + default: + break; + } + + if (expected_type != expr_type.basetype) + { + if (expected_width != expr_type.width) + { + // These are of different widths, so we cannot do a straight bitcast. + auto type = expr_type; + type.basetype = expected_type; + type.width = expected_width; + expr = join(type_to_glsl(type), "(", expr, ")"); + } + else + { + auto type = expr_type; + type.basetype = expected_type; + expr = bitcast_expression(type, expr_type.basetype, expr); + } + } +} + +string CompilerMSL::to_initializer_expression(const SPIRVariable &var) +{ + // We risk getting an array initializer here with MSL. If we have an array. + // FIXME: We cannot handle non-constant arrays being initialized. + // We will need to inject spvArrayCopy here somehow ... + auto &type = get(var.basetype); + string expr; + if (ir.ids[var.initializer].get_type() == TypeConstant && + (!type.array.empty() || type.basetype == SPIRType::Struct)) + expr = constant_expression(get(var.initializer)); + else + expr = CompilerGLSL::to_initializer_expression(var); + // If the initializer has more vector components than the variable, add a swizzle. + // FIXME: This can't handle arrays or structs. + auto &init_type = expression_type(var.initializer); + if (type.array.empty() && type.basetype != SPIRType::Struct && init_type.vecsize > type.vecsize) + expr = enclose_expression(expr + vector_swizzle(type.vecsize, 0)); + return expr; +} + +string CompilerMSL::to_zero_initialized_expression(uint32_t) +{ + return "{}"; +} + +bool CompilerMSL::descriptor_set_is_argument_buffer(uint32_t desc_set) const +{ + if (!msl_options.argument_buffers) + return false; + if (desc_set >= kMaxArgumentBuffers) + return false; + + return (argument_buffer_discrete_mask & (1u << desc_set)) == 0; +} + +bool CompilerMSL::is_supported_argument_buffer_type(const SPIRType &type) const +{ + // iOS Tier 1 argument buffers do not support writable images. + // When the argument buffer is encoded, we don't know whether this image will have a + // NonWritable decoration, so just use discrete arguments for all storage images on iOS. + bool is_supported_type = !(type.basetype == SPIRType::Image && + type.image.sampled == 2 && + msl_options.is_ios() && + msl_options.argument_buffers_tier <= Options::ArgumentBuffersTier::Tier1); + return is_supported_type && !type_is_msl_framebuffer_fetch(type); +} + +void CompilerMSL::emit_argument_buffer_aliased_descriptor(const SPIRVariable &aliased_var, + const SPIRVariable &base_var) +{ + // To deal with buffer <-> image aliasing, we need to perform an unholy UB ritual. + // A texture type in Metal 3.0 is a pointer. However, we cannot simply cast a pointer to texture. + // What we *can* do is to cast pointer-to-pointer to pointer-to-texture. + + // We need to explicitly reach into the descriptor buffer lvalue, not any spvDescriptorArray wrapper. + auto *var_meta = ir.find_meta(base_var.self); + bool old_explicit_qualifier = var_meta && var_meta->decoration.qualified_alias_explicit_override; + if (var_meta) + var_meta->decoration.qualified_alias_explicit_override = false; + auto unqualified_name = to_name(base_var.self, false); + if (var_meta) + var_meta->decoration.qualified_alias_explicit_override = old_explicit_qualifier; + + // For non-arrayed buffers, we have already performed a de-reference. + // We need a proper lvalue to cast, so strip away the de-reference. + if (unqualified_name.size() > 2 && unqualified_name[0] == '(' && unqualified_name[1] == '*') + { + unqualified_name.erase(unqualified_name.begin(), unqualified_name.begin() + 2); + unqualified_name.pop_back(); + } + + string name; + + auto &var_type = get(aliased_var.basetype); + auto &data_type = get_variable_data_type(aliased_var); + string descriptor_storage = descriptor_address_space(aliased_var.self, aliased_var.storage, ""); + + if (aliased_var.storage == StorageClassUniformConstant) + { + if (is_var_runtime_size_array(aliased_var)) + { + // This becomes a plain pointer to spvDescriptor. + name = join("reinterpret_cast<", descriptor_storage, " ", + type_to_glsl(get_variable_data_type(aliased_var), aliased_var.self, true), ">(&", + unqualified_name, ")"); + } + else + { + name = join("reinterpret_cast<", descriptor_storage, " ", + type_to_glsl(get_variable_data_type(aliased_var), aliased_var.self, true), " &>(", + unqualified_name, ");"); + } + } + else + { + // Buffer types. + bool old_is_using_builtin_array = is_using_builtin_array; + is_using_builtin_array = true; + + bool needs_post_cast_deref = !is_array(data_type); + string ref_type = needs_post_cast_deref ? "&" : join("(&)", type_to_array_glsl(var_type, aliased_var.self)); + + if (is_var_runtime_size_array(aliased_var)) + { + name = join("reinterpret_cast<", + type_to_glsl(var_type, aliased_var.self, true), " ", descriptor_storage, " *>(&", + unqualified_name, ")"); + } + else + { + name = join(needs_post_cast_deref ? "*" : "", "reinterpret_cast<", + type_to_glsl(var_type, aliased_var.self, true), " ", descriptor_storage, " ", + ref_type, + ">(", unqualified_name, ");"); + } + + if (needs_post_cast_deref) + descriptor_storage = get_type_address_space(var_type, aliased_var.self, false); + + // These kinds of ridiculous casts trigger warnings in compiler. Just ignore them. + if (!suppress_incompatible_pointer_types_discard_qualifiers) + { + suppress_incompatible_pointer_types_discard_qualifiers = true; + force_recompile_guarantee_forward_progress(); + } + + is_using_builtin_array = old_is_using_builtin_array; + } + + if (!is_var_runtime_size_array(aliased_var)) + { + // Lower to temporary, so drop the qualification. + set_qualified_name(aliased_var.self, ""); + statement(descriptor_storage, " auto &", to_name(aliased_var.self), " = ", name); + } + else + { + // This alias may have already been used to emit an entry point declaration. If there is a mismatch, we need a recompile. + // Moving this code to be run earlier will also conflict, + // because we need the qualified alias for the base resource, + // so forcing recompile until things sync up is the least invasive method for now. + if (ir.meta[aliased_var.self].decoration.qualified_alias != name) + force_recompile(); + + // This will get wrapped in a separate temporary when a spvDescriptorArray wrapper is emitted. + set_qualified_name(aliased_var.self, name); + } +} + +void CompilerMSL::analyze_argument_buffers() +{ + // Gather all used resources and sort them out into argument buffers. + // Each argument buffer corresponds to a descriptor set in SPIR-V. + // The [[id(N)]] values used correspond to the resource mapping we have for MSL. + // Otherwise, the binding number is used, but this is generally not safe some types like + // combined image samplers and arrays of resources. Metal needs different indices here, + // while SPIR-V can have one descriptor set binding. To use argument buffers in practice, + // you will need to use the remapping from the API. + for (auto &id : argument_buffer_ids) + id = 0; + + // Output resources, sorted by resource index & type. + struct Resource + { + SPIRVariable *var; + string name; + SPIRType::BaseType basetype; + uint32_t index; + uint32_t plane_count; + uint32_t plane; + uint32_t overlapping_var_id; + }; + SmallVector resources_in_set[kMaxArgumentBuffers]; + SmallVector inline_block_vars; + + bool set_needs_swizzle_buffer[kMaxArgumentBuffers] = {}; + bool set_needs_buffer_sizes[kMaxArgumentBuffers] = {}; + bool needs_buffer_sizes = false; + + ir.for_each_typed_id([&](uint32_t self, SPIRVariable &var) { + if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant || + var.storage == StorageClassStorageBuffer) && + !is_hidden_variable(var)) + { + uint32_t desc_set = get_decoration(self, DecorationDescriptorSet); + // Ignore if it's part of a push descriptor set. + if (!descriptor_set_is_argument_buffer(desc_set)) + return; + + uint32_t var_id = var.self; + auto &type = get_variable_data_type(var); + + if (desc_set >= kMaxArgumentBuffers) + SPIRV_CROSS_THROW("Descriptor set index is out of range."); + + const MSLConstexprSampler *constexpr_sampler = nullptr; + if (type.basetype == SPIRType::SampledImage || type.basetype == SPIRType::Sampler) + { + constexpr_sampler = find_constexpr_sampler(var_id); + if (constexpr_sampler) + { + // Mark this ID as a constexpr sampler for later in case it came from set/bindings. + constexpr_samplers_by_id[var_id] = *constexpr_sampler; + } + } + + uint32_t binding = get_decoration(var_id, DecorationBinding); + if (type.basetype == SPIRType::SampledImage) + { + add_resource_name(var_id); + + uint32_t plane_count = 1; + if (constexpr_sampler && constexpr_sampler->ycbcr_conversion_enable) + plane_count = constexpr_sampler->planes; + + for (uint32_t i = 0; i < plane_count; i++) + { + uint32_t image_resource_index = get_metal_resource_index(var, SPIRType::Image, i); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::Image, image_resource_index, plane_count, i, 0 }); + } + + if (type.image.dim != DimBuffer && !constexpr_sampler) + { + uint32_t sampler_resource_index = get_metal_resource_index(var, SPIRType::Sampler); + resources_in_set[desc_set].push_back( + { &var, to_sampler_expression(var_id), SPIRType::Sampler, sampler_resource_index, 1, 0, 0 }); + } + } + else if (inline_uniform_blocks.count(SetBindingPair{ desc_set, binding })) + { + inline_block_vars.push_back(var_id); + } + else if (!constexpr_sampler && is_supported_argument_buffer_type(type)) + { + // constexpr samplers are not declared as resources. + // Inline uniform blocks are always emitted at the end. + add_resource_name(var_id); + + uint32_t resource_index = get_metal_resource_index(var, type.basetype); + + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), type.basetype, resource_index, 1, 0, 0 }); + + // Emulate texture2D atomic operations + if (atomic_image_vars_emulated.count(var.self)) + { + uint32_t buffer_resource_index = get_metal_resource_index(var, SPIRType::AtomicCounter, 0); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id) + "_atomic", SPIRType::Struct, buffer_resource_index, 1, 0, 0 }); + } + } + + // Check if this descriptor set needs a swizzle buffer. + if (needs_swizzle_buffer_def && is_sampled_image_type(type)) + set_needs_swizzle_buffer[desc_set] = true; + else if (buffer_requires_array_length(var_id)) + { + set_needs_buffer_sizes[desc_set] = true; + needs_buffer_sizes = true; + } + } + }); + + if (needs_swizzle_buffer_def || needs_buffer_sizes) + { + uint32_t uint_ptr_type_id = 0; + + // We might have to add a swizzle buffer resource to the set. + for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++) + { + if (!set_needs_swizzle_buffer[desc_set] && !set_needs_buffer_sizes[desc_set]) + continue; + + if (uint_ptr_type_id == 0) + { + uint_ptr_type_id = ir.increase_bound_by(1); + + // Create a buffer to hold extra data, including the swizzle constants. + SPIRType uint_type_pointer = get_uint_type(); + uint_type_pointer.op = OpTypePointer; + uint_type_pointer.pointer = true; + uint_type_pointer.pointer_depth++; + uint_type_pointer.parent_type = get_uint_type_id(); + uint_type_pointer.storage = StorageClassUniform; + set(uint_ptr_type_id, uint_type_pointer); + set_decoration(uint_ptr_type_id, DecorationArrayStride, 4); + } + + if (set_needs_swizzle_buffer[desc_set]) + { + uint32_t var_id = ir.increase_bound_by(1); + auto &var = set(var_id, uint_ptr_type_id, StorageClassUniformConstant); + set_name(var_id, "spvSwizzleConstants"); + set_decoration(var_id, DecorationDescriptorSet, desc_set); + set_decoration(var_id, DecorationBinding, kSwizzleBufferBinding); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::UInt, get_metal_resource_index(var, SPIRType::UInt), 1, 0, 0 }); + } + + if (set_needs_buffer_sizes[desc_set]) + { + uint32_t var_id = ir.increase_bound_by(1); + auto &var = set(var_id, uint_ptr_type_id, StorageClassUniformConstant); + set_name(var_id, "spvBufferSizeConstants"); + set_decoration(var_id, DecorationDescriptorSet, desc_set); + set_decoration(var_id, DecorationBinding, kBufferSizeBufferBinding); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::UInt, get_metal_resource_index(var, SPIRType::UInt), 1, 0, 0 }); + } + } + } + + // Now add inline uniform blocks. + for (uint32_t var_id : inline_block_vars) + { + auto &var = get(var_id); + uint32_t desc_set = get_decoration(var_id, DecorationDescriptorSet); + add_resource_name(var_id); + resources_in_set[desc_set].push_back( + { &var, to_name(var_id), SPIRType::Struct, get_metal_resource_index(var, SPIRType::Struct), 1, 0, 0 }); + } + + for (uint32_t desc_set = 0; desc_set < kMaxArgumentBuffers; desc_set++) + { + auto &resources = resources_in_set[desc_set]; + if (resources.empty()) + continue; + + assert(descriptor_set_is_argument_buffer(desc_set)); + + uint32_t next_id = ir.increase_bound_by(3); + uint32_t type_id = next_id + 1; + uint32_t ptr_type_id = next_id + 2; + argument_buffer_ids[desc_set] = next_id; + + auto &buffer_type = set(type_id, OpTypeStruct); + + buffer_type.basetype = SPIRType::Struct; + + if ((argument_buffer_device_storage_mask & (1u << desc_set)) != 0) + { + buffer_type.storage = StorageClassStorageBuffer; + // Make sure the argument buffer gets marked as const device. + set_decoration(next_id, DecorationNonWritable); + // Need to mark the type as a Block to enable this. + set_decoration(type_id, DecorationBlock); + } + else + buffer_type.storage = StorageClassUniform; + + auto buffer_type_name = join("spvDescriptorSetBuffer", desc_set); + set_name(type_id, buffer_type_name); + + auto &ptr_type = set(ptr_type_id, OpTypePointer); + ptr_type = buffer_type; + ptr_type.op = OpTypePointer; + ptr_type.pointer = true; + ptr_type.pointer_depth++; + ptr_type.parent_type = type_id; + + uint32_t buffer_variable_id = next_id; + auto &buffer_var = set(buffer_variable_id, ptr_type_id, StorageClassUniform); + auto buffer_name = join("spvDescriptorSet", desc_set); + set_name(buffer_variable_id, buffer_name); + + // Ids must be emitted in ID order. + stable_sort(begin(resources), end(resources), [&](const Resource &lhs, const Resource &rhs) -> bool { + return tie(lhs.index, lhs.basetype) < tie(rhs.index, rhs.basetype); + }); + + for (size_t i = 0; i < resources.size() - 1; i++) + { + auto &r1 = resources[i]; + auto &r2 = resources[i + 1]; + + if (r1.index == r2.index) + { + if (r1.overlapping_var_id) + r2.overlapping_var_id = r1.overlapping_var_id; + else + r2.overlapping_var_id = r1.var->self; + + set_extended_decoration(r2.var->self, SPIRVCrossDecorationOverlappingBinding, r2.overlapping_var_id); + } + } + + uint32_t member_index = 0; + uint32_t next_arg_buff_index = 0; + uint32_t prev_was_scalar_on_array_offset = 0; + for (auto &resource : resources) + { + auto &var = *resource.var; + auto &type = get_variable_data_type(var); + + if (is_var_runtime_size_array(var) && (argument_buffer_device_storage_mask & (1u << desc_set)) == 0) + SPIRV_CROSS_THROW("Runtime sized variables must be in device storage argument buffers."); + + // If needed, synthesize and add padding members. + // member_index and next_arg_buff_index are incremented when padding members are added. + if (msl_options.pad_argument_buffer_resources && resource.plane == 0 && resource.overlapping_var_id == 0) + { + auto rez_bind = get_argument_buffer_resource(desc_set, next_arg_buff_index - prev_was_scalar_on_array_offset); + rez_bind.count -= prev_was_scalar_on_array_offset; + + while (resource.index > next_arg_buff_index) + { + switch (rez_bind.basetype) + { + case SPIRType::Void: + case SPIRType::Boolean: + case SPIRType::SByte: + case SPIRType::UByte: + case SPIRType::Short: + case SPIRType::UShort: + case SPIRType::Int: + case SPIRType::UInt: + case SPIRType::Int64: + case SPIRType::UInt64: + case SPIRType::AtomicCounter: + case SPIRType::Half: + case SPIRType::Float: + case SPIRType::Double: + add_argument_buffer_padding_buffer_type(buffer_type, member_index, next_arg_buff_index, rez_bind); + break; + case SPIRType::Image: + add_argument_buffer_padding_image_type(buffer_type, member_index, next_arg_buff_index, rez_bind); + break; + case SPIRType::Sampler: + add_argument_buffer_padding_sampler_type(buffer_type, member_index, next_arg_buff_index, rez_bind); + break; + case SPIRType::SampledImage: + if (next_arg_buff_index == rez_bind.msl_sampler) + add_argument_buffer_padding_sampler_type(buffer_type, member_index, next_arg_buff_index, rez_bind); + else + add_argument_buffer_padding_image_type(buffer_type, member_index, next_arg_buff_index, rez_bind); + break; + default: + break; + } + + // After padding, retrieve the resource again. It will either be more padding, or the actual resource. + rez_bind = get_argument_buffer_resource(desc_set, next_arg_buff_index); + prev_was_scalar_on_array_offset = 0; + } + + uint32_t count = rez_bind.count; + + // If the current resource is an array in the descriptor, but is a scalar + // in the shader, only the first element will be consumed. The next pass + // will add a padding member to consume the remaining array elements. + if (count > 1 && type.array.empty()) + count = prev_was_scalar_on_array_offset = 1; + + // Adjust the number of slots consumed by current member itself. + next_arg_buff_index += resource.plane_count * count; + } + + // Here we're locking down the member name early before compilation loops, so ensure that + // the resource name is not reused, even through a reset(). + string mbr_name = ensure_valid_name(resource.name, "m"); + if (resource.plane > 0) + mbr_name += join(plane_name_suffix, resource.plane); + set_member_name(buffer_type.self, member_index, mbr_name); + + if (resource.basetype == SPIRType::Sampler && type.basetype != SPIRType::Sampler) + { + // Have to synthesize a sampler type here. + + bool type_is_array = !type.array.empty(); + uint32_t sampler_type_id = ir.increase_bound_by(type_is_array ? 2 : 1); + auto &new_sampler_type = set(sampler_type_id, OpTypeSampler); + new_sampler_type.basetype = SPIRType::Sampler; + new_sampler_type.storage = StorageClassUniformConstant; + + if (type_is_array) + { + uint32_t sampler_type_array_id = sampler_type_id + 1; + auto &sampler_type_array = set(sampler_type_array_id, OpTypeArray); + sampler_type_array = new_sampler_type; + sampler_type_array.array = type.array; + sampler_type_array.array_size_literal = type.array_size_literal; + sampler_type_array.parent_type = sampler_type_id; + buffer_type.member_types.push_back(sampler_type_array_id); + } + else + buffer_type.member_types.push_back(sampler_type_id); + } + else + { + uint32_t binding = get_decoration(var.self, DecorationBinding); + SetBindingPair pair = { desc_set, binding }; + + if (resource.basetype == SPIRType::Image || resource.basetype == SPIRType::Sampler || + resource.basetype == SPIRType::SampledImage || resource.basetype == SPIRType::AccelerationStructure) + { + // Drop pointer information when we emit the resources into a struct. + buffer_type.member_types.push_back(get_variable_data_type_id(var)); + if (has_extended_decoration(var.self, SPIRVCrossDecorationOverlappingBinding)) + { + if (!msl_options.supports_msl_version(3, 0)) + SPIRV_CROSS_THROW("Full mutable aliasing of argument buffer descriptors only works on Metal 3+."); + + auto &entry_func = get(ir.default_entry_point); + entry_func.fixup_hooks_in.push_back([this, resource]() { + emit_argument_buffer_aliased_descriptor(*resource.var, this->get(resource.overlapping_var_id)); + }); + } + else if (resource.plane == 0) + { + set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name)); + } + } + else if (buffers_requiring_dynamic_offset.count(pair)) + { + // Don't set the qualified name here; we'll define a variable holding the corrected buffer address later. + buffer_type.member_types.push_back(var.basetype); + auto &dynamic_buffer = buffers_requiring_dynamic_offset[pair]; + dynamic_buffer.var_id = var.self; + dynamic_buffer.mbr_name = mbr_name; + } + else if (inline_uniform_blocks.count(pair)) + { + // Put the buffer block itself into the argument buffer. + buffer_type.member_types.push_back(get_variable_data_type_id(var)); + set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name)); + } + else if (atomic_image_vars_emulated.count(var.self)) + { + // Emulate texture2D atomic operations. + // Don't set the qualified name: it's already set for this variable, + // and the code that references the buffer manually appends "_atomic" + // to the name. + uint32_t offset = ir.increase_bound_by(2); + uint32_t atomic_type_id = offset; + uint32_t type_ptr_id = offset + 1; + + SPIRType atomic_type { OpTypeInt }; + atomic_type.basetype = SPIRType::AtomicCounter; + atomic_type.width = 32; + atomic_type.vecsize = 1; + set(atomic_type_id, atomic_type); + + atomic_type.op = OpTypePointer; + atomic_type.pointer = true; + atomic_type.pointer_depth++; + atomic_type.parent_type = atomic_type_id; + atomic_type.storage = StorageClassStorageBuffer; + auto &atomic_ptr_type = set(type_ptr_id, atomic_type); + atomic_ptr_type.self = atomic_type_id; + + buffer_type.member_types.push_back(type_ptr_id); + } + else + { + buffer_type.member_types.push_back(var.basetype); + if (has_extended_decoration(var.self, SPIRVCrossDecorationOverlappingBinding)) + { + // Casting raw pointers is fine since their ABI is fixed, but anything opaque is deeply questionable on Metal 2. + if (get(resource.overlapping_var_id).storage == StorageClassUniformConstant && + !msl_options.supports_msl_version(3, 0)) + { + SPIRV_CROSS_THROW("Full mutable aliasing of argument buffer descriptors only works on Metal 3+."); + } + + auto &entry_func = get(ir.default_entry_point); + + entry_func.fixup_hooks_in.push_back([this, resource]() { + emit_argument_buffer_aliased_descriptor(*resource.var, this->get(resource.overlapping_var_id)); + }); + } + else if (type.array.empty()) + set_qualified_name(var.self, join("(*", to_name(buffer_variable_id), ".", mbr_name, ")")); + else + set_qualified_name(var.self, join(to_name(buffer_variable_id), ".", mbr_name)); + } + } + + set_extended_member_decoration(buffer_type.self, member_index, SPIRVCrossDecorationResourceIndexPrimary, + resource.index); + set_extended_member_decoration(buffer_type.self, member_index, SPIRVCrossDecorationInterfaceOrigID, + var.self); + if (has_extended_decoration(var.self, SPIRVCrossDecorationOverlappingBinding)) + set_extended_member_decoration(buffer_type.self, member_index, SPIRVCrossDecorationOverlappingBinding); + member_index++; + } + + if (msl_options.replace_recursive_inputs && type_contains_recursion(buffer_type)) + { + recursive_inputs.insert(type_id); + auto &entry_func = this->get(ir.default_entry_point); + auto addr_space = get_variable_address_space(buffer_var); + entry_func.fixup_hooks_in.push_back([this, addr_space, buffer_name, buffer_type_name]() { + statement(addr_space, " auto& ", buffer_name, " = *(", addr_space, " ", buffer_type_name, "*)", buffer_name, "_vp;"); + }); + } + } +} + +// Return the resource type of the app-provided resources for the descriptor set, +// that matches the resource index of the argument buffer index. +// This is a two-step lookup, first lookup the resource binding number from the argument buffer index, +// then lookup the resource binding using the binding number. +const MSLResourceBinding &CompilerMSL::get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx) const +{ + auto stage = get_entry_point().model; + StageSetBinding arg_idx_tuple = { stage, desc_set, arg_idx }; + auto arg_itr = resource_arg_buff_idx_to_binding_number.find(arg_idx_tuple); + if (arg_itr != end(resource_arg_buff_idx_to_binding_number)) + { + StageSetBinding bind_tuple = { stage, desc_set, arg_itr->second }; + auto bind_itr = resource_bindings.find(bind_tuple); + if (bind_itr != end(resource_bindings)) + return bind_itr->second.first; + } + SPIRV_CROSS_THROW("Argument buffer resource base type could not be determined. When padding argument buffer " + "elements, all descriptor set resources must be supplied with a base type by the app."); +} + +// Adds an argument buffer padding argument buffer type as one or more members of the struct type at the member index. +// Metal does not support arrays of buffers, so these are emitted as multiple struct members. +void CompilerMSL::add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx, + uint32_t &arg_buff_index, MSLResourceBinding &rez_bind) +{ + if (!argument_buffer_padding_buffer_type_id) + { + uint32_t buff_type_id = ir.increase_bound_by(2); + auto &buff_type = set(buff_type_id, OpNop); + buff_type.basetype = rez_bind.basetype; + buff_type.storage = StorageClassUniformConstant; + + uint32_t ptr_type_id = buff_type_id + 1; + auto &ptr_type = set(ptr_type_id, OpTypePointer); + ptr_type = buff_type; + ptr_type.op = OpTypePointer; + ptr_type.pointer = true; + ptr_type.pointer_depth++; + ptr_type.parent_type = buff_type_id; + + argument_buffer_padding_buffer_type_id = ptr_type_id; + } + + add_argument_buffer_padding_type(argument_buffer_padding_buffer_type_id, struct_type, mbr_idx, arg_buff_index, rez_bind.count); +} + +// Adds an argument buffer padding argument image type as a member of the struct type at the member index. +void CompilerMSL::add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx, + uint32_t &arg_buff_index, MSLResourceBinding &rez_bind) +{ + if (!argument_buffer_padding_image_type_id) + { + uint32_t base_type_id = ir.increase_bound_by(2); + auto &base_type = set(base_type_id, OpTypeFloat); + base_type.basetype = SPIRType::Float; + base_type.width = 32; + + uint32_t img_type_id = base_type_id + 1; + auto &img_type = set(img_type_id, OpTypeImage); + img_type.basetype = SPIRType::Image; + img_type.storage = StorageClassUniformConstant; + + img_type.image.type = base_type_id; + img_type.image.dim = Dim2D; + img_type.image.depth = false; + img_type.image.arrayed = false; + img_type.image.ms = false; + img_type.image.sampled = 1; + img_type.image.format = ImageFormatUnknown; + img_type.image.access = AccessQualifierMax; + + argument_buffer_padding_image_type_id = img_type_id; + } + + add_argument_buffer_padding_type(argument_buffer_padding_image_type_id, struct_type, mbr_idx, arg_buff_index, rez_bind.count); +} + +// Adds an argument buffer padding argument sampler type as a member of the struct type at the member index. +void CompilerMSL::add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx, + uint32_t &arg_buff_index, MSLResourceBinding &rez_bind) +{ + if (!argument_buffer_padding_sampler_type_id) + { + uint32_t samp_type_id = ir.increase_bound_by(1); + auto &samp_type = set(samp_type_id, OpTypeSampler); + samp_type.basetype = SPIRType::Sampler; + samp_type.storage = StorageClassUniformConstant; + + argument_buffer_padding_sampler_type_id = samp_type_id; + } + + add_argument_buffer_padding_type(argument_buffer_padding_sampler_type_id, struct_type, mbr_idx, arg_buff_index, rez_bind.count); +} + +// Adds the argument buffer padding argument type as a member of the struct type at the member index. +// Advances both arg_buff_index and mbr_idx to next argument slots. +void CompilerMSL::add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx, + uint32_t &arg_buff_index, uint32_t count) +{ + uint32_t type_id = mbr_type_id; + if (count > 1) + { + uint32_t ary_type_id = ir.increase_bound_by(1); + auto &ary_type = set(ary_type_id, get(type_id)); + ary_type.op = OpTypeArray; + ary_type.array.push_back(count); + ary_type.array_size_literal.push_back(true); + ary_type.parent_type = type_id; + type_id = ary_type_id; + } + + set_member_name(struct_type.self, mbr_idx, join("_m", arg_buff_index, "_pad")); + set_extended_member_decoration(struct_type.self, mbr_idx, SPIRVCrossDecorationResourceIndexPrimary, arg_buff_index); + struct_type.member_types.push_back(type_id); + + arg_buff_index += count; + mbr_idx++; +} + +void CompilerMSL::activate_argument_buffer_resources() +{ + // For ABI compatibility, force-enable all resources which are part of argument buffers. + ir.for_each_typed_id([&](uint32_t self, const SPIRVariable &) { + if (!has_decoration(self, DecorationDescriptorSet)) + return; + + uint32_t desc_set = get_decoration(self, DecorationDescriptorSet); + if (descriptor_set_is_argument_buffer(desc_set)) + add_active_interface_variable(self); + }); +} + +bool CompilerMSL::using_builtin_array() const +{ + return msl_options.force_native_arrays || is_using_builtin_array; +} + +void CompilerMSL::set_combined_sampler_suffix(const char *suffix) +{ + sampler_name_suffix = suffix; +} + +const char *CompilerMSL::get_combined_sampler_suffix() const +{ + return sampler_name_suffix.c_str(); +} + +bool CompilerMSL::specialization_constant_is_macro(uint32_t const_id) const +{ + return constant_macro_ids.find(const_id) != constant_macro_ids.end(); +} + +// Start with all fast math flags enabled, and selectively disable based execution modes and float controls +uint32_t CompilerMSL::get_fp_fast_math_flags(bool incl_ops) const +{ + uint32_t fp_flags = ~0; + auto &ep = get_entry_point(); + + if (ep.flags.get(ExecutionModeSignedZeroInfNanPreserve)) + fp_flags &= ~(FPFastMathModeNSZMask | FPFastMathModeNotInfMask | FPFastMathModeNotNaNMask); + + if (ep.flags.get(ExecutionModeContractionOff)) + fp_flags &= ~(FPFastMathModeAllowContractMask); + + for (auto &fp_pair : ep.fp_fast_math_defaults) + if (fp_pair.second) + fp_flags &= get(fp_pair.second).scalar(); + + if (incl_ops) + for (auto &p_m : ir.meta) + if (p_m.second.decoration.decoration_flags.get(DecorationFPFastMathMode)) + fp_flags &= p_m.second.decoration.fp_fast_math_mode; + + return fp_flags; +} + +void CompilerMSL::emit_block_hints(const SPIRBlock &) +{ +} + +void CompilerMSL::emit_mesh_entry_point() +{ + auto &ep = get_entry_point(); + auto &f = get(ir.default_entry_point); + + const uint32_t func_id = ir.increase_bound_by(3); + const uint32_t block_id = func_id + 1; + const uint32_t ret_id = func_id + 2; + auto &wrapped_main = set(func_id, f.return_type, f.function_type); + + wrapped_main.blocks.push_back(block_id); + wrapped_main.entry_block = block_id; + + auto &wrapped_entry = set(block_id); + wrapped_entry.terminator = SPIRBlock::Return; + + // Push call to original 'main' + Instruction ix = {}; + ix.op = OpFunctionCall; + ix.offset = uint32_t(ir.spirv.size()); + ix.length = 3; + + ir.spirv.push_back(f.return_type); + ir.spirv.push_back(ret_id); + ir.spirv.push_back(ep.self); + + wrapped_entry.ops.push_back(ix); + + // relace entry-point for new one + SPIREntryPoint proxy_ep = ep; + proxy_ep.self = func_id; + ir.entry_points.insert(std::make_pair(func_id, proxy_ep)); + ir.meta[func_id] = ir.meta[ir.default_entry_point]; + ir.meta[ir.default_entry_point].decoration.alias.clear(); + + ir.default_entry_point = func_id; +} + +void CompilerMSL::emit_mesh_outputs() +{ + auto &mode = get_entry_point(); + + // predefined thread count or zero, if specialization constant is in use + uint32_t num_invocations = 0; + if (mode.workgroup_size.id_x == 0 && mode.workgroup_size.id_y == 0 && mode.workgroup_size.id_z == 0) + num_invocations = mode.workgroup_size.x * mode.workgroup_size.y * mode.workgroup_size.z; + + statement("threadgroup_barrier(mem_flags::mem_threadgroup);"); + statement("if (spvMeshSizes.y == 0)"); + begin_scope(); + statement("return;"); + end_scope(); + statement("spvMesh.set_primitive_count(spvMeshSizes.y);"); + + statement("const uint spvThreadCount [[maybe_unused]] = (gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z);"); + + if (mesh_out_per_vertex != 0) + { + auto &type_vert = get(mesh_out_per_vertex); + + if (num_invocations < mode.output_vertices) + { + statement("for (uint spvVI = gl_LocalInvocationIndex; spvVI < spvMeshSizes.x; spvVI += spvThreadCount)"); + } + else + { + statement("const uint spvVI = gl_LocalInvocationIndex;"); + statement("if (gl_LocalInvocationIndex < spvMeshSizes.x)"); + } + + begin_scope(); + + statement("spvPerVertex spvV = {};"); + for (uint32_t index = 0; index < uint32_t(type_vert.member_types.size()); ++index) + { + uint32_t orig_var = get_extended_member_decoration(type_vert.self, index, SPIRVCrossDecorationInterfaceOrigID); + uint32_t orig_id = get_extended_member_decoration(type_vert.self, index, SPIRVCrossDecorationInterfaceMemberIndex); + + // Clip/cull distances are special-case + if (orig_var == 0 && orig_id == (~0u)) + continue; + + auto &orig = get(orig_var); + auto &orig_type = get(orig.basetype); + + // FIXME: Need to deal with complex composite IO types. These may need extra unroll, etc. + + BuiltIn builtin = BuiltInMax; + std::string access; + if (orig_type.basetype == SPIRType::Struct) + { + if (has_member_decoration(orig_type.self, orig_id, DecorationBuiltIn)) + builtin = BuiltIn(get_member_decoration(orig_type.self, orig_id, DecorationBuiltIn)); + + switch (builtin) + { + case BuiltInPosition: + case BuiltInPointSize: + case BuiltInClipDistance: + case BuiltInCullDistance: + access = "." + builtin_to_glsl(builtin, StorageClassOutput); + break; + default: + access = "." + to_member_name(orig_type, orig_id); + break; + } + + if (has_member_decoration(type_vert.self, index, DecorationIndex)) + { + // Declare the Clip/CullDistance as [[user(clip/cullN)]]. + const uint32_t orig_index = get_member_decoration(type_vert.self, index, DecorationIndex); + access += "[" + to_string(orig_index) + "]"; + statement("spvV.", builtin_to_glsl(builtin, StorageClassOutput), "[", orig_index, "] = ", to_name(orig_var), "[spvVI]", access, ";"); + } + } + + statement("spvV.", to_member_name(type_vert, index), " = ", to_name(orig_var), "[spvVI]", access, ";"); + if (options.vertex.flip_vert_y && builtin == BuiltInPosition) + { + statement("spvV.", to_member_name(type_vert, index), ".y = -(", "spvV.", + to_member_name(type_vert, index), ".y);", " // Invert Y-axis for Metal"); + } + } + statement("spvMesh.set_vertex(spvVI, spvV);"); + end_scope(); + } + + if (mesh_out_per_primitive != 0 || builtin_mesh_primitive_indices_id != 0) + { + if (num_invocations < mode.output_primitives) + { + statement("for (uint spvPI = gl_LocalInvocationIndex; spvPI < spvMeshSizes.y; spvPI += spvThreadCount)"); + } + else + { + statement("const uint spvPI = gl_LocalInvocationIndex;"); + statement("if (gl_LocalInvocationIndex < spvMeshSizes.y)"); + } + + // FIXME: Need to deal with complex composite IO types. These may need extra unroll, etc. + + begin_scope(); + + if (builtin_mesh_primitive_indices_id != 0) + { + if (mode.flags.get(ExecutionModeOutputTrianglesEXT)) + { + statement("spvMesh.set_index(spvPI * 3u + 0u, gl_PrimitiveTriangleIndicesEXT[spvPI].x);"); + statement("spvMesh.set_index(spvPI * 3u + 1u, gl_PrimitiveTriangleIndicesEXT[spvPI].y);"); + statement("spvMesh.set_index(spvPI * 3u + 2u, gl_PrimitiveTriangleIndicesEXT[spvPI].z);"); + } + else if (mode.flags.get(ExecutionModeOutputLinesEXT)) + { + statement("spvMesh.set_index(spvPI * 2u + 0u, gl_PrimitiveLineIndicesEXT[spvPI].x);"); + statement("spvMesh.set_index(spvPI * 2u + 1u, gl_PrimitiveLineIndicesEXT[spvPI].y);"); + } + else + { + statement("spvMesh.set_index(spvPI, gl_PrimitivePointIndicesEXT[spvPI]);"); + } + } + + if (mesh_out_per_primitive != 0) + { + auto &type_prim = get(mesh_out_per_primitive); + statement("spvPerPrimitive spvP = {};"); + for (uint32_t index = 0; index < uint32_t(type_prim.member_types.size()); ++index) + { + uint32_t orig_var = + get_extended_member_decoration(type_prim.self, index, SPIRVCrossDecorationInterfaceOrigID); + uint32_t orig_id = + get_extended_member_decoration(type_prim.self, index, SPIRVCrossDecorationInterfaceMemberIndex); + auto &orig = get(orig_var); + auto &orig_type = get(orig.basetype); + + BuiltIn builtin = BuiltInMax; + std::string access; + if (orig_type.basetype == SPIRType::Struct) + { + if (has_member_decoration(orig_type.self, orig_id, DecorationBuiltIn)) + builtin = BuiltIn(get_member_decoration(orig_type.self, orig_id, DecorationBuiltIn)); + + switch (builtin) + { + case BuiltInPrimitiveId: + case BuiltInLayer: + case BuiltInViewportIndex: + case BuiltInCullPrimitiveEXT: + case BuiltInPrimitiveShadingRateKHR: + access = "." + builtin_to_glsl(builtin, StorageClassOutput); + break; + default: + access = "." + to_member_name(orig_type, orig_id); + } + } + statement("spvP.", to_member_name(type_prim, index), " = ", to_name(orig_var), "[spvPI]", access, ";"); + } + statement("spvMesh.set_primitive(spvPI, spvP);"); + } + + end_scope(); + } +} + +void CompilerMSL::emit_mesh_tasks(SPIRBlock &block) +{ + // GLSL: Once this instruction is called, the workgroup must be terminated immediately, and the mesh shaders are launched. + // TODO: find relieble and clean of terminating shader. + flush_variable_declaration(builtin_task_grid_id); + statement("spvMgp.set_threadgroups_per_grid(uint3(", to_unpacked_expression(block.mesh.groups[0]), ", ", + to_unpacked_expression(block.mesh.groups[1]), ", ", to_unpacked_expression(block.mesh.groups[2]), "));"); + // This is correct if EmitMeshTasks is called in the entry function for shader. + // Only viable solutions would be: + // - Caller ensures the SPIR-V is inlined, then this always holds true. + // - Pass down a "should terminate" bool to leaf functions and chain return (horrible and disgusting, let's not). + statement("return;"); +} + +void CompilerMSL::emit_workgroup_initialization(const SPIRVariable &var) +{ + auto &type = get_variable_data_type(var); + + begin_scope(); + + if (type.array.empty() && type.member_types.empty()) + { + // For simple shared variables, we just initialize it in thread 0 of the block + // We use short to represent bool for threadgroup variable to workaround compiler bug, + // so we do a temporary fixup here. Alas. (see the type_to_glsl method) + bool is_boolean = type.basetype == SPIRType::Boolean; + if (is_boolean) + type.basetype = SPIRType::Short; + + statement("if (gl_LocalInvocationIndex == 0)"); + begin_scope(); + statement(to_name(var.self), " = ", to_initializer_expression(var), ";"); + end_scope(); + + if (is_boolean) + type.basetype = SPIRType::Boolean; + } + else + { + // Otherwise, we use a loop to cooperatively initialize the memory within the group + + // First, we define a few variable names; + string var_name = to_name(var.self); + string var_ptr_name = join(var_name, "_ptr"); + string var_size_name = join(var_name, "_sz"); + string var_pos_name = join(var_name, "_pos"); + string var_stride_name = join(var_name, "_stride"); + string var_ptr2_name = join(var_name, "_ptr2"); + + statement("threadgroup uint *", var_ptr_name, " = (threadgroup uint *)&", var_name, ";"); + statement("uint ", var_size_name, " = ", "sizeof(", var_name, ");"); + statement("uint ", var_pos_name, " = gl_LocalInvocationIndex;"); + statement("uint ", var_stride_name, " = gl_WorkGroupSize.x * gl_WorkGroupSize.y * gl_WorkGroupSize.z;"); + + statement("while (sizeof(uint) * ", var_pos_name, " < ", var_size_name, ")"); + begin_scope(); + statement(var_ptr_name, "[", var_pos_name, "] = 0u;"); + statement(var_pos_name, " += ", var_stride_name, ";"); + end_scope(); + + statement("if (gl_LocalInvocationIndex == 0)"); + begin_scope(); + statement(var_pos_name, " = (", var_size_name, " / sizeof(uint)) * sizeof(uint);"); + statement("threadgroup uchar *", var_ptr2_name, " = (threadgroup uchar *)&", var_name, ";"); + + statement("while (", var_pos_name, " < ", var_size_name, ")"); + begin_scope(); + statement(var_ptr2_name, "[", var_pos_name, "] = '\\0';"); + statement(var_pos_name, "++;"); + end_scope(); + end_scope(); + } + + statement("threadgroup_barrier(mem_flags::mem_threadgroup);"); + + end_scope(); +} + +string CompilerMSL::additional_fixed_sample_mask_str() const +{ + char print_buffer[32]; +#ifdef _MSC_VER + // snprintf does not exist or is buggy on older MSVC versions, some of + // them being used by MinGW. Use sprintf instead and disable + // corresponding warning. +#pragma warning(push) +#pragma warning(disable : 4996) +#endif +#if _WIN32 + sprintf(print_buffer, "0x%x", msl_options.additional_fixed_sample_mask); +#else + snprintf(print_buffer, sizeof(print_buffer), "0x%x", msl_options.additional_fixed_sample_mask); +#endif +#ifdef _MSC_VER +#pragma warning(pop) +#endif + return print_buffer; +} diff --git a/thirdparty/SPIRV-Cross/spirv_msl.hpp b/thirdparty/SPIRV-Cross/spirv_msl.hpp new file mode 100644 index 000000000..1a28e0585 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_msl.hpp @@ -0,0 +1,1440 @@ +/* + * Copyright 2016-2021 The Brenwill Workshop Ltd. + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_MSL_HPP +#define SPIRV_CROSS_MSL_HPP + +#include "spirv_glsl.hpp" +#include +#include +#include +#include +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; + +// Indicates the format of a shader interface variable. Currently limited to specifying +// if the input is an 8-bit unsigned integer, 16-bit unsigned integer, or +// some other format. +enum MSLShaderVariableFormat +{ + MSL_SHADER_VARIABLE_FORMAT_OTHER = 0, + MSL_SHADER_VARIABLE_FORMAT_UINT8 = 1, + MSL_SHADER_VARIABLE_FORMAT_UINT16 = 2, + MSL_SHADER_VARIABLE_FORMAT_ANY16 = 3, + MSL_SHADER_VARIABLE_FORMAT_ANY32 = 4, + + // Deprecated aliases. + MSL_VERTEX_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER, + MSL_VERTEX_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8, + MSL_VERTEX_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16, + MSL_SHADER_INPUT_FORMAT_OTHER = MSL_SHADER_VARIABLE_FORMAT_OTHER, + MSL_SHADER_INPUT_FORMAT_UINT8 = MSL_SHADER_VARIABLE_FORMAT_UINT8, + MSL_SHADER_INPUT_FORMAT_UINT16 = MSL_SHADER_VARIABLE_FORMAT_UINT16, + MSL_SHADER_INPUT_FORMAT_ANY16 = MSL_SHADER_VARIABLE_FORMAT_ANY16, + MSL_SHADER_INPUT_FORMAT_ANY32 = MSL_SHADER_VARIABLE_FORMAT_ANY32, + + MSL_SHADER_VARIABLE_FORMAT_INT_MAX = 0x7fffffff +}; + +// Indicates the rate at which a variable changes value, one of: per-vertex, +// per-primitive, or per-patch. +enum MSLShaderVariableRate +{ + MSL_SHADER_VARIABLE_RATE_PER_VERTEX = 0, + MSL_SHADER_VARIABLE_RATE_PER_PRIMITIVE = 1, + MSL_SHADER_VARIABLE_RATE_PER_PATCH = 2, + + MSL_SHADER_VARIABLE_RATE_INT_MAX = 0x7fffffff, +}; + +// Defines MSL characteristics of a shader interface variable at a particular location. +// After compilation, it is possible to query whether or not this location was used. +// If vecsize is nonzero, it must be greater than or equal to the vecsize declared in the shader, +// or behavior is undefined. +struct MSLShaderInterfaceVariable +{ + uint32_t location = 0; + uint32_t component = 0; + MSLShaderVariableFormat format = MSL_SHADER_VARIABLE_FORMAT_OTHER; + BuiltIn builtin = BuiltInMax; + uint32_t vecsize = 0; + MSLShaderVariableRate rate = MSL_SHADER_VARIABLE_RATE_PER_VERTEX; +}; + +// Matches the binding index of a MSL resource for a binding within a descriptor set. +// Taken together, the stage, desc_set and binding combine to form a reference to a resource +// descriptor used in a particular shading stage. The count field indicates the number of +// resources consumed by this binding, if the binding represents an array of resources. +// If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value +// will be used to declare the array size in MSL, which does not support run-time-sized arrays. +// If pad_argument_buffer_resources is enabled, the base_type and count values are used to +// specify the base type and array size of the resource in the argument buffer, if that resource +// is not defined and used by the shader. With pad_argument_buffer_resources enabled, this +// information will be used to pad the argument buffer structure, in order to align that +// structure consistently for all uses, across all shaders, of the descriptor set represented +// by the arugment buffer. If pad_argument_buffer_resources is disabled, base_type does not +// need to be populated, and if the resource is also not a run-time sized array, the count +// field does not need to be populated. +// If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set, +// and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we +// remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure. +// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will +// become a [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used. +struct MSLResourceBinding +{ + ExecutionModel stage = ExecutionModelMax; + SPIRType::BaseType basetype = SPIRType::Unknown; + uint32_t desc_set = 0; + uint32_t binding = 0; + uint32_t count = 0; + uint32_t msl_buffer = 0; + uint32_t msl_texture = 0; + uint32_t msl_sampler = 0; +}; + +enum MSLSamplerCoord +{ + MSL_SAMPLER_COORD_NORMALIZED = 0, + MSL_SAMPLER_COORD_PIXEL = 1, + MSL_SAMPLER_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerFilter +{ + MSL_SAMPLER_FILTER_NEAREST = 0, + MSL_SAMPLER_FILTER_LINEAR = 1, + MSL_SAMPLER_FILTER_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerMipFilter +{ + MSL_SAMPLER_MIP_FILTER_NONE = 0, + MSL_SAMPLER_MIP_FILTER_NEAREST = 1, + MSL_SAMPLER_MIP_FILTER_LINEAR = 2, + MSL_SAMPLER_MIP_FILTER_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerAddress +{ + MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO = 0, + MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE = 1, + MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER = 2, + MSL_SAMPLER_ADDRESS_REPEAT = 3, + MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT = 4, + MSL_SAMPLER_ADDRESS_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerCompareFunc +{ + MSL_SAMPLER_COMPARE_FUNC_NEVER = 0, + MSL_SAMPLER_COMPARE_FUNC_LESS = 1, + MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL = 2, + MSL_SAMPLER_COMPARE_FUNC_GREATER = 3, + MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL = 4, + MSL_SAMPLER_COMPARE_FUNC_EQUAL = 5, + MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL = 6, + MSL_SAMPLER_COMPARE_FUNC_ALWAYS = 7, + MSL_SAMPLER_COMPARE_FUNC_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerBorderColor +{ + MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK = 0, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK = 1, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE = 2, + MSL_SAMPLER_BORDER_COLOR_INT_MAX = 0x7fffffff +}; + +enum MSLFormatResolution +{ + MSL_FORMAT_RESOLUTION_444 = 0, + MSL_FORMAT_RESOLUTION_422, + MSL_FORMAT_RESOLUTION_420, + MSL_FORMAT_RESOLUTION_INT_MAX = 0x7fffffff +}; + +enum MSLChromaLocation +{ + MSL_CHROMA_LOCATION_COSITED_EVEN = 0, + MSL_CHROMA_LOCATION_MIDPOINT, + MSL_CHROMA_LOCATION_INT_MAX = 0x7fffffff +}; + +enum MSLComponentSwizzle +{ + MSL_COMPONENT_SWIZZLE_IDENTITY = 0, + MSL_COMPONENT_SWIZZLE_ZERO, + MSL_COMPONENT_SWIZZLE_ONE, + MSL_COMPONENT_SWIZZLE_R, + MSL_COMPONENT_SWIZZLE_G, + MSL_COMPONENT_SWIZZLE_B, + MSL_COMPONENT_SWIZZLE_A, + MSL_COMPONENT_SWIZZLE_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerYCbCrModelConversion +{ + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_709, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_601, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_BT_2020, + MSL_SAMPLER_YCBCR_MODEL_CONVERSION_INT_MAX = 0x7fffffff +}; + +enum MSLSamplerYCbCrRange +{ + MSL_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, + MSL_SAMPLER_YCBCR_RANGE_ITU_NARROW, + MSL_SAMPLER_YCBCR_RANGE_INT_MAX = 0x7fffffff +}; + +struct MSLConstexprSampler +{ + MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED; + MSLSamplerFilter min_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerFilter mag_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerMipFilter mip_filter = MSL_SAMPLER_MIP_FILTER_NONE; + MSLSamplerAddress s_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress t_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress r_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerCompareFunc compare_func = MSL_SAMPLER_COMPARE_FUNC_NEVER; + MSLSamplerBorderColor border_color = MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK; + float lod_clamp_min = 0.0f; + float lod_clamp_max = 1000.0f; + int max_anisotropy = 1; + + // Sampler Y'CbCr conversion parameters + uint32_t planes = 0; + MSLFormatResolution resolution = MSL_FORMAT_RESOLUTION_444; + MSLSamplerFilter chroma_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLChromaLocation x_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; + MSLChromaLocation y_chroma_offset = MSL_CHROMA_LOCATION_COSITED_EVEN; + MSLComponentSwizzle swizzle[4]; // IDENTITY, IDENTITY, IDENTITY, IDENTITY + MSLSamplerYCbCrModelConversion ycbcr_model = MSL_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY; + MSLSamplerYCbCrRange ycbcr_range = MSL_SAMPLER_YCBCR_RANGE_ITU_FULL; + uint32_t bpc = 8; + + bool compare_enable = false; + bool lod_clamp_enable = false; + bool anisotropy_enable = false; + bool ycbcr_conversion_enable = false; + + MSLConstexprSampler() + { + for (uint32_t i = 0; i < 4; i++) + swizzle[i] = MSL_COMPONENT_SWIZZLE_IDENTITY; + } + bool swizzle_is_identity() const + { + return (swizzle[0] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[1] == MSL_COMPONENT_SWIZZLE_IDENTITY && + swizzle[2] == MSL_COMPONENT_SWIZZLE_IDENTITY && swizzle[3] == MSL_COMPONENT_SWIZZLE_IDENTITY); + } + bool swizzle_has_one_or_zero() const + { + return (swizzle[0] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[0] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[1] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[1] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[2] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[2] == MSL_COMPONENT_SWIZZLE_ONE || + swizzle[3] == MSL_COMPONENT_SWIZZLE_ZERO || swizzle[3] == MSL_COMPONENT_SWIZZLE_ONE); + } +}; + +// Special constant used in a MSLResourceBinding desc_set +// element to indicate the bindings for the push constants. +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstDescSet = ResourceBindingPushConstantDescriptorSet; + +// Special constant used in a MSLResourceBinding binding +// element to indicate the bindings for the push constants. +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstBinding = ResourceBindingPushConstantBinding; + +// Special constant used in a MSLResourceBinding binding +// element to indicate the buffer binding for swizzle buffers. +static const uint32_t kSwizzleBufferBinding = ~(1u); + +// Special constant used in a MSLResourceBinding binding +// element to indicate the buffer binding for buffer size buffers to support OpArrayLength. +static const uint32_t kBufferSizeBufferBinding = ~(2u); + +// Special constant used in a MSLResourceBinding binding +// element to indicate the buffer binding used for the argument buffer itself. +// This buffer binding should be kept as small as possible as all automatic bindings for buffers +// will start at max(kArgumentBufferBinding) + 1. +static const uint32_t kArgumentBufferBinding = ~(3u); + +static const uint32_t kMaxArgumentBuffers = 8; + +// Decompiles SPIR-V to Metal Shading Language +class CompilerMSL : public CompilerGLSL +{ +public: + // Options for compiling to Metal Shading Language + struct Options + { + typedef enum + { + iOS = 0, + macOS = 1 + } Platform; + + 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 + uint32_t r32ui_linear_texture_alignment = 4; + uint32_t r32ui_alignment_constant_id = 65535; + uint32_t swizzle_buffer_index = 30; + uint32_t indirect_params_buffer_index = 29; + uint32_t shader_output_buffer_index = 28; + uint32_t shader_patch_output_buffer_index = 27; + uint32_t shader_tess_factor_buffer_index = 26; + uint32_t buffer_size_buffer_index = 25; + uint32_t view_mask_buffer_index = 24; + uint32_t dynamic_offsets_buffer_index = 23; + uint32_t shader_input_buffer_index = 22; + uint32_t shader_index_buffer_index = 21; + uint32_t shader_patch_input_buffer_index = 20; + uint32_t shader_input_wg_index = 0; + uint32_t device_index = 0; + uint32_t enable_frag_output_mask = 0xffffffff; + // Metal doesn't allow setting a fixed sample mask directly in the pipeline. + // We can evade this restriction by ANDing the internal sample_mask output + // of the shader with the additional fixed sample mask. + uint32_t additional_fixed_sample_mask = 0xffffffff; + bool enable_point_size_builtin = true; + bool enable_point_size_default = false; + float default_point_size = 1.0f; + bool enable_frag_depth_builtin = true; + bool enable_frag_stencil_ref_builtin = true; + bool disable_rasterization = false; + bool capture_output_to_buffer = false; + bool swizzle_texture_samples = false; + bool tess_domain_origin_lower_left = false; + bool multiview = false; + bool multiview_layered_rendering = true; + bool view_index_from_device_index = false; + bool dispatch_base = false; + bool texture_1D_as_2D = false; + + // Enable use of Metal argument buffers. + // MSL 2.0 must also be enabled. + bool argument_buffers = false; + + // Defines Metal argument buffer tier levels. + // Uses same values as Metal MTLArgumentBuffersTier enumeration. + enum class ArgumentBuffersTier + { + Tier1 = 0, + Tier2 = 1, + }; + + // When using Metal argument buffers, indicates the Metal argument buffer tier level supported by the Metal platform. + // Ignored when Options::argument_buffers is disabled. + // - Tier1 supports writable images on macOS, but not on iOS. + // - Tier2 supports writable images on macOS and iOS, and higher resource count limits. + // Tier capabilities based on recommendations from Apple engineering. + ArgumentBuffersTier argument_buffers_tier = ArgumentBuffersTier::Tier1; + + // Enables specifick argument buffer format with extra information to track SSBO-length + bool runtime_array_rich_descriptor = false; + + // Ensures vertex and instance indices start at zero. This reflects the behavior of HLSL with SV_VertexID and SV_InstanceID. + bool enable_base_index_zero = false; + + // Fragment output in MSL must have at least as many components as the render pass. + // Add support to explicit pad out components. + bool pad_fragment_output_components = false; + + // Specifies whether the iOS target version supports the [[base_vertex]] and [[base_instance]] attributes. + bool ios_support_base_vertex_instance = false; + + // Use Metal's native frame-buffer fetch API for subpass inputs. + bool use_framebuffer_fetch_subpasses = false; + + // Enables use of "fma" intrinsic for invariant float math + bool invariant_float_math = false; + + // Emulate texturecube_array with texture2d_array for iOS where this type is not available + bool emulate_cube_array = false; + + // Allow user to enable decoration binding + bool enable_decoration_binding = false; + + // Requires MSL 2.1, use the native support for texel buffers. + bool texture_buffer_native = false; + + // Forces all resources which are part of an argument buffer to be considered active. + // This ensures ABI compatibility between shaders where some resources might be unused, + // and would otherwise declare a different IAB. + bool force_active_argument_buffer_resources = false; + + // Aligns each resource in an argument buffer to its assigned index value, id(N), + // by adding synthetic padding members in the argument buffer struct for any resources + // in the argument buffer that are not defined and used by the shader. This allows + // the shader to index into the correct argument in a descriptor set argument buffer + // that is shared across shaders, where not all resources in the argument buffer are + // defined in each shader. For this to work, an MSLResourceBinding must be provided for + // all descriptors in any descriptor set held in an argument buffer in the shader, and + // that MSLResourceBinding must have the basetype and count members populated correctly. + // The implementation here assumes any inline blocks in the argument buffer is provided + // in a Metal buffer, and doesn't take into consideration inline blocks that are + // optionally embedded directly into the argument buffer via add_inline_uniform_block(). + bool pad_argument_buffer_resources = false; + + // Forces the use of plain arrays, which works around certain driver bugs on certain versions + // of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210. + // May reduce performance in scenarios where arrays are copied around as value-types. + bool force_native_arrays = false; + + // If a shader writes clip distance, also emit user varyings which + // can be read in subsequent stages. + bool enable_clip_distance_user_varying = true; + + // In a tessellation control shader, assume that more than one patch can be processed in a + // single workgroup. This requires changes to the way the InvocationId and PrimitiveId + // builtins are processed, but should result in more efficient usage of the GPU. + bool multi_patch_workgroup = false; + + // Use storage buffers instead of vertex-style attributes for tessellation evaluation + // input. This may require conversion of inputs in the generated post-tessellation + // vertex shader, but allows the use of nested arrays. + bool raw_buffer_tese_input = false; + + // If set, a vertex shader will be compiled as part of a tessellation pipeline. + // It will be translated as a compute kernel, so it can use the global invocation ID + // to index the output buffer. + bool vertex_for_tessellation = false; + + // Assume that SubpassData images have multiple layers. Layered input attachments + // are addressed relative to the Layer output from the vertex pipeline. This option + // has no effect with multiview, since all input attachments are assumed to be layered + // and will be addressed using the current ViewIndex. + bool arrayed_subpass_input = false; + + // Whether to use SIMD-group or quadgroup functions to implement group non-uniform + // operations. Some GPUs on iOS do not support the SIMD-group functions, only the + // quadgroup functions. + bool ios_use_simdgroup_functions = false; + + // If set, the subgroup size will be assumed to be one, and subgroup-related + // builtins and operations will be emitted accordingly. This mode is intended to + // be used by MoltenVK on hardware/software configurations which do not provide + // sufficient support for subgroups. + bool emulate_subgroups = false; + + // If nonzero, a fixed subgroup size to assume. Metal, similarly to VK_EXT_subgroup_size_control, + // allows the SIMD-group size (aka thread execution width) to vary depending on + // register usage and requirements. In certain circumstances--for example, a pipeline + // in MoltenVK without VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT-- + // this is undesirable. This fixes the value of the SubgroupSize builtin, instead of + // mapping it to the Metal builtin [[thread_execution_width]]. If the thread + // execution width is reduced, the extra invocations will appear to be inactive. + // If zero, the SubgroupSize will be allowed to vary, and the builtin will be mapped + // to the Metal [[thread_execution_width]] builtin. + uint32_t fixed_subgroup_size = 0; + + enum class IndexType + { + None = 0, + UInt16 = 1, + UInt32 = 2 + }; + + // The type of index in the index buffer, if present. For a compute shader, Metal + // requires specifying the indexing at pipeline creation, rather than at draw time + // as with graphics pipelines. This means we must create three different pipelines, + // for no indexing, 16-bit indices, and 32-bit indices. Each requires different + // handling for the gl_VertexIndex builtin. We may as well, then, create three + // different shaders for these three scenarios. + IndexType vertex_index_type = IndexType::None; + + // If set, a dummy [[sample_id]] input is added to a fragment shader if none is present. + // This will force the shader to run at sample rate, assuming Metal does not optimize + // the extra threads away. + bool force_sample_rate_shading = false; + + // If set, gl_HelperInvocation will be set manually whenever a fragment is discarded. + // Some Metal devices have a bug where simd_is_helper_thread() does not return true + // after a fragment has been discarded. This is a workaround that is only expected to be needed + // until the bug is fixed in Metal; it is provided as an option to allow disabling it when that occurs. + bool manual_helper_invocation_updates = true; + + // If set, extra checks will be emitted in fragment shaders to prevent writes + // from discarded fragments. Some Metal devices have a bug where writes to storage resources + // from discarded fragment threads continue to occur, despite the fragment being + // discarded. This is a workaround that is only expected to be needed until the + // bug is fixed in Metal; it is provided as an option so it can be enabled + // only when the bug is present. + bool check_discarded_frag_stores = false; + + // If set, Lod operands to OpImageSample*DrefExplicitLod for 1D and 2D array images + // will be implemented using a gradient instead of passing the level operand directly. + // Some Metal devices have a bug where the level() argument to depth2d_array::sample_compare() + // in a fragment shader is biased by some unknown amount, possibly dependent on the + // partial derivatives of the texture coordinates. This is a workaround that is only + // expected to be needed until the bug is fixed in Metal; it is provided as an option + // so it can be enabled only when the bug is present. + bool sample_dref_lod_array_as_grad = false; + + // MSL doesn't guarantee coherence between writes and subsequent reads of read_write textures. + // This inserts fences before each read of a read_write texture to ensure coherency. + // If you're sure you never rely on this, you can set this to false for a possible performance improvement. + // Note: Only Apple's GPU compiler takes advantage of the lack of coherency, so make sure to test on Apple GPUs if you disable this. + bool readwrite_texture_fences = true; + + // Metal 3.1 introduced a Metal regression bug which causes infinite recursion during + // Metal's analysis of an entry point input structure that is itself recursive. Enabling + // this option will replace the recursive input declaration with a alternate variable of + // type void*, and then cast to the correct type at the top of the entry point function. + // The bug has been reported to Apple, and will hopefully be fixed in future releases. + bool replace_recursive_inputs = false; + + // If set, manual fixups of gradient vectors for cube texture lookups will be performed. + // All released Apple Silicon GPUs to date behave incorrectly when sampling a cube texture + // with explicit gradients. They will ignore one of the three partial derivatives based + // on the selected major axis, and expect the remaining derivatives to be partially + // transformed. + bool agx_manual_cube_grad_fixup = false; + + // Metal will discard fragments with side effects under certain circumstances prematurely. + // Example: CTS test dEQP-VK.fragment_operations.early_fragment.discard_no_early_fragment_tests_depth + // Test will render a full screen quad with varying depth [0,1] for each fragment. + // Each fragment will do an operation with side effects, modify the depth value and + // discard the fragment. The test expects the fragment to be run due to: + // https://registry.khronos.org/vulkan/specs/1.0-extensions/html/vkspec.html#fragops-shader-depthreplacement + // which states that the fragment shader must be run due to replacing the depth in shader. + // However, Metal may prematurely discards fragments without executing them + // (I believe this to be due to a greedy optimization on their end) making the test fail. + // This option enforces fragment execution for such cases where the fragment has operations + // with side effects. Provided as an option hoping Metal will fix this issue in the future. + bool force_fragment_with_side_effects_execution = false; + + // If set, adds a depth pass through statement to circumvent the following issue: + // When the same depth/stencil is used as input and depth/stencil attachment, we need to + // force Metal to perform the depth/stencil write after fragment execution. Otherwise, + // Metal will write to the depth attachment before fragment execution. This happens + // if the fragment does not modify the depth value. + bool input_attachment_is_ds_attachment = false; + + // If BuiltInPosition is not written, automatically disable rasterization. + // The result can be queried with get_is_rasterization_disabled. + bool auto_disable_rasterization = false; + + // Use Fast Math pragmas in MSL code, based on SPIR-V float controls and FP ExecutionModes. + // Requires MSL 3.2 or above, and has no effect with earlier MSL versions. + bool use_fast_math_pragmas = false; + + bool is_ios() const + { + return platform == iOS; + } + + bool is_macos() const + { + return platform == macOS; + } + + bool use_quadgroup_operation() const + { + return is_ios() && !ios_use_simdgroup_functions; + } + + void set_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) + { + msl_version = make_msl_version(major, minor, patch); + } + + bool supports_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) const + { + return msl_version >= make_msl_version(major, minor, patch); + } + + static uint32_t make_msl_version(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) + { + return (major * 10000) + (minor * 100) + patch; + } + }; + + const Options &get_msl_options() const + { + return msl_options; + } + + void set_msl_options(const Options &opts) + { + msl_options = opts; + } + + // Provide feedback to calling API to allow runtime to disable pipeline + // rasterization if vertex shader requires rasterization to be disabled. + bool get_is_rasterization_disabled() const + { + return is_rasterization_disabled && (get_entry_point().model == ExecutionModelVertex || + get_entry_point().model == ExecutionModelTessellationControl || + get_entry_point().model == ExecutionModelTessellationEvaluation); + } + + // Provide feedback to calling API to allow it to pass an auxiliary + // swizzle buffer if the shader needs it. + bool needs_swizzle_buffer() const + { + return used_swizzle_buffer; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing STORAGE_BUFFER buffer sizes to support OpArrayLength. + bool needs_buffer_size_buffer() const + { + return !buffers_requiring_array_length.empty(); + } + + bool buffer_requires_array_length(VariableID id) const + { + return buffers_requiring_array_length.count(id) != 0; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing the view mask for the current multiview subpass. + bool needs_view_mask_buffer() const + { + return msl_options.multiview && !msl_options.view_index_from_device_index; + } + + // Provide feedback to calling API to allow it to pass a buffer + // containing the dispatch base workgroup ID. + bool needs_dispatch_base_buffer() const + { + return msl_options.dispatch_base && !msl_options.supports_msl_version(1, 2); + } + + // Provide feedback to calling API to allow it to pass an output + // buffer if the shader needs it. + bool needs_output_buffer() const + { + return capture_output_to_buffer && stage_out_var_id != ID(0); + } + + // Provide feedback to calling API to allow it to pass a patch output + // buffer if the shader needs it. + bool needs_patch_output_buffer() const + { + return capture_output_to_buffer && patch_stage_out_var_id != ID(0); + } + + // Provide feedback to calling API to allow it to pass an input threadgroup + // buffer if the shader needs it. + bool needs_input_threadgroup_mem() const + { + return capture_output_to_buffer && stage_in_var_id != ID(0); + } + + explicit CompilerMSL(std::vector spirv); + CompilerMSL(const uint32_t *ir, size_t word_count); + explicit CompilerMSL(const ParsedIR &ir); + explicit CompilerMSL(ParsedIR &&ir); + + // input is a shader interface variable description used to fix up shader input variables. + // If shader inputs are provided, is_msl_shader_input_used() will return true after + // calling ::compile() if the location were used by the MSL code. + void add_msl_shader_input(const MSLShaderInterfaceVariable &input); + + // output is a shader interface variable description used to fix up shader output variables. + // If shader outputs are provided, is_msl_shader_output_used() will return true after + // calling ::compile() if the location were used by the MSL code. + void add_msl_shader_output(const MSLShaderInterfaceVariable &output); + + // resource is a resource binding to indicate the MSL buffer, + // texture or sampler index to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_msl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the MSL code. + void add_msl_resource_binding(const MSLResourceBinding &resource); + + // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource + // in this shader. index is the index within the dynamic offset buffer to use. This + // function marks that resource as using a dynamic offset (VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC + // or VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC). This function only has any effect if argument buffers + // are enabled. If so, the buffer will have its address adjusted at the beginning of the shader with + // an offset taken from the dynamic offset buffer. + void add_dynamic_buffer(uint32_t desc_set, uint32_t binding, uint32_t index); + + // desc_set and binding are the SPIR-V descriptor set and binding of a buffer resource + // in this shader. This function marks that resource as an inline uniform block + // (VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT). This function only has any effect if argument buffers + // are enabled. If so, the buffer block will be directly embedded into the argument + // buffer, instead of being referenced indirectly via pointer. + void add_inline_uniform_block(uint32_t desc_set, uint32_t binding); + + // When using MSL argument buffers, we can force "classic" MSL 1.0 binding schemes for certain descriptor sets. + // This corresponds to VK_KHR_push_descriptor in Vulkan. + void add_discrete_descriptor_set(uint32_t desc_set); + + // If an argument buffer is large enough, it may need to be in the device storage space rather than + // constant. Opt-in to this behavior here on a per set basis. + void set_argument_buffer_device_address_space(uint32_t desc_set, bool device_storage); + + // Query after compilation is done. This allows you to check if an input location was used by the shader. + bool is_msl_shader_input_used(uint32_t location); + + // Query after compilation is done. This allows you to check if an output location were used by the shader. + bool is_msl_shader_output_used(uint32_t location); + + // If not using add_msl_shader_input, it's possible + // that certain builtin attributes need to be automatically assigned locations. + // This is typical for tessellation builtin inputs such as tess levels, gl_Position, etc. + // This returns k_unknown_location if the location was explicitly assigned with + // add_msl_shader_input or the builtin is not used, otherwise returns N in [[attribute(N)]]. + uint32_t get_automatic_builtin_input_location(BuiltIn builtin) const; + + // If not using add_msl_shader_output, it's possible + // that certain builtin attributes need to be automatically assigned locations. + // This is typical for tessellation builtin outputs such as tess levels, gl_Position, etc. + // This returns k_unknown_location if the location were explicitly assigned with + // add_msl_shader_output or the builtin were not used, otherwise returns N in [[attribute(N)]]. + uint32_t get_automatic_builtin_output_location(BuiltIn builtin) const; + + // NOTE: Only resources which are remapped using add_msl_resource_binding will be reported here. + // Constexpr samplers are always assumed to be emitted. + // No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped + // by remap_constexpr_sampler(_by_binding). + bool is_msl_resource_binding_used(ExecutionModel model, uint32_t set, uint32_t binding) const; + + // This must only be called after a successful call to CompilerMSL::compile(). + // For a variable resource ID obtained through reflection API, report the automatically assigned resource index. + // If the descriptor set was part of an argument buffer, report the [[id(N)]], + // or [[buffer/texture/sampler]] binding for other resources. + // If the resource was a combined image sampler, report the image binding here, + // use the _secondary version of this call to query the sampler half of the resource. + // If no binding exists, uint32_t(-1) is returned. + uint32_t get_automatic_msl_resource_binding(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers, in which case the + // sampler's binding is returned instead. For any other resource type, -1 is returned. + // Secondary bindings are also used for the auxillary image atomic buffer. + uint32_t get_automatic_msl_resource_binding_secondary(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for multiplanar images, + // in which case the second plane's binding is returned instead. For any other resource type, -1 is returned. + uint32_t get_automatic_msl_resource_binding_tertiary(uint32_t id) const; + + // Same as get_automatic_msl_resource_binding, but should only be used for combined image samplers for triplanar images, + // in which case the third plane's binding is returned instead. For any other resource type, -1 is returned. + uint32_t get_automatic_msl_resource_binding_quaternary(uint32_t id) const; + + // Compiles the SPIR-V code into Metal Shading Language. + std::string compile() override; + + // Remap a sampler with ID to a constexpr sampler. + // Older iOS targets must use constexpr samplers in certain cases (PCF), + // so a static sampler must be used. + // The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler. + // This can be used on both combined image/samplers (sampler2D) or standalone samplers. + // The remapped sampler must not be an array of samplers. + // Prefer remap_constexpr_sampler_by_binding unless you're also doing reflection anyways. + void remap_constexpr_sampler(VariableID id, const MSLConstexprSampler &sampler); + + // Same as remap_constexpr_sampler, except you provide set/binding, rather than variable ID. + // Remaps based on ID take priority over set/binding remaps. + void remap_constexpr_sampler_by_binding(uint32_t desc_set, uint32_t binding, const MSLConstexprSampler &sampler); + + // If using CompilerMSL::Options::pad_fragment_output_components, override the number of components we expect + // to use for a particular location. The default is 4 if number of components is not overridden. + void set_fragment_output_components(uint32_t location, uint32_t components); + + void set_combined_sampler_suffix(const char *suffix); + const char *get_combined_sampler_suffix() const; + + // Information about specialization constants that are translated into MSL macros + // instead of using function constant + // These must only be called after a successful call to CompilerMSL::compile(). + bool specialization_constant_is_macro(uint32_t constant_id) const; + + // Returns a mask of SPIR-V FP Fast Math Mode flags, that represents the set of flags that can be applied + // across all floating-point types. Each FPFastMathDefault execution mode operation identifies the flags + // for one floating-point type, and the value returned here is a bitwise-AND combination across all types. + // If incl_ops is enabled, the FPFastMathMode of any SPIR-V operations are also included in the bitwise-AND + // to determine the minimal fast-math that applies to all default execution modes and all operations. + // The returned value is also affected by execution modes SignedZeroInfNanPreserve and ContractionOff. + uint32_t get_fp_fast_math_flags(bool incl_ops) const; + +protected: + // An enum of SPIR-V functions that are implemented in additional + // source code that is added to the shader if necessary. + enum SPVFuncImpl : uint8_t + { + SPVFuncImplNone, + SPVFuncImplMod, + SPVFuncImplSMod, + SPVFuncImplRadians, + SPVFuncImplDegrees, + SPVFuncImplFindILsb, + SPVFuncImplFindSMsb, + SPVFuncImplFindUMsb, + SPVFuncImplSSign, + SPVFuncImplArrayCopy, + SPVFuncImplArrayCopyMultidim, + SPVFuncImplArrayCopyExtendedSrc, + SPVFuncImplArrayCopyExtendedDst, + SPVFuncImplTexelBufferCoords, + SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations + SPVFuncImplGradientCube, + SPVFuncImplFMul, + SPVFuncImplFAdd, + SPVFuncImplFSub, + SPVFuncImplQuantizeToF16, + SPVFuncImplCubemapTo2DArrayFace, + SPVFuncImplUnsafeArray, // Allow Metal to use the array template to make arrays a value type + SPVFuncImplStorageMatrix, // Allow threadgroup construction of matrices + SPVFuncImplInverse4x4, + SPVFuncImplInverse3x3, + SPVFuncImplInverse2x2, + // It is very important that this come before *Swizzle, to ensure it's emitted before them. + SPVFuncImplGetSwizzle, + SPVFuncImplTextureSwizzle, + SPVFuncImplGatherReturn, + SPVFuncImplGatherCompareReturn, + SPVFuncImplGatherSwizzle, + SPVFuncImplGatherCompareSwizzle, + SPVFuncImplGatherConstOffsets, + SPVFuncImplGatherCompareConstOffsets, + SPVFuncImplSubgroupBroadcast, + SPVFuncImplSubgroupBroadcastFirst, + SPVFuncImplSubgroupBallot, + SPVFuncImplSubgroupBallotBitExtract, + SPVFuncImplSubgroupBallotFindLSB, + SPVFuncImplSubgroupBallotFindMSB, + SPVFuncImplSubgroupBallotBitCount, + SPVFuncImplSubgroupAllEqual, + SPVFuncImplSubgroupShuffle, + SPVFuncImplSubgroupShuffleXor, + SPVFuncImplSubgroupShuffleUp, + SPVFuncImplSubgroupShuffleDown, + SPVFuncImplSubgroupRotate, + SPVFuncImplSubgroupClusteredAdd, + SPVFuncImplSubgroupClusteredFAdd = SPVFuncImplSubgroupClusteredAdd, + SPVFuncImplSubgroupClusteredIAdd = SPVFuncImplSubgroupClusteredAdd, + SPVFuncImplSubgroupClusteredMul, + SPVFuncImplSubgroupClusteredFMul = SPVFuncImplSubgroupClusteredMul, + SPVFuncImplSubgroupClusteredIMul = SPVFuncImplSubgroupClusteredMul, + SPVFuncImplSubgroupClusteredMin, + SPVFuncImplSubgroupClusteredFMin = SPVFuncImplSubgroupClusteredMin, + SPVFuncImplSubgroupClusteredSMin = SPVFuncImplSubgroupClusteredMin, + SPVFuncImplSubgroupClusteredUMin = SPVFuncImplSubgroupClusteredMin, + SPVFuncImplSubgroupClusteredMax, + SPVFuncImplSubgroupClusteredFMax = SPVFuncImplSubgroupClusteredMax, + SPVFuncImplSubgroupClusteredSMax = SPVFuncImplSubgroupClusteredMax, + SPVFuncImplSubgroupClusteredUMax = SPVFuncImplSubgroupClusteredMax, + SPVFuncImplSubgroupClusteredAnd, + SPVFuncImplSubgroupClusteredBitwiseAnd = SPVFuncImplSubgroupClusteredAnd, + SPVFuncImplSubgroupClusteredLogicalAnd = SPVFuncImplSubgroupClusteredAnd, + SPVFuncImplSubgroupClusteredOr, + SPVFuncImplSubgroupClusteredBitwiseOr = SPVFuncImplSubgroupClusteredOr, + SPVFuncImplSubgroupClusteredLogicalOr = SPVFuncImplSubgroupClusteredOr, + SPVFuncImplSubgroupClusteredXor, + SPVFuncImplSubgroupClusteredBitwiseXor = SPVFuncImplSubgroupClusteredXor, + SPVFuncImplSubgroupClusteredLogicalXor = SPVFuncImplSubgroupClusteredXor, + SPVFuncImplQuadBroadcast, + SPVFuncImplQuadSwap, + SPVFuncImplReflectScalar, + SPVFuncImplRefractScalar, + SPVFuncImplFaceForwardScalar, + SPVFuncImplChromaReconstructNearest2Plane, + SPVFuncImplChromaReconstructNearest3Plane, + SPVFuncImplChromaReconstructLinear422CositedEven2Plane, + SPVFuncImplChromaReconstructLinear422CositedEven3Plane, + SPVFuncImplChromaReconstructLinear422Midpoint2Plane, + SPVFuncImplChromaReconstructLinear422Midpoint3Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven2Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYCositedEven3Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven2Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYCositedEven3Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint2Plane, + SPVFuncImplChromaReconstructLinear420XCositedEvenYMidpoint3Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint2Plane, + SPVFuncImplChromaReconstructLinear420XMidpointYMidpoint3Plane, + SPVFuncImplExpandITUFullRange, + SPVFuncImplExpandITUNarrowRange, + SPVFuncImplConvertYCbCrBT709, + SPVFuncImplConvertYCbCrBT601, + SPVFuncImplConvertYCbCrBT2020, + SPVFuncImplDynamicImageSampler, + SPVFuncImplRayQueryIntersectionParams, + SPVFuncImplVariableDescriptor, + SPVFuncImplVariableSizedDescriptor, + SPVFuncImplVariableDescriptorArray, + SPVFuncImplPaddedStd140, + SPVFuncImplPaddedArrayElement, + SPVFuncImplReduceAdd, + SPVFuncImplImageFence, + SPVFuncImplTextureCast, + SPVFuncImplMulExtended, + SPVFuncImplSetMeshOutputsEXT, + SPVFuncImplAssume, + }; + + // If the underlying resource has been used for comparison then duplicate loads of that resource must be too + // Use Metal's native frame-buffer fetch API for subpass inputs. + void emit_texture_op(const Instruction &i, bool sparse) override; + void emit_binary_ptr_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + std::string to_ptr_expression(uint32_t id, bool register_expression_read = true); + void emit_binary_unord_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op); + void emit_instruction(const Instruction &instr) override; + void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, + uint32_t count) override; + void emit_spv_amd_shader_trinary_minmax_op(uint32_t result_type, uint32_t result_id, uint32_t op, + const uint32_t *args, uint32_t count) override; + void emit_header() override; + void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override; + void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override; + void emit_subgroup_op(const Instruction &i) override; + void emit_subgroup_cluster_op(uint32_t result_type, uint32_t result_id, uint32_t cluster_size, uint32_t op0, + const char *op); + void emit_subgroup_cluster_op_cast(uint32_t result_type, uint32_t result_id, uint32_t cluster_size, uint32_t op0, + const char *op, SPIRType::BaseType input_type, + SPIRType::BaseType expected_result_type); + std::string to_texture_op(const Instruction &i, bool sparse, bool *forward, + SmallVector &inherited_expressions) override; + void emit_fixup() override; + std::string to_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, + const std::string &qualifier = ""); + 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; + std::string type_to_glsl(const SPIRType &type, uint32_t id, bool member); + std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; + void emit_block_hints(const SPIRBlock &block) override; + void emit_mesh_entry_point(); + void emit_mesh_outputs(); + void emit_mesh_tasks(SPIRBlock &block) override; + void emit_workgroup_initialization(const SPIRVariable &var) override; + + // Allow Metal to use the array template to make arrays a value type + std::string type_to_array_glsl(const SPIRType &type, uint32_t variable_id) override; + std::string constant_op_expression(const SPIRConstantOp &cop) override; + + bool variable_decl_is_remapped_storage(const SPIRVariable &variable, StorageClass storage) const override; + + // GCC workaround of lambdas calling protected functions (for older GCC versions) + std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override; + + std::string image_type_glsl(const SPIRType &type, uint32_t id, bool member) override; + std::string sampler_type(const SPIRType &type, uint32_t id, bool member); + std::string builtin_to_glsl(BuiltIn builtin, StorageClass storage) override; + std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override; + std::string to_name(uint32_t id, bool allow_alias = true) const override; + std::string to_function_name(const TextureFunctionNameArguments &args) override; + std::string to_function_args(const TextureFunctionArguments &args, bool *p_forward) override; + std::string to_initializer_expression(const SPIRVariable &var) override; + std::string to_zero_initialized_expression(uint32_t type_id) override; + + std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t physical_type_id, + bool is_packed, bool row_major) override; + + // Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but [[sample_mask]] is a scalar in Metal. + bool builtin_translates_to_nonarray(BuiltIn builtin) const override; + + std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override; + bool emit_complex_bitcast(uint32_t result_id, uint32_t id, uint32_t op0) override; + bool skip_argument(uint32_t id) const override; + std::string to_member_reference(uint32_t base, const SPIRType &type, uint32_t index, bool ptr_chain_is_resolved) override; + std::string to_qualifiers_glsl(uint32_t id) override; + void replace_illegal_names() override; + void declare_constant_arrays(); + + void replace_illegal_entry_point_names(); + void sync_entry_point_aliases_and_names(); + + static const std::unordered_set &get_reserved_keyword_set(); + static const std::unordered_set &get_illegal_func_names(); + + // Constant arrays of non-primitive types (i.e. matrices) won't link properly into Metal libraries + void declare_complex_constant_arrays(); + + bool is_patch_block(const SPIRType &type); + bool is_non_native_row_major_matrix(uint32_t id) override; + bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override; + std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id, + bool is_packed, bool relaxed) override; + + bool is_tesc_shader() const; + bool is_tese_shader() const; + bool is_mesh_shader() const; + + void preprocess_op_codes(); + void localize_global_variables(); + void extract_global_variables_from_functions(); + void mark_packable_structs(); + void mark_as_packable(SPIRType &type); + void mark_as_workgroup_struct(SPIRType &type); + + std::unordered_map> function_global_vars; + void extract_global_variables_from_function(uint32_t func_id, std::set &added_arg_ids, + std::unordered_set &global_var_ids, + std::unordered_set &processed_func_ids); + uint32_t add_interface_block(StorageClass storage, bool patch = false); + uint32_t add_interface_block_pointer(uint32_t ib_var_id, StorageClass storage); + uint32_t add_meshlet_block(bool per_primitive); + + struct InterfaceBlockMeta + { + struct LocationMeta + { + uint32_t base_type_id = 0; + uint32_t num_components = 0; + bool flat = false; + bool noperspective = false; + bool centroid = false; + bool sample = false; + }; + std::unordered_map location_meta; + bool strip_array = false; + bool allow_local_declaration = false; + }; + + std::string to_tesc_invocation_id(); + void emit_local_masked_variable(const SPIRVariable &masked_var, bool strip_array); + void add_variable_to_interface_block(StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, InterfaceBlockMeta &meta); + void add_composite_variable_to_interface_block(StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); + void add_plain_variable_to_interface_block(StorageClass storage, const std::string &ib_var_ref, + SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta); + bool add_component_variable_to_interface_block(StorageClass storage, const std::string &ib_var_ref, + SPIRVariable &var, const SPIRType &type, + InterfaceBlockMeta &meta); + void add_plain_member_variable_to_interface_block(StorageClass storage, + const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, SPIRType &var_type, + uint32_t mbr_idx, InterfaceBlockMeta &meta, + const std::string &mbr_name_qual, + const std::string &var_chain_qual, + uint32_t &location, uint32_t &var_mbr_idx); + void add_composite_member_variable_to_interface_block(StorageClass storage, + const std::string &ib_var_ref, SPIRType &ib_type, + SPIRVariable &var, SPIRType &var_type, + uint32_t mbr_idx, InterfaceBlockMeta &meta, + const std::string &mbr_name_qual, + const std::string &var_chain_qual, + uint32_t &location, uint32_t &var_mbr_idx, + const Bitset &interpolation_qual); + void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var); + void add_tess_level_input(const std::string &base_ref, const std::string &mbr_name, SPIRVariable &var); + + void ensure_struct_members_valid_vecsizes(SPIRType &struct_type, uint32_t &location); + void fix_up_interface_member_indices(StorageClass storage, uint32_t ib_type_id); + + void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, + StorageClass storage, bool fallback = false); + uint32_t ensure_correct_builtin_type(uint32_t type_id, BuiltIn builtin); + uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t component, + uint32_t num_components, bool strip_array); + + void emit_custom_templates(); + void emit_custom_functions(); + void emit_resources(); + void emit_specialization_constants_and_structs(); + void emit_interface_block(uint32_t ib_var_id); + bool maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs); + bool is_var_runtime_size_array(const SPIRVariable &var) const; + uint32_t get_resource_array_size(const SPIRType &type, uint32_t id) const; + + void fix_up_shader_inputs_outputs(); + + bool entry_point_is_vertex() const; + bool entry_point_returns_stage_output() const; + bool entry_point_requires_const_device_buffers() const; + std::string func_type_decl(SPIRType &type); + std::string entry_point_args_classic(bool append_comma); + std::string entry_point_args_argument_buffer(bool append_comma); + std::string entry_point_arg_stage_in(); + void entry_point_args_builtin(std::string &args); + void entry_point_args_discrete_descriptors(std::string &args); + std::string append_member_name(const std::string &qualifier, const SPIRType &type, uint32_t index); + std::string ensure_valid_name(std::string name, std::string pfx); + std::string to_sampler_expression(uint32_t id); + std::string to_swizzle_expression(uint32_t id); + std::string to_buffer_size_expression(uint32_t id); + bool is_sample_rate() const; + bool is_intersection_query() const; + bool is_direct_input_builtin(BuiltIn builtin); + std::string builtin_qualifier(BuiltIn builtin); + std::string builtin_type_decl(BuiltIn builtin, uint32_t id = 0); + std::string built_in_func_arg(BuiltIn builtin, bool prefix_comma); + std::string member_attribute_qualifier(const SPIRType &type, uint32_t index); + std::string member_location_attribute_qualifier(const SPIRType &type, uint32_t index); + std::string argument_decl(const SPIRFunction::Parameter &arg); + const char *descriptor_address_space(uint32_t id, StorageClass storage, const char *plain_address_space) const; + std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp); + uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0); + uint32_t get_member_location(uint32_t type_id, uint32_t index, uint32_t *comp = nullptr) const; + uint32_t get_or_allocate_builtin_input_member_location(BuiltIn builtin, + uint32_t type_id, uint32_t index, uint32_t *comp = nullptr); + uint32_t get_or_allocate_builtin_output_member_location(BuiltIn builtin, + uint32_t type_id, uint32_t index, uint32_t *comp = nullptr); + + uint32_t get_physical_tess_level_array_size(BuiltIn builtin) const; + + uint32_t get_physical_type_id_stride(TypeID type_id) const override; + + // MSL packing rules. These compute the effective packing rules as observed by the MSL compiler in the MSL output. + // These values can change depending on various extended decorations which control packing rules. + // We need to make these rules match up with SPIR-V declared rules. + uint32_t get_declared_type_size_msl(TypeID type_id, const SPIRType *special_type, bool packed, bool row_major) const; + uint32_t get_declared_type_array_stride_msl(TypeID type_id, const SPIRType *special_type, bool packed, bool row_major) const; + uint32_t get_declared_type_matrix_stride_msl(TypeID type_id, const SPIRType *special_type, bool packed, bool row_major) const; + uint32_t get_declared_type_alignment_msl(TypeID type_id, const SPIRType *special_type, bool packed, bool row_major) const; + + uint32_t get_declared_struct_member_size_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_struct_member_alignment_msl(const SPIRType &struct_type, uint32_t index) const; + + uint32_t get_declared_input_size_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_array_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_matrix_stride_msl(const SPIRType &struct_type, uint32_t index) const; + uint32_t get_declared_input_alignment_msl(const SPIRType &struct_type, uint32_t index) const; + + TypeID get_physical_member_type_id(const SPIRType &struct_type, uint32_t index) const; + SPIRType get_presumed_input_type(const SPIRType &struct_type, uint32_t index) const; + + uint32_t get_declared_struct_size_msl(const SPIRType &struct_type) const; + + std::string to_component_argument(uint32_t id); + void align_struct(SPIRType &ib_type, std::unordered_set &aligned_structs); + void mark_scalar_layout_structs(const SPIRType &ib_type); + void mark_struct_members_packed(const SPIRType &type); + void ensure_member_packing_rules_msl(SPIRType &ib_type, uint32_t index); + bool validate_member_packing_rules_msl(const SPIRType &type, uint32_t index) const; + std::string get_variable_address_space(const SPIRVariable &argument); + // Special case of get_variable_address_space which is only used for leaf functions. + std::string get_leaf_argument_address_space(const SPIRVariable &argument); + std::string get_type_address_space(const SPIRType &type, uint32_t id, bool argument = false); + bool decoration_flags_signal_volatile(const Bitset &flags) const; + bool decoration_flags_signal_coherent(const Bitset &flags) const; + const char *to_restrict(uint32_t id, bool space); + SPIRType &get_stage_in_struct_type(); + SPIRType &get_stage_out_struct_type(); + SPIRType &get_patch_stage_in_struct_type(); + SPIRType &get_patch_stage_out_struct_type(); + std::string get_tess_factor_struct_name(); + SPIRType &get_uint_type(); + uint32_t get_uint_type_id(); + void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, const char *op, Op opcode, + uint32_t mem_order_1, uint32_t mem_order_2, bool has_mem_order_2, uint32_t op0, uint32_t op1 = 0, + bool op1_is_pointer = false, bool op1_is_literal = false, uint32_t op2 = 0); + const char *get_memory_order(uint32_t spv_mem_sem); + void add_pragma_line(const std::string &line, bool recompile_on_unique); + void add_typedef_line(const std::string &line); + void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem); + bool emit_array_copy(const char *expr, uint32_t lhs_id, uint32_t rhs_id, + StorageClass lhs_storage, StorageClass rhs_storage) override; + void build_implicit_builtins(); + uint32_t build_constant_uint_array_pointer(); + void emit_entry_point_declarations() override; + bool uses_explicit_early_fragment_test(); + + uint32_t builtin_frag_coord_id = 0; + uint32_t builtin_sample_id_id = 0; + uint32_t builtin_sample_mask_id = 0; + uint32_t builtin_helper_invocation_id = 0; + uint32_t builtin_vertex_idx_id = 0; + uint32_t builtin_base_vertex_id = 0; + uint32_t builtin_instance_idx_id = 0; + uint32_t builtin_base_instance_id = 0; + uint32_t builtin_view_idx_id = 0; + uint32_t builtin_layer_id = 0; + uint32_t builtin_invocation_id_id = 0; + uint32_t builtin_primitive_id_id = 0; + uint32_t builtin_subgroup_invocation_id_id = 0; + uint32_t builtin_subgroup_size_id = 0; + uint32_t builtin_dispatch_base_id = 0; + uint32_t builtin_stage_input_size_id = 0; + uint32_t builtin_local_invocation_index_id = 0; + uint32_t builtin_workgroup_size_id = 0; + uint32_t builtin_mesh_primitive_indices_id = 0; + uint32_t builtin_mesh_sizes_id = 0; + uint32_t builtin_task_grid_id = 0; + uint32_t builtin_frag_depth_id = 0; + uint32_t swizzle_buffer_id = 0; + uint32_t buffer_size_buffer_id = 0; + uint32_t view_mask_buffer_id = 0; + uint32_t dynamic_offsets_buffer_id = 0; + uint32_t uint_type_id = 0; + uint32_t shared_uint_type_id = 0; + uint32_t meshlet_type_id = 0; + uint32_t argument_buffer_padding_buffer_type_id = 0; + uint32_t argument_buffer_padding_image_type_id = 0; + uint32_t argument_buffer_padding_sampler_type_id = 0; + + bool does_shader_write_sample_mask = false; + bool frag_shader_needs_discard_checks = false; + + void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override; + void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override; + void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override; + + void analyze_sampled_image_usage(); + void analyze_workgroup_variables(); + + bool access_chain_needs_stage_io_builtin_translation(uint32_t base) override; + bool prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, StorageClass storage, + bool &is_packed) override; + void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length); + bool check_physical_type_cast(std::string &expr, const SPIRType *type, uint32_t physical_type) override; + + bool emit_tessellation_access_chain(const uint32_t *ops, uint32_t length); + bool emit_tessellation_io_load(uint32_t result_type, uint32_t id, uint32_t ptr); + bool is_out_of_bounds_tessellation_level(uint32_t id_lhs); + + void ensure_builtin(StorageClass storage, BuiltIn builtin); + + void mark_implicit_builtin(StorageClass storage, BuiltIn builtin, uint32_t id); + + std::string convert_to_f32(const std::string &expr, uint32_t components); + + Options msl_options; + std::set spv_function_implementations; + // Must be ordered to ensure declarations are in a specific order. + std::map inputs_by_location; + std::unordered_map inputs_by_builtin; + std::map outputs_by_location; + std::unordered_map outputs_by_builtin; + std::unordered_set location_inputs_in_use; + std::unordered_set location_inputs_in_use_fallback; + std::unordered_set location_outputs_in_use; + std::unordered_set location_outputs_in_use_fallback; + std::unordered_map fragment_output_components; + std::unordered_map builtin_to_automatic_input_location; + std::unordered_map builtin_to_automatic_output_location; + std::vector pragma_lines; + std::vector typedef_lines; + SmallVector vars_needing_early_declaration; + std::unordered_set constant_macro_ids; + + std::unordered_map, InternalHasher> resource_bindings; + std::unordered_map resource_arg_buff_idx_to_binding_number; + + uint32_t next_metal_resource_index_buffer = 0; + uint32_t next_metal_resource_index_texture = 0; + uint32_t next_metal_resource_index_sampler = 0; + // Intentionally uninitialized, works around MSVC 2013 bug. + uint32_t next_metal_resource_ids[kMaxArgumentBuffers]; + + VariableID stage_in_var_id = 0; + VariableID stage_out_var_id = 0; + VariableID patch_stage_in_var_id = 0; + VariableID patch_stage_out_var_id = 0; + VariableID stage_in_ptr_var_id = 0; + VariableID stage_out_ptr_var_id = 0; + VariableID tess_level_inner_var_id = 0; + VariableID tess_level_outer_var_id = 0; + VariableID mesh_out_per_vertex = 0; + VariableID mesh_out_per_primitive = 0; + VariableID stage_out_masked_builtin_type_id = 0; + + // Handle HLSL-style 0-based vertex/instance index. + enum class TriState + { + Neutral, + No, + Yes + }; + TriState needs_base_vertex_arg = TriState::Neutral; + TriState needs_base_instance_arg = TriState::Neutral; + + bool has_sampled_images = false; + bool builtin_declaration = false; // Handle HLSL-style 0-based vertex/instance index. + + bool is_using_builtin_array = false; // Force the use of C style array declaration. + bool using_builtin_array() const; + + bool is_rasterization_disabled = false; + bool has_descriptor_side_effects_buffer = false; + bool capture_output_to_buffer = false; + bool needs_swizzle_buffer_def = false; + bool used_swizzle_buffer = false; + bool added_builtin_tess_level = false; + bool needs_local_invocation_index = false; + bool needs_subgroup_invocation_id = false; + bool needs_subgroup_size = false; + bool needs_sample_id = false; + bool needs_helper_invocation = false; + bool needs_workgroup_zero_init = false; + bool needs_point_size_output = false; + bool writes_to_depth = false; + bool writes_to_point_size = false; + std::string qual_pos_var_name; + std::string stage_in_var_name = "in"; + std::string stage_out_var_name = "out"; + std::string patch_stage_in_var_name = "patchIn"; + std::string patch_stage_out_var_name = "patchOut"; + std::string sampler_name_suffix = "Smplr"; + std::string swizzle_name_suffix = "Swzl"; + std::string buffer_size_name_suffix = "BufferSize"; + std::string plane_name_suffix = "Plane"; + std::string input_wg_var_name = "gl_in"; + std::string input_buffer_var_name = "spvIn"; + std::string output_buffer_var_name = "spvOut"; + std::string patch_input_buffer_var_name = "spvPatchIn"; + std::string patch_output_buffer_var_name = "spvPatchOut"; + std::string tess_factor_buffer_var_name = "spvTessLevel"; + std::string index_buffer_var_name = "spvIndices"; + Op previous_instruction_opcode = OpNop; + + // Must be ordered since declaration is in a specific order. + std::map constexpr_samplers_by_id; + std::unordered_map constexpr_samplers_by_binding; + const MSLConstexprSampler *find_constexpr_sampler(uint32_t id) const; + + std::unordered_set buffers_requiring_array_length; + SmallVector buffer_aliases_discrete; + std::unordered_set atomic_image_vars_emulated; // Emulate texture2D atomic operations + std::unordered_set pull_model_inputs; + std::unordered_set recursive_inputs; + + SmallVector entry_point_bindings; + + // Must be ordered since array is in a specific order. + struct DynamicBuffer + { + uint32_t base_index; + uint32_t var_id; + std::string mbr_name; + }; + std::map buffers_requiring_dynamic_offset; + + SmallVector disabled_frag_outputs; + + std::unordered_set inline_uniform_blocks; + + uint32_t argument_buffer_ids[kMaxArgumentBuffers]; + uint32_t argument_buffer_discrete_mask = 0; + uint32_t argument_buffer_device_storage_mask = 0; + + void emit_argument_buffer_aliased_descriptor(const SPIRVariable &aliased_var, + const SPIRVariable &base_var); + + void analyze_argument_buffers(); + bool descriptor_set_is_argument_buffer(uint32_t desc_set) const; + const MSLResourceBinding &get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx) const; + void add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); + void add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); + void add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind); + void add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, uint32_t count); + + uint32_t get_target_components_for_fragment_location(uint32_t location) const; + uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components, + SPIRType::BaseType basetype = SPIRType::Unknown); + uint32_t build_msl_interpolant_type(uint32_t type_id, bool is_noperspective); + + bool suppress_missing_prototypes = false; + bool suppress_incompatible_pointer_types_discard_qualifiers = false; + bool suppress_sometimes_unitialized = false; + + void add_spv_func_and_recompile(SPVFuncImpl spv_func); + + void activate_argument_buffer_resources(); + + bool type_is_msl_framebuffer_fetch(const SPIRType &type) const; + bool is_supported_argument_buffer_type(const SPIRType &type) const; + + bool variable_storage_requires_stage_io(StorageClass storage) const; + + bool needs_manual_helper_invocation_updates() const + { + return msl_options.manual_helper_invocation_updates && msl_options.supports_msl_version(2, 3); + } + bool needs_frag_discard_checks() const + { + return get_execution_model() == ExecutionModelFragment && msl_options.supports_msl_version(2, 3) && + msl_options.check_discarded_frag_stores && frag_shader_needs_discard_checks; + } + + bool has_additional_fixed_sample_mask() const { return msl_options.additional_fixed_sample_mask != 0xffffffff; } + std::string additional_fixed_sample_mask_str() const; + + // OpcodeHandler that handles several MSL preprocessing operations. + struct OpCodePreprocessor : OpcodeHandler + { + explicit OpCodePreprocessor(CompilerMSL &compiler_) + : OpcodeHandler(compiler_), self(compiler_) + { + enable_result_types = true; + } + + bool handle(Op opcode, const uint32_t *args, uint32_t length) override; + CompilerMSL::SPVFuncImpl get_spv_func_impl(Op opcode, const uint32_t *args, uint32_t length); + void check_resource_write(uint32_t var_id); + + CompilerMSL &self; + std::unordered_map image_pointers_emulated; // Emulate texture2D atomic operations + bool suppress_missing_prototypes = false; + bool uses_atomics = false; + bool uses_image_write = false; + bool uses_buffer_write = false; + bool uses_discard = false; + bool needs_local_invocation_index = false; + bool needs_subgroup_invocation_id = false; + bool needs_subgroup_size = false; + bool needs_sample_id = false; + bool needs_helper_invocation = false; + bool uses_cooperative_matrix = false; + }; + + // OpcodeHandler that scans for uses of sampled images + struct SampledImageScanner : OpcodeHandler + { + explicit SampledImageScanner(CompilerMSL &compiler_) + : OpcodeHandler(compiler_), self(compiler_) + { + } + + CompilerMSL &self; + bool handle(Op opcode, const uint32_t *args, uint32_t) override; + }; + + // Sorts the members of a SPIRType and associated Meta info based on a settable sorting + // aspect, which defines which aspect of the struct members will be used to sort them. + // Regardless of the sorting aspect, built-in members always appear at the end of the struct. + struct MemberSorter + { + enum SortAspect + { + LocationThenBuiltInType, + Offset + }; + + void sort(); + bool operator()(uint32_t mbr_idx1, uint32_t mbr_idx2); + MemberSorter(SPIRType &t, Meta &m, SortAspect sa); + + SPIRType &type; + Meta &meta; + SortAspect sort_aspect; + }; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_parser.cpp b/thirdparty/SPIRV-Cross/spirv_parser.cpp new file mode 100644 index 000000000..ba49db1fb --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_parser.cpp @@ -0,0 +1,1610 @@ +/* + * Copyright 2018-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_parser.hpp" +#include "NonSemanticShaderDebugInfo100.h" +#include + +using namespace std; +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; + +namespace SPIRV_CROSS_NAMESPACE +{ +Parser::Parser(vector spirv) +{ + ir.spirv = std::move(spirv); +} + +Parser::Parser(const uint32_t *spirv_data, size_t word_count) +{ + ir.spirv = vector(spirv_data, spirv_data + word_count); +} + +static bool decoration_is_string(Decoration decoration) +{ + switch (decoration) + { + case DecorationUserSemantic: + return true; + + default: + return false; + } +} + +static inline uint32_t swap_endian(uint32_t v) +{ + return ((v >> 24) & 0x000000ffu) | ((v >> 8) & 0x0000ff00u) | ((v << 8) & 0x00ff0000u) | ((v << 24) & 0xff000000u); +} + +static bool is_valid_spirv_version(uint32_t version) +{ + switch (version) + { + // Allow v99 since it tends to just work. + case 99: + case 0x10000: // SPIR-V 1.0 + case 0x10100: // SPIR-V 1.1 + case 0x10200: // SPIR-V 1.2 + case 0x10300: // SPIR-V 1.3 + case 0x10400: // SPIR-V 1.4 + case 0x10500: // SPIR-V 1.5 + case 0x10600: // SPIR-V 1.6 + return true; + + default: + return false; + } +} + +void Parser::parse() +{ + auto &spirv = ir.spirv; + + auto len = spirv.size(); + if (len < 5) + SPIRV_CROSS_THROW("SPIRV file too small."); + + auto s = spirv.data(); + + // Endian-swap if we need to. + if (s[0] == swap_endian(MagicNumber)) + transform(begin(spirv), end(spirv), begin(spirv), [](uint32_t c) { return swap_endian(c); }); + + if (s[0] != MagicNumber || !is_valid_spirv_version(s[1])) + SPIRV_CROSS_THROW("Invalid SPIRV format."); + + uint32_t bound = s[3]; + + const uint32_t MaximumNumberOfIDs = 0x3fffff; + if (bound > MaximumNumberOfIDs) + SPIRV_CROSS_THROW("ID bound exceeds limit of 0x3fffff.\n"); + + ir.set_id_bounds(bound); + + uint32_t offset = 5; + + SmallVector instructions; + while (offset < len) + { + Instruction instr = {}; + instr.op = spirv[offset] & 0xffff; + instr.count = (spirv[offset] >> 16) & 0xffff; + + if (instr.count == 0) + SPIRV_CROSS_THROW("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file."); + + instr.offset = offset + 1; + instr.length = instr.count - 1; + + offset += instr.count; + + if (offset > spirv.size()) + SPIRV_CROSS_THROW("SPIR-V instruction goes out of bounds."); + + instructions.push_back(instr); + } + + for (auto &i : instructions) + parse(i); + + for (auto &fixup : forward_pointer_fixups) + { + auto &target = get(fixup.first); + auto &source = get(fixup.second); + target.member_types = source.member_types; + target.basetype = source.basetype; + target.self = source.self; + } + forward_pointer_fixups.clear(); + + for (auto &source : ir.sources) + { + auto cmp = [](const ParsedIR::Source::Marker &a, const ParsedIR::Source::Marker &b) { + return a.line < b.line; + }; + + std::sort(source.line_markers.begin(), source.line_markers.end(), cmp); + } + + if (current_function) + SPIRV_CROSS_THROW("Function was not terminated."); + if (current_block) + SPIRV_CROSS_THROW("Block was not terminated."); + if (ir.default_entry_point == 0) + SPIRV_CROSS_THROW("There is no entry point in the SPIR-V module."); +} + +const uint32_t *Parser::stream(const Instruction &instr) const +{ + // If we're not going to use any arguments, just return nullptr. + // We want to avoid case where we return an out of range pointer + // that trips debug assertions on some platforms. + if (!instr.length) + return nullptr; + + if (instr.offset + instr.length > ir.spirv.size()) + SPIRV_CROSS_THROW("Compiler::stream() out of range."); + return &ir.spirv[instr.offset]; +} + +static string extract_string(const vector &spirv, uint32_t offset) +{ + string ret; + for (uint32_t i = offset; i < spirv.size(); i++) + { + uint32_t w = spirv[i]; + + for (uint32_t j = 0; j < 4; j++, w >>= 8) + { + char c = w & 0xff; + if (c == '\0') + return ret; + ret += c; + } + } + + SPIRV_CROSS_THROW("String was not terminated before EOF"); +} + +void Parser::parse(const Instruction &instruction) +{ + auto *ops = stream(instruction); + auto op = static_cast(instruction.op); + uint32_t length = instruction.length; + + // HACK for glslang that might emit OpEmitMeshTasksEXT followed by return / branch. + // Instead of failing hard, just ignore it. + if (ignore_trailing_block_opcodes) + { + ignore_trailing_block_opcodes = false; + if (op == OpReturn || op == OpBranch || op == OpUnreachable) + return; + } + + switch (op) + { + case OpSourceExtension: + case OpNop: + case OpModuleProcessed: + break; + + case OpString: + { + set(ops[0], extract_string(ir.spirv, instruction.offset + 1)); + break; + } + + case OpMemoryModel: + ir.addressing_model = static_cast(ops[0]); + ir.memory_model = static_cast(ops[1]); + break; + + case OpSource: + { + ir.sources.emplace_back(); + auto &source = ir.sources.back(); + source.lang = static_cast(ops[0]); + + switch (source.lang) + { + case SourceLanguageESSL: + source.es = true; + source.version = ops[1]; + source.known = true; + source.hlsl = false; + break; + + case SourceLanguageGLSL: + source.es = false; + source.version = ops[1]; + source.known = true; + source.hlsl = false; + break; + + case SourceLanguageHLSL: + // For purposes of cross-compiling, this is GLSL 450. + source.es = false; + source.version = 450; + source.known = true; + source.hlsl = true; + break; + + default: + source.known = false; + break; + } + + if (length >= 3) + source.file_id = ops[2]; + + if (length >= 4) + source.source = extract_string(ir.spirv, instruction.offset + 3); + + break; + } + + case OpSourceContinued: + if (!ir.sources.empty()) + ir.sources.back().source += extract_string(ir.spirv, instruction.offset); + break; + + case OpUndef: + { + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + set(id, result_type); + if (current_block) + current_block->ops.push_back(instruction); + break; + } + + case OpCapability: + { + uint32_t cap = ops[0]; + if (cap == CapabilityKernel) + SPIRV_CROSS_THROW("Kernel capability not supported."); + + ir.declared_capabilities.push_back(static_cast(ops[0])); + break; + } + + case OpExtension: + { + auto ext = extract_string(ir.spirv, instruction.offset); + ir.declared_extensions.push_back(std::move(ext)); + break; + } + + case OpExtInstImport: + { + uint32_t id = ops[0]; + + SPIRExtension::Extension spirv_ext = SPIRExtension::Unsupported; + + auto ext = extract_string(ir.spirv, instruction.offset + 1); + if (ext == "GLSL.std.450") + spirv_ext = SPIRExtension::GLSL; + else if (ext == "DebugInfo") + spirv_ext = SPIRExtension::SPV_debug_info; + else if (ext == "SPV_AMD_shader_ballot") + spirv_ext = SPIRExtension::SPV_AMD_shader_ballot; + else if (ext == "SPV_AMD_shader_explicit_vertex_parameter") + spirv_ext = SPIRExtension::SPV_AMD_shader_explicit_vertex_parameter; + else if (ext == "SPV_AMD_shader_trinary_minmax") + spirv_ext = SPIRExtension::SPV_AMD_shader_trinary_minmax; + else if (ext == "SPV_AMD_gcn_shader") + spirv_ext = SPIRExtension::SPV_AMD_gcn_shader; + else if (ext == "NonSemantic.DebugPrintf") + spirv_ext = SPIRExtension::NonSemanticDebugPrintf; + else if (ext == "NonSemantic.Shader.DebugInfo.100") + spirv_ext = SPIRExtension::NonSemanticShaderDebugInfo; + else if (ext.find("NonSemantic.") == 0) + spirv_ext = SPIRExtension::NonSemanticGeneric; + + set(id, spirv_ext); + // Other SPIR-V extensions which have ExtInstrs are currently not supported. + + break; + } + + case OpExtInst: + case OpExtInstWithForwardRefsKHR: + { + // The SPIR-V debug information extended instructions might come at global scope. + if (current_block) + { + current_block->ops.push_back(instruction); + if (length >= 2) + { + const auto *type = maybe_get(ops[0]); + if (type) + ir.load_type_width.insert({ ops[1], type->width }); + } + } + + if (op == OpExtInst && length > 4) + { + // Don't want to deal with ForwardRefs here. + auto &ext = get(ops[2]); + if (ext.ext == SPIRExtension::NonSemanticShaderDebugInfo) + { + const auto instr = ops[3]; + if (instr == NonSemanticShaderDebugInfo100DebugSource) + { + set(ops[1], get(ops[4]).str); + + ir.sources.emplace_back(); + auto &source = ir.sources.back(); + source.file_id = ops[4]; + source.define_id = ops[1]; + if (length >= 6) + source.source = ir.get(ops[5]).str; + } + else if (instr == NonSemanticShaderDebugInfo100DebugSourceContinued) + { + if (length < 5) + SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugSourceContinued"); + if (!ir.sources.empty()) + ir.sources.back().source += ir.get(ops[4]).str; + } + else if (instr == NonSemanticShaderDebugInfo100DebugLine) + { + if (length < 9) + SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugLine"); + auto source_id = ops[4]; + auto line_start = ir.get(ops[5]).scalar_i32(); + auto col_start = ir.get(ops[7]).scalar_i32(); + + for (auto &source : ir.sources) + { + if (source.define_id != source_id) + continue; + + source.line_markers.emplace_back(); + auto &marker = source.line_markers.back(); + marker.line = line_start; + marker.col = col_start; + marker.offset = instruction.offset - 1; + marker.function_id = current_function ? current_function->self : ID(0); + marker.block_id = current_block ? current_block->self : ID(0); + break; + } + } + else if (instr == NonSemanticShaderDebugInfo100DebugLocalVariable) + { + if (length < 11) + SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugLocalVariable"); + auto &lvar = set(ops[1]); + lvar.name_id = ops[4]; + } + else if (instr == NonSemanticShaderDebugInfo100DebugDeclare) + { + if (length < 7) + SPIRV_CROSS_THROW("Invalid arguments for ShaderDebugInfo100DebugDeclare"); + auto &lvar = get(ops[4]); + auto &var = get(ops[5]); + var.debug_local_variables.push_back(lvar.self); + } + } + } + break; + } + + case OpEntryPoint: + { + auto itr = + ir.entry_points.insert(make_pair(ops[1], SPIREntryPoint(ops[1], static_cast(ops[0]), + extract_string(ir.spirv, instruction.offset + 2)))); + auto &e = itr.first->second; + + // Strings need nul-terminator and consume the whole word. + uint32_t strlen_words = uint32_t((e.name.size() + 1 + 3) >> 2); + + for (uint32_t i = strlen_words + 2; i < instruction.length; i++) + e.interface_variables.push_back(ops[i]); + + // Set the name of the entry point in case OpName is not provided later. + ir.set_name(ops[1], e.name); + + // If we don't have an entry, make the first one our "default". + if (!ir.default_entry_point) + ir.default_entry_point = ops[1]; + break; + } + + case OpExecutionMode: + { + auto &execution = ir.entry_points[ops[0]]; + auto mode = static_cast(ops[1]); + execution.flags.set(mode); + + switch (mode) + { + case ExecutionModeInvocations: + execution.invocations = ops[2]; + break; + + case ExecutionModeLocalSize: + execution.workgroup_size.x = ops[2]; + execution.workgroup_size.y = ops[3]; + execution.workgroup_size.z = ops[4]; + break; + + case ExecutionModeOutputVertices: + execution.output_vertices = ops[2]; + break; + + case ExecutionModeOutputPrimitivesEXT: + execution.output_primitives = ops[2]; + break; + + case ExecutionModeSignedZeroInfNanPreserve: + switch (ops[2]) + { + case 8: + execution.signed_zero_inf_nan_preserve_8 = true; + break; + + case 16: + execution.signed_zero_inf_nan_preserve_16 = true; + break; + + case 32: + execution.signed_zero_inf_nan_preserve_32 = true; + break; + + case 64: + execution.signed_zero_inf_nan_preserve_64 = true; + break; + + default: + SPIRV_CROSS_THROW("Invalid bit-width for SignedZeroInfNanPreserve."); + } + break; + + default: + break; + } + break; + } + + case OpExecutionModeId: + { + auto &execution = ir.entry_points[ops[0]]; + auto mode = static_cast(ops[1]); + execution.flags.set(mode); + + switch (mode) + { + case ExecutionModeLocalSizeId: + execution.workgroup_size.id_x = ops[2]; + execution.workgroup_size.id_y = ops[3]; + execution.workgroup_size.id_z = ops[4]; + break; + + case ExecutionModeFPFastMathDefault: + execution.fp_fast_math_defaults[ops[2]] = ops[3]; + break; + + default: + break; + } + break; + } + + case OpName: + { + uint32_t id = ops[0]; + ir.set_name(id, extract_string(ir.spirv, instruction.offset + 1)); + break; + } + + case OpMemberName: + { + uint32_t id = ops[0]; + uint32_t member = ops[1]; + ir.set_member_name(id, member, extract_string(ir.spirv, instruction.offset + 2)); + break; + } + + case OpDecorationGroup: + { + // Noop, this simply means an ID should be a collector of decorations. + // The meta array is already a flat array of decorations which will contain the relevant decorations. + break; + } + + case OpGroupDecorate: + { + uint32_t group_id = ops[0]; + auto &decorations = ir.meta[group_id].decoration; + auto &flags = decorations.decoration_flags; + + // Copies decorations from one ID to another. Only copy decorations which are set in the group, + // i.e., we cannot just copy the meta structure directly. + for (uint32_t i = 1; i < length; i++) + { + uint32_t target = ops[i]; + flags.for_each_bit([&](uint32_t bit) { + auto decoration = static_cast(bit); + + if (decoration_is_string(decoration)) + { + ir.set_decoration_string(target, decoration, ir.get_decoration_string(group_id, decoration)); + } + else + { + ir.meta[target].decoration_word_offset[decoration] = + ir.meta[group_id].decoration_word_offset[decoration]; + ir.set_decoration(target, decoration, ir.get_decoration(group_id, decoration)); + } + }); + } + break; + } + + case OpGroupMemberDecorate: + { + uint32_t group_id = ops[0]; + auto &flags = ir.meta[group_id].decoration.decoration_flags; + + // Copies decorations from one ID to another. Only copy decorations which are set in the group, + // i.e., we cannot just copy the meta structure directly. + for (uint32_t i = 1; i + 1 < length; i += 2) + { + uint32_t target = ops[i + 0]; + uint32_t index = ops[i + 1]; + flags.for_each_bit([&](uint32_t bit) { + auto decoration = static_cast(bit); + + if (decoration_is_string(decoration)) + ir.set_member_decoration_string(target, index, decoration, + ir.get_decoration_string(group_id, decoration)); + else + ir.set_member_decoration(target, index, decoration, ir.get_decoration(group_id, decoration)); + }); + } + break; + } + + case OpDecorate: + case OpDecorateId: + { + // OpDecorateId technically supports an array of arguments, but our only supported decorations are single uint, + // so merge decorate and decorate-id here. + uint32_t id = ops[0]; + + auto decoration = static_cast(ops[1]); + if (length >= 3) + { + ir.meta[id].decoration_word_offset[decoration] = uint32_t(&ops[2] - ir.spirv.data()); + ir.set_decoration(id, decoration, ops[2]); + } + else + ir.set_decoration(id, decoration); + + break; + } + + case OpDecorateStringGOOGLE: + { + uint32_t id = ops[0]; + auto decoration = static_cast(ops[1]); + ir.set_decoration_string(id, decoration, extract_string(ir.spirv, instruction.offset + 2)); + break; + } + + case OpMemberDecorate: + { + uint32_t id = ops[0]; + uint32_t member = ops[1]; + auto decoration = static_cast(ops[2]); + if (length >= 4) + ir.set_member_decoration(id, member, decoration, ops[3]); + else + ir.set_member_decoration(id, member, decoration); + break; + } + + // MemberDecorateIdEXT only applies to OffsetIdEXT when descriptors are packed in structs. + // This is currently unsupported and will fail in compilation. + // Pass it through in case someone just needs reflection. + case OpMemberDecorateIdEXT: + break; + + case OpMemberDecorateStringGOOGLE: + { + uint32_t id = ops[0]; + uint32_t member = ops[1]; + auto decoration = static_cast(ops[2]); + ir.set_member_decoration_string(id, member, decoration, extract_string(ir.spirv, instruction.offset + 3)); + break; + } + + // Build up basic types. + case OpTypeVoid: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::Void; + break; + } + + case OpTypeBool: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::Boolean; + type.width = 1; + break; + } + + case OpTypeFloat: + { + uint32_t id = ops[0]; + uint32_t width = ops[1]; + auto &type = set(id, op); + + if (width != 16 && width != 8 && length > 2) + SPIRV_CROSS_THROW("Unrecognized FP encoding mode for OpTypeFloat."); + + if (width == 64) + type.basetype = SPIRType::Double; + else if (width == 32) + type.basetype = SPIRType::Float; + else if (width == 16) + { + if (length > 2) + { + if (ops[2] == FPEncodingBFloat16KHR) + type.basetype = SPIRType::BFloat16; + else + SPIRV_CROSS_THROW("Unrecognized encoding for OpTypeFloat 16."); + } + else + type.basetype = SPIRType::Half; + } + else if (width == 8) + { + if (length < 2) + SPIRV_CROSS_THROW("Missing encoding for OpTypeFloat 8."); + else if (ops[2] == FPEncodingFloat8E4M3EXT) + type.basetype = SPIRType::FloatE4M3; + else if (ops[2] == FPEncodingFloat8E5M2EXT) + type.basetype = SPIRType::FloatE5M2; + else + SPIRV_CROSS_THROW("Invalid encoding for OpTypeFloat 8."); + } + else + SPIRV_CROSS_THROW("Unrecognized bit-width of floating point type."); + type.width = width; + break; + } + + case OpTypeInt: + { + uint32_t id = ops[0]; + uint32_t width = ops[1]; + bool signedness = ops[2] != 0; + auto &type = set(id, op); + type.basetype = signedness ? to_signed_basetype(width) : to_unsigned_basetype(width); + type.width = width; + break; + } + + // Build composite types by "inheriting". + // NOTE: The self member is also copied! For pointers and array modifiers this is a good thing + // since we can refer to decorations on pointee classes which is needed for UBO/SSBO, I/O blocks in geometry/tess etc. + case OpTypeVector: + { + uint32_t id = ops[0]; + uint32_t vecsize = ops[2]; + + auto &base = get(ops[1]); + auto &vecbase = set(id, base); + + vecbase.op = op; + vecbase.vecsize = vecsize; + vecbase.self = id; + vecbase.parent_type = ops[1]; + break; + } + + case OpTypeMatrix: + { + uint32_t id = ops[0]; + uint32_t colcount = ops[2]; + + auto &base = get(ops[1]); + auto &matrixbase = set(id, base); + + matrixbase.op = op; + matrixbase.columns = colcount; + matrixbase.self = id; + matrixbase.parent_type = ops[1]; + break; + } + + case OpTypeCooperativeMatrixKHR: + { + uint32_t id = ops[0]; + auto &base = get(ops[1]); + auto &matrixbase = set(id, base); + + matrixbase.op = op; + matrixbase.ext.cooperative.scope_id = ops[2]; + matrixbase.ext.cooperative.rows_id = ops[3]; + matrixbase.ext.cooperative.columns_id = ops[4]; + matrixbase.ext.cooperative.use_id = ops[5]; + matrixbase.self = id; + matrixbase.parent_type = ops[1]; + break; + } + + case OpTypeCooperativeVectorNV: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + + type.basetype = SPIRType::CoopVecNV; + type.op = op; + type.ext.coopVecNV.component_type_id = ops[1]; + type.ext.coopVecNV.component_count_id = ops[2]; + type.parent_type = ops[1]; + + // CoopVec-Nv can be used with integer operations like SMax where + // where spirv-opt does explicit checks on integer bitwidth + auto component_type = get(type.ext.coopVecNV.component_type_id); + type.width = component_type.width; + break; + } + + case OpTypeArray: + { + uint32_t id = ops[0]; + uint32_t tid = ops[1]; + auto &base = get(tid); + auto &arraybase = set(id, base); + + arraybase.op = op; + arraybase.parent_type = tid; + + uint32_t cid = ops[2]; + ir.mark_used_as_array_length(cid); + auto *c = maybe_get(cid); + bool literal = c && !c->specialization; + + // We're copying type information into Array types, so we'll need a fixup for any physical pointer + // references. + if (base.forward_pointer) + forward_pointer_fixups.push_back({ id, tid }); + + arraybase.array_size_literal.push_back(literal); + arraybase.array.push_back(literal ? c->scalar() : cid); + + // .self resolves down to non-array/non-pointer type. + arraybase.self = base.self; + break; + } + + case OpTypeRuntimeArray: + { + uint32_t id = ops[0]; + + auto &base = get(ops[1]); + auto &arraybase = set(id, base); + + // We're copying type information into Array types, so we'll need a fixup for any physical pointer + // references. + if (base.forward_pointer) + forward_pointer_fixups.push_back({ id, ops[1] }); + + arraybase.op = op; + arraybase.array.push_back(0); + arraybase.array_size_literal.push_back(true); + arraybase.parent_type = ops[1]; + + // .self resolves down to non-array/non-pointer type. + arraybase.self = base.self; + break; + } + + case OpTypeImage: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::Image; + type.image.type = ops[1]; + type.image.dim = static_cast(ops[2]); + type.image.depth = ops[3] == 1; + type.image.arrayed = ops[4] != 0; + type.image.ms = ops[5] != 0; + type.image.sampled = ops[6]; + type.image.format = static_cast(ops[7]); + type.image.access = (length >= 9) ? static_cast(ops[8]) : AccessQualifierMax; + break; + } + + case OpTypeSampledImage: + { + uint32_t id = ops[0]; + uint32_t imagetype = ops[1]; + auto &type = set(id, op); + type = get(imagetype); + type.basetype = SPIRType::SampledImage; + type.self = id; + break; + } + + case OpTypeSampler: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::Sampler; + break; + } + + case OpTypeUntypedPointerKHR: + case OpTypePointer: + { + uint32_t id = ops[0]; + + // Very rarely, we might receive a FunctionPrototype here. + // We won't be able to compile it, but we shouldn't crash when parsing. + // We should be able to reflect. + auto *base = op == OpTypePointer ? maybe_get(ops[2]) : nullptr; + auto &ptrbase = set(id, op); + + if (base) + { + ptrbase = *base; + ptrbase.op = op; + } + + ptrbase.pointer = true; + ptrbase.pointer_depth++; + ptrbase.storage = static_cast(ops[1]); + + if (ptrbase.storage == StorageClassAtomicCounter) + ptrbase.basetype = SPIRType::AtomicCounter; + + if (base && base->forward_pointer) + forward_pointer_fixups.push_back({ id, ops[2] }); + + if (op == OpTypePointer) + ptrbase.parent_type = ops[2]; + else + ptrbase.basetype = SPIRType::Void; + + // Do NOT set ptrbase.self! + break; + } + + case OpTypeForwardPointer: + { + uint32_t id = ops[0]; + auto &ptrbase = set(id, op); + ptrbase.pointer = true; + ptrbase.pointer_depth++; + ptrbase.storage = static_cast(ops[1]); + ptrbase.forward_pointer = true; + + if (ptrbase.storage == StorageClassAtomicCounter) + ptrbase.basetype = SPIRType::AtomicCounter; + + break; + } + + case OpTypeStruct: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::Struct; + for (uint32_t i = 1; i < length; i++) + type.member_types.push_back(ops[i]); + + // Check if we have seen this struct type before, with just different + // decorations. + // + // Add workaround for issue #17 as well by looking at OpName for the struct + // types, which we shouldn't normally do. + // We should not normally have to consider type aliases like this to begin with + // however ... glslang issues #304, #307 cover this. + + // For stripped names, never consider struct type aliasing. + // We risk declaring the same struct multiple times, but type-punning is not allowed + // so this is safe. + bool consider_aliasing = !ir.get_name(type.self).empty(); + if (consider_aliasing) + { + for (auto &other : global_struct_cache) + { + if (ir.get_name(type.self) == ir.get_name(other) && + types_are_logically_equivalent(type, get(other))) + { + type.type_alias = other; + break; + } + } + + if (type.type_alias == TypeID(0)) + global_struct_cache.push_back(id); + } + break; + } + + case OpTypeFunction: + { + uint32_t id = ops[0]; + uint32_t ret = ops[1]; + + auto &func = set(id, ret); + for (uint32_t i = 2; i < length; i++) + func.parameter_types.push_back(ops[i]); + break; + } + + case OpTypeAccelerationStructureKHR: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::AccelerationStructure; + break; + } + + case OpTypeRayQueryKHR: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::RayQuery; + break; + } + + case OpTypeTensorARM: + { + uint32_t id = ops[0]; + auto &type = set(id, op); + type.basetype = SPIRType::Tensor; + type.ext.tensor = {}; + type.ext.tensor.type = ops[1]; + if (length >= 3) + type.ext.tensor.rank = ops[2]; + if (length >= 4) + type.ext.tensor.shape = ops[3]; + break; + } + + // Variable declaration + // All variables are essentially pointers with a storage qualifier. + case OpVariable: + { + uint32_t type = ops[0]; + uint32_t id = ops[1]; + auto storage = static_cast(ops[2]); + uint32_t initializer = length == 4 ? ops[3] : 0; + + if (storage == StorageClassFunction) + { + if (!current_function) + SPIRV_CROSS_THROW("No function currently in scope"); + current_function->add_local_variable(id); + } + + set(id, type, storage, initializer); + break; + } + + case OpUntypedVariableKHR: + { + uint32_t type = ops[0]; + uint32_t id = ops[1]; + auto storage = static_cast(ops[2]); + uint32_t data_type = length >= 4 ? ops[3] : 0; + uint32_t initializer = length >= 5 ? ops[4] : 0; + + if (storage == StorageClassFunction) + { + if (!current_function) + SPIRV_CROSS_THROW("No function currently in scope"); + current_function->add_local_variable(id); + } + + auto &v = set(id, type, storage, initializer); + v.untyped = true; + v.untyped_alloca_type = data_type; + break; + } + + // OpPhi + // OpPhi is a fairly magical opcode. + // It selects temporary variables based on which parent block we *came from*. + // In high-level languages we can "de-SSA" by creating a function local, and flush out temporaries to this function-local + // variable to emulate SSA Phi. + case OpPhi: + { + if (!current_function) + SPIRV_CROSS_THROW("No function currently in scope"); + if (!current_block) + SPIRV_CROSS_THROW("No block currently in scope"); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + + // Instead of a temporary, create a new function-wide temporary with this ID instead. + auto &var = set(id, result_type, StorageClassFunction); + var.phi_variable = true; + + current_function->add_local_variable(id); + + for (uint32_t i = 2; i + 2 <= length; i += 2) + current_block->phi_variables.push_back({ ops[i], ops[i + 1], id }); + break; + } + + // Constants + case OpSpecConstant: + case OpConstant: + case OpConstantCompositeReplicateEXT: + case OpSpecConstantCompositeReplicateEXT: + { + uint32_t id = ops[1]; + auto &type = get(ops[0]); + if (op == OpConstantCompositeReplicateEXT || op == OpSpecConstantCompositeReplicateEXT) + { + auto subconstant = uint32_t(ops[2]); + set(id, ops[0], &subconstant, 1, op == OpSpecConstantCompositeReplicateEXT, true); + } + else + { + + if (type.width > 32) + set(id, ops[0], ops[2] | (uint64_t(ops[3]) << 32), op == OpSpecConstant); + else + set(id, ops[0], ops[2], op == OpSpecConstant); + } + break; + } + + case OpSpecConstantFalse: + case OpConstantFalse: + { + uint32_t id = ops[1]; + set(id, ops[0], uint32_t(0), op == OpSpecConstantFalse); + break; + } + + case OpSpecConstantTrue: + case OpConstantTrue: + { + uint32_t id = ops[1]; + set(id, ops[0], uint32_t(1), op == OpSpecConstantTrue); + break; + } + + case OpConstantNull: + { + uint32_t id = ops[1]; + uint32_t type = ops[0]; + ir.make_constant_null(id, type, true); + break; + } + + case OpSpecConstantComposite: + case OpConstantComposite: + { + uint32_t id = ops[1]; + uint32_t type = ops[0]; + + auto &ctype = get(type); + + // We can have constants which are structs and arrays. + // In this case, our SPIRConstant will be a list of other SPIRConstant ids which we + // can refer to. + if (ctype.basetype == SPIRType::Struct || !ctype.array.empty()) + { + set(id, type, ops + 2, length - 2, op == OpSpecConstantComposite); + } + else + { + uint32_t elements = length - 2; + if (elements > 4) + SPIRV_CROSS_THROW("OpConstantComposite only supports 1, 2, 3 and 4 elements."); + + SPIRConstant remapped_constant_ops[4]; + const SPIRConstant *c[4]; + for (uint32_t i = 0; i < elements; i++) + { + // Specialization constants operations can also be part of this. + // We do not know their value, so any attempt to query SPIRConstant later + // will fail. We can only propagate the ID of the expression and use to_expression on it. + auto *constant_op = maybe_get(ops[2 + i]); + auto *undef_op = maybe_get(ops[2 + i]); + if (constant_op) + { + if (op == OpConstantComposite) + SPIRV_CROSS_THROW("Specialization constant operation used in OpConstantComposite."); + + remapped_constant_ops[i].make_null(get(constant_op->basetype)); + remapped_constant_ops[i].self = constant_op->self; + remapped_constant_ops[i].constant_type = constant_op->basetype; + remapped_constant_ops[i].specialization = true; + c[i] = &remapped_constant_ops[i]; + } + else if (undef_op) + { + // Undefined, just pick 0. + remapped_constant_ops[i].make_null(get(undef_op->basetype)); + remapped_constant_ops[i].constant_type = undef_op->basetype; + c[i] = &remapped_constant_ops[i]; + } + else + c[i] = &get(ops[2 + i]); + } + set(id, type, c, elements, op == OpSpecConstantComposite); + } + break; + } + + case OpConstantSizeOfEXT: + { + uint32_t id = ops[1]; + uint32_t type = ops[0]; + auto &c = set(id, type); + c.size_of_type = ops[2]; + break; + } + + case OpTypeBufferEXT: + { + uint32_t type = ops[0]; + auto &t = set(type, OpTypeBufferEXT); + t.basetype = SPIRType::DescriptorHeapBuffer; + t.ext.descriptor_heap_buffer.storage = static_cast(ops[1]); + break; + } + + // Functions + case OpFunction: + { + uint32_t res = ops[0]; + uint32_t id = ops[1]; + // Control + uint32_t type = ops[3]; + + if (current_function) + SPIRV_CROSS_THROW("Must end a function before starting a new one!"); + + current_function = &set(id, res, type); + break; + } + + case OpFunctionParameter: + { + uint32_t type = ops[0]; + uint32_t id = ops[1]; + + if (!current_function) + SPIRV_CROSS_THROW("Must be in a function!"); + + current_function->add_parameter(type, id); + set(id, type, StorageClassFunction); + break; + } + + case OpFunctionEnd: + { + if (current_block) + { + // Very specific error message, but seems to come up quite often. + SPIRV_CROSS_THROW( + "Cannot end a function before ending the current block.\n" + "Likely cause: If this SPIR-V was created from glslang HLSL, make sure the entry point is valid."); + } + current_function = nullptr; + break; + } + + // Blocks + case OpLabel: + { + // OpLabel always starts a block. + if (!current_function) + SPIRV_CROSS_THROW("Blocks cannot exist outside functions!"); + + uint32_t id = ops[0]; + + current_function->blocks.push_back(id); + if (!current_function->entry_block) + current_function->entry_block = id; + + if (current_block) + SPIRV_CROSS_THROW("Cannot start a block before ending the current block."); + + current_block = &set(id); + break; + } + + // Branch instructions end blocks. + case OpBranch: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + + uint32_t target = ops[0]; + current_block->terminator = SPIRBlock::Direct; + current_block->next_block = target; + current_block = nullptr; + break; + } + + case OpBranchConditional: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + + current_block->condition = ops[0]; + current_block->true_block = ops[1]; + current_block->false_block = ops[2]; + + current_block->terminator = SPIRBlock::Select; + + if (current_block->true_block == current_block->false_block) + { + // Bogus conditional, translate to a direct branch. + // Avoids some ugly edge cases later when analyzing CFGs. + + // There are some super jank cases where the merge block is different from the true/false, + // and later branches can "break" out of the selection construct this way. + // This is complete nonsense, but CTS hits this case. + // In this scenario, we should see the selection construct as more of a Switch with one default case. + // The problem here is that this breaks any attempt to break out of outer switch statements, + // but it's theoretically solvable if this ever comes up using the ladder breaking system ... + + if (current_block->true_block != current_block->next_block && + current_block->merge == SPIRBlock::MergeSelection) + { + uint32_t ids = ir.increase_bound_by(2); + + auto &type = set(ids, OpTypeInt); + type.basetype = SPIRType::Int; + type.width = 32; + auto &c = set(ids + 1, ids); + + current_block->condition = c.self; + current_block->default_block = current_block->true_block; + current_block->terminator = SPIRBlock::MultiSelect; + ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT; + ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT; + } + else + { + // Collapse loops if we have to. + bool collapsed_loop = current_block->true_block == current_block->merge_block && + current_block->merge == SPIRBlock::MergeLoop; + + if (collapsed_loop) + { + ir.block_meta[current_block->merge_block] &= ~ParsedIR::BLOCK_META_LOOP_MERGE_BIT; + ir.block_meta[current_block->continue_block] &= ~ParsedIR::BLOCK_META_CONTINUE_BIT; + } + + current_block->next_block = current_block->true_block; + current_block->condition = 0; + current_block->true_block = 0; + current_block->false_block = 0; + current_block->merge_block = 0; + current_block->merge = SPIRBlock::MergeNone; + current_block->terminator = SPIRBlock::Direct; + } + } + + current_block = nullptr; + break; + } + + case OpSwitch: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + + current_block->terminator = SPIRBlock::MultiSelect; + + current_block->condition = ops[0]; + current_block->default_block = ops[1]; + + uint32_t remaining_ops = length - 2; + if ((remaining_ops % 2) == 0) + { + for (uint32_t i = 2; i + 2 <= length; i += 2) + current_block->cases_32bit.push_back({ ops[i], ops[i + 1] }); + } + + if ((remaining_ops % 3) == 0) + { + for (uint32_t i = 2; i + 3 <= length; i += 3) + { + uint64_t value = (static_cast(ops[i + 1]) << 32) | ops[i]; + current_block->cases_64bit.push_back({ value, ops[i + 2] }); + } + } + + // If we jump to next block, make it break instead since we're inside a switch case block at that point. + ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT; + + current_block = nullptr; + break; + } + + case OpKill: + case OpTerminateInvocation: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Kill; + current_block = nullptr; + break; + } + + case OpTerminateRayKHR: + // NV variant is not a terminator. + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::TerminateRay; + current_block = nullptr; + break; + + case OpIgnoreIntersectionKHR: + // NV variant is not a terminator. + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::IgnoreIntersection; + current_block = nullptr; + break; + + case OpEmitMeshTasksEXT: + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::EmitMeshTasks; + for (uint32_t i = 0; i < 3; i++) + current_block->mesh.groups[i] = ops[i]; + current_block->mesh.payload = length >= 4 ? ops[3] : 0; + current_block = nullptr; + // Currently glslang is bugged and does not treat EmitMeshTasksEXT as a terminator. + ignore_trailing_block_opcodes = true; + break; + + case OpReturn: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Return; + current_block = nullptr; + break; + } + + case OpReturnValue: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Return; + current_block->return_value = ops[0]; + current_block = nullptr; + break; + } + + case OpUnreachable: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to end a non-existing block."); + current_block->terminator = SPIRBlock::Unreachable; + current_block = nullptr; + break; + } + + case OpSelectionMerge: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to modify a non-existing block."); + + current_block->next_block = ops[0]; + current_block->merge = SPIRBlock::MergeSelection; + ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_SELECTION_MERGE_BIT; + + if (length >= 2) + { + if (ops[1] & SelectionControlFlattenMask) + current_block->hint = SPIRBlock::HintFlatten; + else if (ops[1] & SelectionControlDontFlattenMask) + current_block->hint = SPIRBlock::HintDontFlatten; + } + break; + } + + case OpLoopMerge: + { + if (!current_block) + SPIRV_CROSS_THROW("Trying to modify a non-existing block."); + + current_block->merge_block = ops[0]; + current_block->continue_block = ops[1]; + current_block->merge = SPIRBlock::MergeLoop; + + ir.block_meta[current_block->self] |= ParsedIR::BLOCK_META_LOOP_HEADER_BIT; + ir.block_meta[current_block->merge_block] |= ParsedIR::BLOCK_META_LOOP_MERGE_BIT; + + ir.continue_block_to_loop_header[current_block->continue_block] = BlockID(current_block->self); + + // Don't add loop headers to continue blocks, + // which would make it impossible branch into the loop header since + // they are treated as continues. + if (current_block->continue_block != BlockID(current_block->self)) + ir.block_meta[current_block->continue_block] |= ParsedIR::BLOCK_META_CONTINUE_BIT; + + if (length >= 3) + { + if (ops[2] & LoopControlUnrollMask) + current_block->hint = SPIRBlock::HintUnroll; + else if (ops[2] & LoopControlDontUnrollMask) + current_block->hint = SPIRBlock::HintDontUnroll; + } + break; + } + + case OpSpecConstantOp: + { + if (length < 3) + SPIRV_CROSS_THROW("OpSpecConstantOp not enough arguments."); + + uint32_t result_type = ops[0]; + uint32_t id = ops[1]; + auto spec_op = static_cast(ops[2]); + + set(id, result_type, spec_op, ops + 3, length - 3); + break; + } + + case OpLine: + { + // OpLine might come at global scope, but we don't care about those since they will not be declared in any + // meaningful correct order. + // Ignore all OpLine directives which live outside a function. + if (current_block) + current_block->ops.push_back(instruction); + + // Line directives may arrive before first OpLabel. + // Treat this as the line of the function declaration, + // so warnings for arguments can propagate properly. + if (current_function) + { + // Store the first one we find and emit it before creating the function prototype. + if (current_function->entry_line.file_id == 0) + { + current_function->entry_line.file_id = ops[0]; + current_function->entry_line.line_literal = ops[1]; + } + } + + uint32_t file = ops[0]; + uint32_t line = ops[1]; + + for (auto &source : ir.sources) + { + if (source.file_id == file) + { + source.line_markers.emplace_back(); + auto &marker = source.line_markers.back(); + marker.line = line; + marker.offset = instruction.offset - 1; + marker.function_id = current_function ? current_function->self : ID(0); + marker.block_id = current_block ? current_block->self : ID(0); + break; + } + } + + break; + } + + case OpNoLine: + { + // OpNoLine might come at global scope. + if (current_block) + current_block->ops.push_back(instruction); + break; + } + + // Actual opcodes. + default: + { + if (length >= 2) + { + const auto *type = maybe_get(ops[0]); + if (type) + ir.load_type_width.insert({ ops[1], type->width }); + } + + if (!current_block) + SPIRV_CROSS_THROW("Currently no block to insert opcode."); + + current_block->ops.push_back(instruction); + break; + } + } +} + +bool Parser::types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const +{ + if (a.basetype != b.basetype) + return false; + if (a.width != b.width) + return false; + if (a.vecsize != b.vecsize) + return false; + if (a.columns != b.columns) + return false; + if (a.array.size() != b.array.size()) + return false; + + size_t array_count = a.array.size(); + if (array_count && memcmp(a.array.data(), b.array.data(), array_count * sizeof(uint32_t)) != 0) + return false; + + if (a.basetype == SPIRType::Image || a.basetype == SPIRType::SampledImage) + { + if (memcmp(&a.image, &b.image, sizeof(SPIRType::Image)) != 0) + return false; + } + + if (a.member_types.size() != b.member_types.size()) + return false; + + size_t member_types = a.member_types.size(); + for (size_t i = 0; i < member_types; i++) + { + if (!types_are_logically_equivalent(get(a.member_types[i]), get(b.member_types[i]))) + return false; + } + + return true; +} + +bool Parser::variable_storage_is_aliased(const SPIRVariable &v) const +{ + auto &type = get(v.basetype); + + auto *type_meta = ir.find_meta(type.self); + + bool ssbo = v.storage == StorageClassStorageBuffer || + (type_meta && type_meta->decoration.decoration_flags.get(DecorationBufferBlock)); + bool image = type.basetype == SPIRType::Image; + bool counter = type.basetype == SPIRType::AtomicCounter; + + bool is_restrict; + if (ssbo) + is_restrict = ir.get_buffer_block_flags(v).get(DecorationRestrict); + else + is_restrict = ir.has_decoration(v.self, DecorationRestrict); + + return !is_restrict && (ssbo || image || counter); +} +} // namespace SPIRV_CROSS_NAMESPACE diff --git a/thirdparty/SPIRV-Cross/spirv_parser.hpp b/thirdparty/SPIRV-Cross/spirv_parser.hpp new file mode 100644 index 000000000..dabc0e224 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_parser.hpp @@ -0,0 +1,103 @@ +/* + * Copyright 2018-2021 Arm Limited + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_PARSER_HPP +#define SPIRV_CROSS_PARSER_HPP + +#include "spirv_cross_parsed_ir.hpp" +#include + +namespace SPIRV_CROSS_NAMESPACE +{ +class Parser +{ +public: + Parser(const uint32_t *spirv_data, size_t word_count); + Parser(std::vector spirv); + + void parse(); + + ParsedIR &get_parsed_ir() + { + return ir; + } + +private: + ParsedIR ir; + SPIRFunction *current_function = nullptr; + SPIRBlock *current_block = nullptr; + // For workarounds. + bool ignore_trailing_block_opcodes = false; + + void parse(const Instruction &instr); + const uint32_t *stream(const Instruction &instr) const; + + template + T &set(uint32_t id, P &&... args) + { + ir.add_typed_id(static_cast(T::type), id); + auto &var = variant_set(ir.ids[id], std::forward

(args)...); + var.self = id; + return var; + } + + template + T &get(uint32_t id) + { + return variant_get(ir.ids[id]); + } + + template + T *maybe_get(uint32_t id) + { + if (ir.ids[id].get_type() == static_cast(T::type)) + return &get(id); + else + return nullptr; + } + + template + const T &get(uint32_t id) const + { + return variant_get(ir.ids[id]); + } + + template + const T *maybe_get(uint32_t id) const + { + if (ir.ids[id].get_type() == T::type) + return &get(id); + else + return nullptr; + } + + // This must be an ordered data structure so we always pick the same type aliases. + SmallVector global_struct_cache; + SmallVector> forward_pointer_fixups; + + bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const; + bool variable_storage_is_aliased(const SPIRVariable &v) const; +}; +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/thirdparty/SPIRV-Cross/spirv_reflect.cpp b/thirdparty/SPIRV-Cross/spirv_reflect.cpp new file mode 100644 index 000000000..380761304 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_reflect.cpp @@ -0,0 +1,721 @@ +/* + * Copyright 2018-2021 Bradley Austin Davis + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#include "spirv_reflect.hpp" +#include "spirv_glsl.hpp" +#include + +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +namespace simple_json +{ +enum class Type +{ + Object, + Array, +}; + +using State = std::pair; +using Stack = std::stack; + +class Stream +{ + Stack stack; + StringStream<> buffer; + uint32_t indent{ 0 }; + char current_locale_radix_character = '.'; + +public: + void set_current_locale_radix_character(char c) + { + current_locale_radix_character = c; + } + + 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); + void emit_json_array_value(bool 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::emit_json_array_value(bool 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 ? "true" : "false"); + 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(convert_to_string(value, current_locale_radix_character)); +} + +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() +{ + json_stream = std::make_shared(); + json_stream->set_current_locale_radix_character(current_locale_radix_character); + json_stream->begin_json_object(); + reorder_type_alias(); + emit_entry_points(); + emit_types(); + emit_resources(); + emit_specialization_constants(); + json_stream->end_json_object(); + return json_stream->str(); +} + +static bool naturally_emit_type(const SPIRType &type) +{ + return type.basetype == SPIRType::Struct && !type.pointer && type.array.empty(); +} + +bool CompilerReflection::type_is_reference(const SPIRType &type) const +{ + // Physical pointers and arrays of physical pointers need to refer to the pointee's type. + return is_physical_pointer(type) || + (type_is_array_of_pointers(type) && type.storage == StorageClassPhysicalStorageBuffer); +} + +void CompilerReflection::emit_types() +{ + bool emitted_open_tag = false; + + SmallVector physical_pointee_types; + + // If we have physical pointers or arrays of physical pointers, it's also helpful to emit the pointee type + // and chain the type hierarchy. For POD, arrays can emit the entire type in-place. + ir.for_each_typed_id([&](uint32_t self, SPIRType &type) { + if (naturally_emit_type(type)) + { + emit_type(self, emitted_open_tag); + } + else if (type_is_reference(type)) + { + if (!naturally_emit_type(this->get(type.parent_type)) && + find(physical_pointee_types.begin(), physical_pointee_types.end(), type.parent_type) == + physical_pointee_types.end()) + { + physical_pointee_types.push_back(type.parent_type); + } + } + }); + + for (uint32_t pointee_type : physical_pointee_types) + emit_type(pointee_type, emitted_open_tag); + + if (emitted_open_tag) + { + json_stream->end_json_object(); + } +} + +void CompilerReflection::emit_type(uint32_t type_id, bool &emitted_open_tag) +{ + auto &type = get(type_id); + auto name = type_to_glsl(type); + + 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_id)); + json_stream->emit_json_key_value("name", name); + + if (is_physical_pointer(type)) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type)); + json_stream->emit_json_key_value("physical_pointer", true); + } + else if (!type.array.empty()) + { + emit_type_array(type); + json_stream->emit_json_key_value("type", "_" + std::to_string(type.parent_type)); + json_stream->emit_json_key_value("array_stride", get_decoration(type_id, DecorationArrayStride)); + } + else + { + 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 (type_is_reference(membertype)) + { + json_stream->emit_json_key_value("type", "_" + std::to_string(membertype.parent_type)); + } + else 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 (!is_physical_pointer(type) && !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(); + + json_stream->emit_json_key_array("array_size_is_literal"); + for (const auto &value : type.array_size_literal) + 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 &membertype = get(type.member_types[index]); + emit_type_array(membertype); + auto &memb = ir.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); + + // Array stride is a property of the array type, not the struct. + if (has_decoration(type.member_types[index], DecorationArrayStride)) + json_stream->emit_json_key_value("array_stride", + get_decoration(type.member_types[index], DecorationArrayStride)); + + if (dec.decoration_flags.get(DecorationMatrixStride)) + json_stream->emit_json_key_value("matrix_stride", dec.matrix_stride); + if (dec.decoration_flags.get(DecorationRowMajor)) + json_stream->emit_json_key_value("row_major", true); + + if (is_physical_pointer(membertype)) + json_stream->emit_json_key_value("physical_pointer", true); + } +} + +string CompilerReflection::execution_model_to_str(ExecutionModel model) +{ + switch (model) + { + case ExecutionModelVertex: + return "vert"; + case ExecutionModelTessellationControl: + return "tesc"; + case ExecutionModelTessellationEvaluation: + return "tese"; + case ExecutionModelGeometry: + return "geom"; + case ExecutionModelFragment: + return "frag"; + case ExecutionModelGLCompute: + return "comp"; + case ExecutionModelRayGenerationNV: + return "rgen"; + case ExecutionModelIntersectionNV: + return "rint"; + case ExecutionModelAnyHitNV: + return "rahit"; + case ExecutionModelClosestHitNV: + return "rchit"; + case ExecutionModelMissNV: + return "rmiss"; + case ExecutionModelCallableNV: + return "rcall"; + case ExecutionModelMeshNV: + case ExecutionModelMeshEXT: + return "mesh"; + case ExecutionModelTaskNV: + case ExecutionModelTaskEXT: + return "task"; + 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()) + { + // Needed to make output deterministic. + sort(begin(entries), end(entries), [](const EntryPoint &a, const EntryPoint &b) -> bool { + if (a.execution_model < b.execution_model) + return true; + else if (a.execution_model > b.execution_model) + return false; + else + return a.name < b.name; + }); + + 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)); + if (e.execution_model == ExecutionModelGLCompute || e.execution_model == ExecutionModelMeshEXT || + e.execution_model == ExecutionModelMeshNV || e.execution_model == ExecutionModelTaskEXT || + e.execution_model == ExecutionModelTaskNV) + { + const auto &spv_entry = get_entry_point(e.name, e.execution_model); + + SpecializationConstant spec_x, spec_y, spec_z; + get_work_group_size_specialization_constants(spec_x, spec_y, spec_z); + + json_stream->emit_json_key_array("workgroup_size"); + json_stream->emit_json_array_value(spec_x.id != ID(0) ? spec_x.constant_id : + spv_entry.workgroup_size.x); + json_stream->emit_json_array_value(spec_y.id != ID(0) ? spec_y.constant_id : + spv_entry.workgroup_size.y); + json_stream->emit_json_array_value(spec_z.id != ID(0) ? spec_z.constant_id : + spv_entry.workgroup_size.z); + json_stream->end_json_array(); + + json_stream->emit_json_key_array("workgroup_size_is_spec_constant_id"); + json_stream->emit_json_array_value(spec_x.id != ID(0)); + json_stream->emit_json_array_value(spec_y.id != ID(0)); + json_stream->emit_json_array_value(spec_z.id != ID(0)); + json_stream->end_json_array(); + } + 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); + emit_resources("acceleration_structures", res.acceleration_structures); + emit_resources("tensors", res.tensors); +} + +void CompilerReflection::emit_resources(const char *tag, const SmallVector &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 = ir.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); + + ID fallback_id = !is_push_constant && is_block ? ID(res.base_type_id) : 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)); + Bitset qualifier_mask = ssbo_block ? get_buffer_block_flags(res.id) : mask; + + if (qualifier_mask.get(DecorationNonReadable)) + json_stream->emit_json_key_value("writeonly", true); + if (qualifier_mask.get(DecorationNonWritable)) + json_stream->emit_json_key_value("readonly", true); + if (qualifier_mask.get(DecorationRestrict)) + json_stream->emit_json_key_value("restrict", true); + if (qualifier_mask.get(DecorationCoherent)) + json_stream->emit_json_key_value("coherent", true); + if (qualifier_mask.get(DecorationVolatile)) + json_stream->emit_json_key_value("volatile", true); + } + + emit_type_array(type); + + { + bool is_sized_block = is_block && (get_storage_class(res.id) == StorageClassUniform || + get_storage_class(res.id) == StorageClassUniformConstant || + get_storage_class(res.id) == StorageClassStorageBuffer); + 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)); + if (mask.get(DecorationWeightTextureQCOM)) + json_stream->emit_json_key_value("WeightTextureQCOM", get_decoration(res.id, DecorationWeightTextureQCOM)); + if (mask.get(DecorationBlockMatchTextureQCOM)) + json_stream->emit_json_key_value("BlockMatchTextureQCOM", get_decoration(res.id, DecorationBlockMatchTextureQCOM)); + if (mask.get(DecorationBlockMatchSamplerQCOM)) + json_stream->emit_json_key_value("BlockMatchSamplerQCOM", get_decoration(res.id, DecorationBlockMatchSamplerQCOM)); + + // 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("name", get_name(spec_const.id)); + json_stream->emit_json_key_value("id", spec_const.constant_id); + json_stream->emit_json_key_value("type", type_to_glsl(type)); + json_stream->emit_json_key_value("variable_id", spec_const.id); + 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 *type_meta = ir.find_meta(type.self); + + if (type_meta) + { + auto &memb = type_meta->members; + if (index < memb.size() && !memb[index].alias.empty()) + return memb[index].alias; + else + return join("_m", index); + } + else + return join("_m", index); +} diff --git a/thirdparty/SPIRV-Cross/spirv_reflect.hpp b/thirdparty/SPIRV-Cross/spirv_reflect.hpp new file mode 100644 index 000000000..492a951f9 --- /dev/null +++ b/thirdparty/SPIRV-Cross/spirv_reflect.hpp @@ -0,0 +1,92 @@ +/* + * Copyright 2018-2021 Bradley Austin Davis + * SPDX-License-Identifier: Apache-2.0 OR MIT + * + * 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. + */ + +/* + * At your option, you may choose to accept this material under either: + * 1. The Apache License, Version 2.0, found at , or + * 2. The MIT License, found at . + */ + +#ifndef SPIRV_CROSS_REFLECT_HPP +#define SPIRV_CROSS_REFLECT_HPP + +#include "spirv_glsl.hpp" +#include + +namespace simple_json +{ +class Stream; +} + +namespace SPIRV_CROSS_NAMESPACE +{ +using namespace SPIRV_CROSS_SPV_HEADER_NAMESPACE; +class CompilerReflection : public CompilerGLSL +{ + using Parent = CompilerGLSL; + +public: + explicit CompilerReflection(std::vector spirv_) + : Parent(std::move(spirv_)) + { + options.vulkan_semantics = true; + } + + CompilerReflection(const uint32_t *ir_, size_t word_count) + : Parent(ir_, word_count) + { + options.vulkan_semantics = true; + } + + explicit CompilerReflection(const ParsedIR &ir_) + : CompilerGLSL(ir_) + { + options.vulkan_semantics = true; + } + + explicit CompilerReflection(ParsedIR &&ir_) + : CompilerGLSL(std::move(ir_)) + { + options.vulkan_semantics = true; + } + + void set_format(const std::string &format); + std::string compile() override; + +private: + static std::string execution_model_to_str(ExecutionModel model); + + void emit_entry_points(); + void emit_types(); + void emit_resources(); + void emit_specialization_constants(); + + void emit_type(uint32_t type_id, 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 SmallVector &resources); + bool type_is_reference(const SPIRType &type) const; + + std::string to_member_name(const SPIRType &type, uint32_t index) const; + + std::shared_ptr json_stream; +}; + +} // namespace SPIRV_CROSS_NAMESPACE + +#endif diff --git a/util/dragonModel.py b/util/dragonModel.py new file mode 100644 index 000000000..d8f842096 --- /dev/null +++ b/util/dragonModel.py @@ -0,0 +1,320 @@ +# Builds assets/DragonModel.obj (+ .mtl): a solid, three-dimensional dragon whose side view is exactly +# the tangram picture. The tiles of Dragon.jpeg are grouped into body parts; each part gets its own +# thickness, with the tiles as chamfered slabs on both faces and a bulging faceted core between them. +# Legs and horns are mirrored to both sides, and the wings are swept out from the shoulders. +# +# --print makes the printable variant: every tile grows out to meet its neighbours (no gaps), wings, +# horns and crest become thick plates with cores of their own, and the cores are a fourth material +# ("frame") that shows as grout in the V-grooves between tiles and on every side wall. --stl writes +# one binary STL per material for a multi-colour printer, scaled so the dragon is --width mm long; +# --check voxelises the result and reports how many separate pieces it forms. +# +# --relief (with --print) keeps only the top half, lying flat: every part's core runs down to Z = 0 +# so the dragon can sit on a plate (see util/plaqueModel.py). +# +# Usage: python3 util/dragonModel.py pieces.json assets/DragonModel.obj [--scale 0.01] +# python3 util/dragonModel.py pieces.json assets/DragonPrint.obj --print --stl assets --width 150 --check +# pieces.json comes from: python3 util/traceDragon.py assets/Dragon.jpeg assets/Dragon.svg --pieces pieces.json +import argparse +import json +import os +import numpy as np +from PIL import Image, ImageDraw +from scipy import ndimage +from objWriter import ObjWriterT, writeMtl, pieceCount, reportPieces, DRAGON_MATERIALS +from outline import traceBoundary, simplifyClean, signedArea, offsetRing, earClip + +SLAB = 4.0 # thickness of a tile slab, image pixels +CHAMFER = 3.0 # chamfer on the visible tile edges +SEAM = 4 # how far a part outline grows beyond its tiles, image pixels +CLOSE = 14 # closing radius that bridges the gaps between a part's tiles +BULGE = 0.15 # how far the core swells outward at its mid-plane, as a fraction of depth +OUTLINE_TOLERANCE = 2.5 # polygon simplification for part outlines, image pixels +CELL_TOLERANCE = 1.0 # polygon simplification for grown tiles in the printable variant +CELL_GAP = 0.6 # inset of each grown tile so neighbouring colour bodies never overlap +PLATE_DEPTH = 7.0 # half thickness of printable wings, horns and crest +BODY_DEPTH = 55.0 +MATERIAL_ORDER = ('dark', 'champagne', 'glass', 'frame', 'plate') + + +def identity(p): + return p + + +class DragonT: + # Everything needed to build the dragon from a pieces.json: tile rasters, part outlines, and the + # mesh routines. build() emits the parts into a writer; footprint() is the outline of the parts. + def __init__(self, data, scale=0.01, printable=False, relief=False): + self.pieces = data['pieces'] + self.x0, self.y0, self.x1, self.y1 = data['bounds'] + self.width = self.x1 - self.x0 + self.imageW = data['width'] + self.imageH = data['height'] + self.scale = scale + self.printable = printable + self.relief = relief + tileImage = Image.new('I', (self.imageW, self.imageH), -1) + draw = ImageDraw.Draw(tileImage) + for index, piece in enumerate(self.pieces): + draw.polygon([tuple(p) for p in piece['points']], fill=index) + self.tileIndex = np.asarray(tileImage).astype(int) + self.nearestY, self.nearestX = ndimage.distance_transform_edt(self.tileIndex < 0, return_indices=True)[1] + self.centroids = np.array([np.mean(piece['points'], axis=0) for piece in self.pieces]) + self.tileDistance = {} + self.masks = {} + self.writer = None + + # ---- Tiles and outlines ---- + def tilesNear(self, points): + # Pick tiles by the nearest centroid to each anchor, so the selection survives re-tracing. + found = [] + for p in points: + index = int(np.argmin(np.hypot(*(self.centroids - np.array(p, dtype=float)).T))) + if index not in found: + found.append(index) + return found + + def materialNear(self, x, y): + xi = min(self.imageW - 1, max(0, int(round(x)))) + yi = min(self.imageH - 1, max(0, int(round(y)))) + return self.pieces[self.tileIndex[self.nearestY[yi, xi], self.nearestX[yi, xi]]]['material'] + + def toModel(self, x, y, z): + return np.array([(x - (self.x0 + self.width / 2)) * self.scale, (self.y1 - y) * self.scale, z * self.scale]) + + def modelCcw(self, ring): + # Orders a ring counter-clockwise as seen from +Z in model space (image y points down, so this + # is clockwise on the picture). + xy = np.array([self.toModel(x, y, 0.0)[:2] for x, y in ring]) + return ring[::-1] if signedArea(xy) < 0 else ring + + def partMask(self, tiles): + # Close the gaps between the tiles (which can be wider than the seams where corners meet) + # without growing the outline by more than SEAM, and make sure the part is one island. + key = tuple(tiles) + if key in self.masks: + return self.masks[key] + mask = np.isin(self.tileIndex, tiles) + radius = CLOSE + while True: + closed = ndimage.binary_dilation(mask, iterations=radius) + closed = ndimage.binary_erosion(closed, iterations=radius - SEAM) + closed = ndimage.binary_fill_holes(closed) + if ndimage.label(closed)[1] == 1 or radius > 60: + self.masks[key] = closed + return closed + radius += 8 + + def tileCells(self, tiles, mask): + # Grow every tile of a part to the bisectors between it and its neighbours, filling the whole + # part outline, then pull each cell in a touch so the colour bodies stay disjoint. + for index in tiles: + if index not in self.tileDistance: + self.tileDistance[index] = ndimage.distance_transform_edt(self.tileIndex != index) + owner = np.argmin(np.stack([self.tileDistance[index] for index in tiles]), axis=0) + cells = [] + for k, index in enumerate(tiles): + cell = mask & (owner == k) + labels, count = ndimage.label(cell) + if count > 1: + sizes = ndimage.sum(cell, labels, range(1, count + 1)) + cell = labels == (1 + int(np.argmax(sizes))) + ring = simplifyClean(traceBoundary(cell), CELL_TOLERANCE) + cells.append((index, offsetRing(self.modelCcw(ring), -CELL_GAP))) + return cells + + def insetRing(self, ring, chamfer): + # Shrink the chamfer until the inset outline keeps every edge pointing the way it did. + n = len(ring) + while chamfer > 0.25: + inner = offsetRing(ring, -chamfer) + good = np.sign(signedArea(inner)) == np.sign(signedArea(ring)) + for i in range(n): + if not good: + break + good = (ring[(i + 1) % n] - ring[i]) @ (inner[(i + 1) % n] - inner[i]) > 0 + if good: + return inner, chamfer + chamfer = chamfer * 0.5 + return ring.copy(), 0.0 + + # ---- Mesh pieces ---- + def emitFaces(self, name, faces): + # faces: list of (material, indices); grouped by material under one object. + self.writer.object(name) + byMaterial = {} + for material, indices in faces: + byMaterial.setdefault(material, []).append(indices) + for material, items in byMaterial.items(): + self.writer.material(material) + for indices in items: + self.writer.face(indices) + + def rings(self, levels, transform): + return [([self.writer.vertex(transform(self.toModel(x, y, z))) for x, y in poly], poly) for poly, z in levels] + + @staticmethod + def walls(rings, material=None, materialAt=None): + # Quads between successive rings of equal length, ordered by increasing Z; outward for CCW rings. + faces = [] + n = len(rings[0][0]) + for level in range(len(rings) - 1): + a, ringA = rings[level] + b, ringB = rings[level + 1] + for i in range(n): + j = (i + 1) % n + m = material if materialAt is None else materialAt((ringA[i] + ringA[j]) / 2) + faces.append((m, [a[i], a[j], b[j], b[i]])) + return faces + + @staticmethod + def caps(ring, backIndices, frontIndices, materialAt): + # Triangulated end faces: the back one faces -Z, the front one +Z. earClip returns every + # triangle counter-clockwise in image space; match the ring's own turn. + faces = [] + ringCcw = signedArea(ring) > 0 + for a, b, c in earClip(ring): + tri = [a, b, c] if ringCcw else [c, b, a] + material = materialAt(ring[[a, b, c]].mean(axis=0)) + faces.append((material, [backIndices[i] for i in tri[::-1]])) + faces.append((material, [frontIndices[i] for i in tri])) + return faces + + def slab(self, outline, material, zFront, zBack, chamferFront, chamferBack, transform=identity): + # A tile as a slab between two Z planes, chamfered on whichever faces are visible. + ring = self.modelCcw(np.array(outline, dtype=float)) + levels = [] + if chamferBack > 0: + inner, chamferBack = self.insetRing(ring, chamferBack) + levels.append((inner, zBack)) + levels.append((ring, zBack + chamferBack)) + else: + levels.append((ring, zBack)) + if chamferFront > 0: + inner, chamferFront = self.insetRing(ring, chamferFront) + levels.append((ring, zFront - chamferFront)) + levels.append((inner, zFront)) + else: + levels.append((ring, zFront)) + rings = self.rings(levels, transform) + return self.caps(ring, rings[0][0], rings[-1][0], lambda p: material) + self.walls(rings, material) + + def core(self, ring, zCentre, depth, transform=identity): + # The volume between the two tile faces of a part: the part outline, widest at its mid-plane. + # In relief the core is a plinth from Z = 0 up to the top tiles instead. + inner = depth - SLAB + if self.relief: + levels = [(ring, 0.0), (ring, zCentre + inner)] + else: + levels = [(ring, zCentre - inner), (offsetRing(ring, depth * BULGE), zCentre), (ring, zCentre + inner)] + rings = self.rings(levels, transform) + materialAt = (lambda p: 'frame') if self.printable else (lambda p: self.materialNear(p[0], p[1])) + return self.walls(rings, materialAt=materialAt) + self.caps(ring, rings[0][0], rings[-1][0], materialAt) + + def part(self, name, tiles, zCentre, depth, transform=identity): + mask = self.partMask(tiles) + ring = self.modelCcw(simplifyClean(traceBoundary(mask), OUTLINE_TOLERANCE)) + faces = self.core(ring, zCentre, depth, transform) + outlines = self.tileCells(tiles, mask) if self.printable else [(index, self.pieces[index]['points']) for index in tiles] + for index, outline in outlines: + material = self.pieces[index]['material'] + faces += self.slab(outline, material, zCentre + depth, zCentre + depth - SLAB, CHAMFER, 0.0, transform) + if not self.relief: + faces += self.slab(outline, material, zCentre - depth + SLAB, zCentre - depth, 0.0, CHAMFER, transform) + self.emitFaces(name, faces) + + def plate(self, name, tiles, zCentre, thickness, transform=identity): + # A free-standing tile group visible from both sides (wings, horns, crest): thin chamfered + # slabs in the display model, a proper cored part in the printable one. + if self.printable: + self.part(name, tiles, zCentre, PLATE_DEPTH, transform) + return + faces = [] + chamfer = min(CHAMFER, thickness / 3.0) + for index in tiles: + piece = self.pieces[index] + faces += self.slab(piece['points'], piece['material'], zCentre + thickness / 2, zCentre - thickness / 2, chamfer, chamfer, transform) + self.emitFaces(name, faces) + + # ---- The dragon: tile groups by anchor point, half-depths in image pixels ---- + def parts(self): + # (name, tiles, zCentre, depth, plate?) for every part; relief keeps only the +Z side. + sides = ((1.0, 'R'),) if self.relief else ((-1.0, 'L'), (1.0, 'R')) + found = [('head', self.tilesNear([(800, 130), (874, 171), (968, 182), (856, 157), (838, 243), (917, 247)]), 0.0, 42.0, False), + ('neck', self.tilesNear([(689, 232), (767, 233), (768, 313), (749, 383), (848, 408)]), 0.0, 36.0, False), + ('body', self.tilesNear([(766, 509), (566, 492), (664, 549), (905, 511), (859, 542), (786, 638), (881, 618)]), 0.0, BODY_DEPTH, False), + ('tail', self.tilesNear([(577, 730), (494, 705), (365, 713), (260, 647), (225, 558), (291, 477)]), 0.0, 26.0, False)] + for side, label in sides: + found.append(('hindLeg' + label, self.tilesNear([(934, 702), (885, 722), (1001, 727)]), side * (BODY_DEPTH - 26.0), 26.0, False)) + found.append(('foreLeg' + label, self.tilesNear([(406, 589), (479, 578)]), side * 27.0, 22.0, False)) + found.append(('horn' + label, self.tilesNear([(657, 117)]), side * 26.0, 10.0, True)) + found.append(('crest', self.tilesNear([(711, 80)]), 0.0, 8.0, True)) + for side, label in sides: + found.append(('wing' + label, self.tilesNear([(450, 246), (396, 301), (574, 398)]), side * 46.0, 6.0, True)) + return found + + def build(self, writer, transform=identity): + self.writer = writer + wingRoot = self.toModel(690, 462, 0.0) + wingAngle = np.radians(25.0) + for name, tiles, zCentre, depth, isPlate in self.parts(): + shape = transform + if name.startswith('wing') and not self.relief: + # Hinged at the shoulder and swept outward so the two wings do not coincide. + side = -1.0 if name.endswith('L') else 1.0 + cosA = np.cos(wingAngle * side) + sinA = np.sin(wingAngle * side) + + def shape(p, cosA=cosA, sinA=sinA): + q = p - wingRoot + return transform(np.array([q[0] * cosA + q[2] * sinA, q[1], -q[0] * sinA + q[2] * cosA]) + wingRoot) + + if isPlate: + self.plate(name, tiles, zCentre, depth, shape) + else: + self.part(name, tiles, zCentre, depth, shape) + + def footprint(self): + # Union of the part outlines, in image pixels. + mask = np.zeros((self.imageH, self.imageW), dtype=bool) + for name, tiles, zCentre, depth, isPlate in self.parts(): + mask |= self.partMask(tiles) + return mask + + +def writeOutputs(writer, out, header, stlDir=None, widthMm=None, modelWidth=None, check=False, voxel=0.01): + # OBJ + MTL, optional per-material STLs (scaled so the model spans widthMm), optional piece count. + mtlName = os.path.splitext(os.path.basename(out))[0] + '.mtl' + writer.write(out, mtlName, header) + used = [m for m in MATERIAL_ORDER if any(f[1] == m for f in writer.faces)] + writeMtl(os.path.join(os.path.dirname(out), mtlName), {m: DRAGON_MATERIALS[m] for m in used}, 'Materials for %s' % os.path.basename(out)) + print('%s: %d vertices, %d faces, materials %s' % (out, len(writer.vertices), writer.faceCount, ', '.join(used))) + if stlDir: + stem = os.path.splitext(os.path.basename(out))[0] + unitScale = widthMm / modelWidth + for material in used: + path = os.path.join(stlDir, '%s-%s.stl' % (stem, material)) + print('%s: %d triangles' % (path, writer.writeStl(path, {material}, unitScale, '%s %s' % (stem, material)))) + if check: + reportPieces(*pieceCount(writer, voxel)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('pieces') + parser.add_argument('out') + parser.add_argument('--scale', type=float, default=0.01, help='model units per image pixel') + parser.add_argument('--print', dest='printable', action='store_true', help='gapless, four-material printable variant') + parser.add_argument('--relief', action='store_true', help='with --print: top half only, flat underneath') + parser.add_argument('--stl', default=None, help='directory to write one STL per material into') + parser.add_argument('--width', type=float, default=150.0, help='length of the dragon in the STL files, millimetres') + parser.add_argument('--check', action='store_true', help='voxelise the model and count separate pieces') + args = parser.parse_args() + data = json.load(open(args.pieces)) + dragon = DragonT(data, args.scale, args.printable, args.relief and args.printable) + writer = ObjWriterT() + dragon.build(writer) + variant = ('printable relief ' if dragon.relief else 'printable ') if args.printable else '' + header = ['Singe dragon, %ssolid model built by util/dragonModel.py from the tiles of Dragon.jpeg' % variant, + 'Units: %g per image pixel; Y up, faces right (+X), resting on Y = 0, symmetric about Z = 0' % args.scale] + writeOutputs(writer, args.out, header, args.stl, args.width, dragon.width * args.scale, args.check, args.scale) diff --git a/util/dragonObj.py b/util/dragonObj.py new file mode 100644 index 000000000..da4f43caa --- /dev/null +++ b/util/dragonObj.py @@ -0,0 +1,122 @@ +# Builds assets/Dragon.obj (+ .mtl) from the tile polygons that util/traceDragon.py exports: every +# tile becomes a slab with a chamfered front edge, standing upright in the XY plane with depth along +Z, +# resting on Y = 0 and centred on X = 0. Planar UVs run across the whole figure so a rendering of +# Dragon.svg can be used as a texture. +# Usage: python3 util/dragonObj.py pieces.json assets/Dragon.obj [--scale 0.01] [--depth 10] [--bevel 3] +import argparse +import json +import os +import numpy as np +from objWriter import ObjWriterT, writeMtl, DRAGON_MATERIALS + +parser = argparse.ArgumentParser() +parser.add_argument('pieces') +parser.add_argument('out') +parser.add_argument('--scale', type=float, default=0.01, help='model units per image pixel') +parser.add_argument('--depth', type=float, default=10.0, help='slab thickness in image pixels') +parser.add_argument('--bevel', type=float, default=3.0, help='front chamfer size in image pixels') +args = parser.parse_args() + +data = json.load(open(args.pieces)) +x0, y0, x1, y1 = data['bounds'] +width = x1 - x0 +height = y1 - y0 + +def lineMeet(a0, a1, b0, b1): + da = a1 - a0 + db = b1 - b0 + den = da[0] * db[1] - da[1] * db[0] + if abs(den) < 1e-9: + return None + t = ((b0[0] - a0[0]) * db[1] - (b0[1] - a0[1]) * db[0]) / den + return a0 + da * t + + +def offsetPolygon(verts, amount): + # Positive amount grows a counter-clockwise polygon, negative shrinks it. + n = len(verts) + shifted = [] + for i in range(n): + a = verts[i] + b = verts[(i + 1) % n] + d = b - a + d = d / np.hypot(*d) + normal = np.array([d[1], -d[0]]) + shifted.append((a + normal * amount, b + normal * amount)) + result = [] + for i in range(n): + meet = lineMeet(*shifted[i - 1], *shifted[i]) + result.append(meet if meet is not None else verts[i]) + return np.array(result) + + +def signedArea(verts): + n = len(verts) + return 0.5 * sum(verts[i][0] * verts[(i + 1) % n][1] - verts[(i + 1) % n][0] * verts[i][1] for i in range(n)) + + +def isConvexCcw(verts): + n = len(verts) + for i in range(n): + a = verts[i] + b = verts[(i + 1) % n] + c = verts[(i + 2) % n] + if np.hypot(*(b - a)) < 1e-6: + return False + if (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0]) <= 0: + return False + return True + + +def insetForBevel(verts, bevel): + # Shrink the chamfer until the inset outline is still a valid convex polygon (sharp tips need less). + while bevel > 0.25: + inner = offsetPolygon(verts, -bevel) + if isConvexCcw(inner): + return inner, bevel + bevel = bevel * 0.5 + return verts.copy(), 0.0 + + +writer = ObjWriterT() + + +def addVertex(p): + # Image space to model space: X right, Y up (image y flipped), Z toward the viewer. + x = (p[0] - (x0 + width / 2)) * args.scale + y = (y1 - p[1]) * args.scale + z = p[2] * args.scale + return writer.vertex((x, y, z), ((p[0] - x0) / width, 1.0 - (p[1] - y0) / height)) + + +bevelUsed = [] +for index, piece in enumerate(data['pieces']): + verts = np.array(piece['points'], dtype=float) + # The offset code wants a positive signed area in image space (y down); flipping y for the model + # reverses the winding, so the rings are emitted reversed to come out counter-clockwise from +Z. + if signedArea(verts) < 0: + verts = verts[::-1] + inner, bevel = insetForBevel(verts, args.bevel) + bevelUsed.append(bevel) + verts = verts[::-1] + inner = inner[::-1] + n = len(verts) + writer.object('tile%02d_%s' % (index + 1, piece['material'])) + writer.material(piece['material']) + back = [addVertex((p[0], p[1], 0.0)) for p in verts] + rim = [addVertex((p[0], p[1], args.depth - bevel)) for p in verts] + front = [addVertex((p[0], p[1], args.depth)) for p in inner] + # Front cap, chamfer ring, side walls, back cap. + writer.face(front, textured=True) + for i in range(n): + j = (i + 1) % n + if bevel > 0: + writer.face([rim[i], rim[j], front[j], front[i]], textured=True) + writer.face([back[i], back[j], rim[j], rim[i]], textured=True) + writer.face(back[::-1], textured=True) + +mtlName = os.path.splitext(os.path.basename(args.out))[0] + '.mtl' +writer.write(args.out, mtlName, ['Singe dragon, extruded from the tiles of Dragon.jpeg by util/dragonObj.py', + 'Units: %g per image pixel; %d tiles; Y up, front faces toward +Z, resting on Y = 0' % (args.scale, len(data['pieces']))]) +writeMtl(os.path.join(os.path.dirname(args.out), mtlName), DRAGON_MATERIALS, 'Materials for %s' % os.path.basename(args.out)) +print('%s: %d vertices, %d faces, %d normals; bevel %.2f..%.2f px' % (args.out, len(writer.vertices), writer.faceCount, len(writer.normals), min(bevelUsed), max(bevelUsed))) diff --git a/util/objToGlb.py b/util/objToGlb.py new file mode 100644 index 000000000..484f712b2 --- /dev/null +++ b/util/objToGlb.py @@ -0,0 +1,165 @@ +# Converts a Wavefront OBJ (+ MTL) into a self-contained binary glTF 2.0 file, which is the only model +# format Singe loads. Each OBJ object becomes a node with one mesh primitive per material; faces are +# fan-triangulated and keep their flat normals. Materials map Kd/d to the base colour and alpha, +# Pm/Pr (or a guess from Ks/Ns) to metallic and roughness; translucent ones render blended, two-sided. +# Usage: python3 util/objToGlb.py model.obj model.glb [--root Name] +import argparse +import json +import os +import struct +import numpy as np + +parser = argparse.ArgumentParser() +parser.add_argument('obj') +parser.add_argument('out') +parser.add_argument('--root', default=None, help='name of the root node (default: the file name)') +args = parser.parse_args() + + +def loadMtl(path): + materials = {} + current = None + for line in open(path): + parts = line.split() + if not parts: + continue + if parts[0] == 'newmtl': + current = {'Kd': (0.8, 0.8, 0.8), 'Ks': (0.0, 0.0, 0.0), 'Ns': 10.0, 'd': 1.0, 'Pm': None, 'Pr': None} + materials[parts[1]] = current + elif current is not None and parts[0] in ('Kd', 'Ks'): + current[parts[0]] = tuple(float(v) for v in parts[1:4]) + elif current is not None and parts[0] in ('Ns', 'd', 'Pm', 'Pr'): + current[parts[0]] = float(parts[1]) + return materials + + +positions = [] +uvs = [] +normals = [] +materials = {} +objects = {} +objectOrder = [] +currentObject = 'default' +currentMaterial = None +for line in open(args.obj): + parts = line.split() + if not parts or parts[0] == '#': + continue + if parts[0] == 'mtllib': + materials = loadMtl(os.path.join(os.path.dirname(args.obj), parts[1])) + elif parts[0] == 'v': + positions.append([float(v) for v in parts[1:4]]) + elif parts[0] == 'vt': + uvs.append([float(parts[1]), 1.0 - float(parts[2])]) + elif parts[0] == 'vn': + normals.append([float(v) for v in parts[1:4]]) + elif parts[0] == 'o': + currentObject = parts[1] + elif parts[0] == 'usemtl': + currentMaterial = parts[1] + elif parts[0] == 'f': + corners = [] + for token in parts[1:]: + fields = token.split('/') + v = int(fields[0]) - 1 + vt = int(fields[1]) - 1 if len(fields) > 1 and fields[1] else None + vn = int(fields[2]) - 1 if len(fields) > 2 and fields[2] else None + corners.append((v, vt, vn)) + if currentObject not in objects: + objects[currentObject] = {} + objectOrder.append(currentObject) + primitive = objects[currentObject].setdefault(currentMaterial, {'lookup': {}, 'vertices': [], 'indices': []}) + indices = [] + for corner in corners: + if corner not in primitive['lookup']: + primitive['lookup'][corner] = len(primitive['vertices']) + primitive['vertices'].append(corner) + indices.append(primitive['lookup'][corner]) + for i in range(1, len(indices) - 1): + primitive['indices'] += [indices[0], indices[i], indices[i + 1]] + +positions = np.array(positions, dtype=np.float32) +uvs = np.array(uvs, dtype=np.float32) if uvs else None +normals = np.array(normals, dtype=np.float32) if normals else None + +binary = bytearray() +bufferViews = [] +accessors = [] + + +def addView(data, target): + while len(binary) % 4: + binary.append(0) + bufferViews.append({'buffer': 0, 'byteOffset': len(binary), 'byteLength': len(data), 'target': target}) + binary.extend(data) + return len(bufferViews) - 1 + + +def addAccessor(array, componentType, kind, target, bounds=False): + view = addView(array.tobytes(), target) + accessor = {'bufferView': view, 'componentType': componentType, 'count': int(array.shape[0]), 'type': kind} + if bounds: + accessor['min'] = [float(v) for v in array.min(axis=0)] + accessor['max'] = [float(v) for v in array.max(axis=0)] + accessors.append(accessor) + return len(accessors) - 1 + + +materialNames = list(materials.keys()) +glMaterials = [] +for name in materialNames: + m = materials[name] + metallic = m['Pm'] if m['Pm'] is not None else (1.0 if (np.mean(m['Ks']) > 0.5 and m['d'] >= 1.0) else 0.0) + roughness = m['Pr'] if m['Pr'] is not None else float(np.clip(1.0 - m['Ns'] / 400.0, 0.05, 1.0)) + entry = {'name': name, 'pbrMetallicRoughness': {'baseColorFactor': [m['Kd'][0], m['Kd'][1], m['Kd'][2], m['d']], 'metallicFactor': metallic, 'roughnessFactor': roughness}} + if m['d'] < 1.0: + entry['alphaMode'] = 'BLEND' + entry['doubleSided'] = True + glMaterials.append(entry) + +meshes = [] +nodes = [] +for name in objectOrder: + primitives = [] + for materialName, primitive in objects[name].items(): + verts = primitive['vertices'] + pos = positions[[v for v, vt, vn in verts]] + attributes = {'POSITION': addAccessor(pos, 5126, 'VEC3', 34962, bounds=True)} + if normals is not None and all(vn is not None for v, vt, vn in verts): + attributes['NORMAL'] = addAccessor(normals[[vn for v, vt, vn in verts]], 5126, 'VEC3', 34962) + if uvs is not None and all(vt is not None for v, vt, vn in verts): + attributes['TEXCOORD_0'] = addAccessor(uvs[[vt for v, vt, vn in verts]], 5126, 'VEC2', 34962) + indexArray = np.array(primitive['indices'], dtype=np.uint32 if len(verts) > 65535 else np.uint16) + entry = {'attributes': attributes, 'indices': addAccessor(indexArray, 5125 if indexArray.dtype == np.uint32 else 5123, 'SCALAR', 34963), 'mode': 4} + if materialName in materialNames: + entry['material'] = materialNames.index(materialName) + primitives.append(entry) + meshes.append({'name': name, 'primitives': primitives}) + nodes.append({'name': name, 'mesh': len(meshes) - 1}) + +rootName = args.root or os.path.splitext(os.path.basename(args.out))[0] +nodes.append({'name': rootName, 'children': list(range(len(nodes)))}) +gltf = { + 'asset': {'version': '2.0', 'generator': 'Singe util/objToGlb.py'}, + 'scene': 0, + 'scenes': [{'name': rootName, 'nodes': [len(nodes) - 1]}], + 'nodes': nodes, + 'meshes': meshes, + 'materials': glMaterials, + 'accessors': accessors, + 'bufferViews': bufferViews, + 'buffers': [{'byteLength': len(binary)}], +} +while len(binary) % 4: + binary.append(0) +jsonBytes = json.dumps(gltf, separators=(',', ':')).encode('utf-8') +while len(jsonBytes) % 4: + jsonBytes += b' ' +total = 12 + 8 + len(jsonBytes) + 8 + len(binary) +with open(args.out, 'wb') as f: + f.write(struct.pack('<4sII', b'glTF', 2, total)) + f.write(struct.pack(' 1e-12 else np.array([0.0, 0.0, 1.0]) + if inside is not None and n @ (np.mean(pts, axis=0) - inside) < 0: + indices = indices[::-1] + n = -n + ni = self.normal(n) + self.faces.append((self.currentObject, self.currentMaterial, list(indices))) + if textured: + self.lines.append('f ' + ' '.join('%d/%d/%d' % (i, i, ni) for i in indices)) + else: + self.lines.append('f ' + ' '.join('%d//%d' % (i, ni) for i in indices)) + self.faceCount += 1 + + def write(self, path, mtlName, header): + out = ['# ' + line for line in header] + ['mtllib %s' % mtlName] + out += self.vertices + self.uvs + self.normals + self.lines + open(path, 'w').write('\n'.join(out) + '\n') + + + def writeStl(self, path, materials, unitScale, name): + # Binary STL of the faces using any of the given materials, fan-triangulated, scaled to millimetres. + triangles = [] + for obj, material, indices in self.faces: + if material not in materials: + continue + pts = [self.point(i) * unitScale for i in indices] + for i in range(1, len(pts) - 1): + triangles.append((pts[0], pts[i], pts[i + 1])) + with open(path, 'wb') as f: + f.write(('Singe %s' % name).encode('ascii')[:80].ljust(80, b'\0')) + f.write(struct.pack(' 1e-12 else np.zeros(3) + f.write(struct.pack('<12fH', *n, *a, *b, *c, 0)) + return len(triangles) + + +def pieceCount(writer, voxel, margin=12): + # Voxelises every object of a writer by ray parity along X on a grid of the given spacing, unions + # them, and returns (count, [(voxels, objects, (min, max)) per piece]). Objects may overlap each + # other, so each is rasterised on its own; the shells within one object must not overlap. + points = np.array([writer.point(i + 1) for i in range(len(writer.vertices))]) + lower = points.min(axis=0) - margin * voxel + upper = points.max(axis=0) + margin * voxel + size = np.ceil((upper - lower) / voxel).astype(int) + 1 + solid = np.zeros((size[1], size[2], size[0]), dtype=bool) + byObject = {} + for obj, material, indices in writer.faces: + byObject.setdefault(obj, []).append(indices) + voxelsOf = {} + for obj, faces in byObject.items(): + crossings = np.zeros((size[1], size[2], size[0] + 1), dtype=np.int8) + for indices in faces: + pts = [(writer.point(i) - lower) / voxel for i in indices] + for t in range(1, len(pts) - 1): + a, b, c = pts[0], pts[t], pts[t + 1] + det = (b[1] - a[1]) * (c[2] - a[2]) - (c[1] - a[1]) * (b[2] - a[2]) + if abs(det) < 1e-9: + continue + yMin = max(0, int(np.floor(min(a[1], b[1], c[1])))) + yMax = min(size[1] - 1, int(np.ceil(max(a[1], b[1], c[1])))) + zMin = max(0, int(np.floor(min(a[2], b[2], c[2])))) + zMax = min(size[2] - 1, int(np.ceil(max(a[2], b[2], c[2])))) + if yMin > yMax or zMin > zMax: + continue + # Sample off the half-voxel so outline vertices never sit exactly on a ray. + gy, gz = np.meshgrid(np.arange(yMin, yMax + 1) + 0.5123, np.arange(zMin, zMax + 1) + 0.5123, indexing='ij') + l1 = ((b[1] - gy) * (c[2] - gz) - (c[1] - gy) * (b[2] - gz)) / det + l2 = ((c[1] - gy) * (a[2] - gz) - (a[1] - gy) * (c[2] - gz)) / det + l3 = 1 - l1 - l2 + inside = (l1 >= 0) & (l2 >= 0) & (l3 >= 0) + if not inside.any(): + continue + x = l1 * a[0] + l2 * b[0] + l3 * c[0] + xi = np.clip(np.ceil(x).astype(int), 0, size[0]) + ys, zs = np.nonzero(inside) + np.add.at(crossings, (ys + yMin, zs + zMin, xi[inside]), 1) + voxelsOf[obj] = (np.cumsum(crossings, axis=2, dtype=np.int8)[:, :, :size[0]] % 2).astype(bool) + solid |= voxelsOf[obj] + labels, count = ndimage.label(solid, structure=np.ones((3, 3, 3))) + members = {} + for obj, voxels in voxelsOf.items(): + found = labels[voxels] + if found.size: + members.setdefault(int(np.bincount(found).argmax()), []).append(obj) + pieces = [] + for label in range(1, count + 1): + box = ndimage.find_objects(labels == label)[0] + low = lower + np.array([box[2].start, box[0].start, box[1].start]) * voxel + high = lower + np.array([box[2].stop, box[0].stop, box[1].stop]) * voxel + pieces.append((int((labels == label).sum()), members.get(label, []), (low, high))) + return count, pieces + + +def reportPieces(count, pieces): + print('check: %d piece%s' % (count, '' if count == 1 else 's')) + for voxels, objects, (low, high) in pieces: + print(' piece of %d voxels at x %.2f..%.2f y %.2f..%.2f z %.2f..%.2f: %s' % (voxels, low[0], high[0], low[1], high[1], low[2], high[2], ', '.join(objects) or 'no whole object')) + + +def writeMtl(path, materials, header): + lines = ['# ' + header] + for name, m in materials.items(): + lines.append('newmtl %s' % name) + lines.append('Ka %.3f %.3f %.3f' % tuple(v * 0.2 for v in m['Kd'])) + lines.append('Kd %.3f %.3f %.3f' % m['Kd']) + lines.append('Ks %.3f %.3f %.3f' % m['Ks']) + lines.append('Ns %.1f' % m['Ns']) + lines.append('d %.2f' % m['d']) + lines.append('Ni %.2f' % m['Ni']) + lines.append('illum %d' % m['illum']) + # PBR extension keys, read by util/objToGlb.py and most modern importers. + lines.append('Pm %.2f' % m['Pm']) + lines.append('Pr %.2f' % m['Pr']) + lines.append('') + open(path, 'w').write('\n'.join(lines) + '\n') + + +# The three tile materials, shared by every dragon model. +DRAGON_MATERIALS = { + 'dark': {'Kd': (0.30, 0.31, 0.33), 'Ks': (0.70, 0.70, 0.70), 'Ns': 180.0, 'd': 1.0, 'Ni': 1.0, 'illum': 2, 'Pm': 0.8, 'Pr': 0.35}, + 'champagne': {'Kd': (0.72, 0.64, 0.54), 'Ks': (0.85, 0.80, 0.70), 'Ns': 120.0, 'd': 1.0, 'Ni': 1.0, 'illum': 2, 'Pm': 0.8, 'Pr': 0.40}, + 'glass': {'Kd': (0.88, 0.92, 0.90), 'Ks': (0.95, 0.95, 0.95), 'Ns': 300.0, 'd': 0.55, 'Ni': 1.50, 'illum': 4, 'Pm': 0.0, 'Pr': 0.15}, + # The fourth print colour: the core, side walls and grout between tiles of the printable dragon. + 'frame': {'Kd': (0.12, 0.11, 0.10), 'Ks': (0.30, 0.30, 0.30), 'Ns': 40.0, 'd': 1.0, 'Ni': 1.0, 'illum': 2, 'Pm': 0.0, 'Pr': 0.7}, + # Backing plate of the printable logotype and plaque; its own body so any filament can be assigned. + 'plate': {'Kd': (0.55, 0.53, 0.50), 'Ks': (0.20, 0.20, 0.20), 'Ns': 30.0, 'd': 1.0, 'Ni': 1.0, 'illum': 2, 'Pm': 0.0, 'Pr': 0.8}, +} diff --git a/util/outline.py b/util/outline.py new file mode 100644 index 000000000..1cadd63ec --- /dev/null +++ b/util/outline.py @@ -0,0 +1,191 @@ +# Polygon helpers shared by the model builders: boundary tracing of a bitmap mask, simplification, +# offsetting, orientation and triangulation. Rings are Nx2 numpy arrays of (x, y) points. +import numpy as np + + +def traceBoundary(mask): + # Moore-neighbour tracing, clockwise on screen, starting from the top-left foreground pixel. + padded = np.pad(mask, 1) + ys, xs = np.nonzero(padded) + start = (ys[0], xs[0]) + steps = [(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0), (-1, 1)] + boundary = [start] + current = start + came = 6 + while True: + found = False + for k in range(8): + d = (came + 1 + k) % 8 + ny = current[0] + steps[d][0] + nx = current[1] + steps[d][1] + if padded[ny, nx]: + came = (d + 4) % 8 + # Next search starts just after the backtrack direction. + came = (d + 5) % 8 + current = (ny, nx) + found = True + break + if not found or (current == start and len(boundary) > 1): + break + boundary.append(current) + if len(boundary) > 100000: + break + return np.array([(x - 1, y - 1) for y, x in boundary], dtype=float) + + +def simplify(points, tolerance): + # Douglas-Peucker on a closed ring, split at the two points farthest apart. + def dp(pts): + if len(pts) < 3: + return pts + a = pts[0] + b = pts[-1] + d = b - a + n = np.hypot(*d) + if n < 1e-9: + dist = np.hypot(*(pts - a).T) + else: + dist = np.abs(d[0] * (pts[:, 1] - a[1]) - d[1] * (pts[:, 0] - a[0])) / n + i = int(np.argmax(dist)) + if dist[i] > tolerance: + return np.vstack([dp(pts[:i + 1])[:-1], dp(pts[i:])]) + return np.array([a, b]) + far = int(np.argmax(np.hypot(*(points - points[0]).T))) + first = dp(points[:far + 1]) + second = dp(np.vstack([points[far:], points[:1]])) + ring = np.vstack([first[:-1], second[:-1]]) + return ring + + +def signedArea(ring): + n = len(ring) + return 0.5 * sum(ring[i][0] * ring[(i + 1) % n][1] - ring[(i + 1) % n][0] * ring[i][1] for i in range(n)) + + +def offsetRing(ring, amount): + # Grow (positive) or shrink a ring by a distance; mitres are clamped so acute tips do not spike. + n = len(ring) + sign = 1.0 if signedArea(ring) > 0 else -1.0 + result = [] + for i in range(n): + p = ring[i] + d0 = p - ring[i - 1] + d1 = ring[(i + 1) % n] - p + d0 = d0 / max(1e-9, np.hypot(*d0)) + d1 = d1 / max(1e-9, np.hypot(*d1)) + n0 = np.array([d0[1], -d0[0]]) * sign + n1 = np.array([d1[1], -d1[0]]) * sign + bis = n0 + n1 + length = np.hypot(*bis) + if length < 1e-6: + result.append(p + n0 * amount) + continue + bis = bis / length + mitre = amount / max(0.35, bis @ n0) + result.append(p + bis * mitre) + return np.array(result) + + +def earClip(ring): + # Triangulates a simple polygon; returns index triples into ring. + n = len(ring) + indices = list(range(n)) + if signedArea(ring) < 0: + indices.reverse() + triangles = [] + + def convex(a, b, c): + return (ring[b][0] - ring[a][0]) * (ring[c][1] - ring[a][1]) - (ring[b][1] - ring[a][1]) * (ring[c][0] - ring[a][0]) > 1e-9 + + def inside(p, a, b, c): + def side(u, v): + return (v[0] - u[0]) * (p[1] - u[1]) - (v[1] - u[1]) * (p[0] - u[0]) + return side(ring[a], ring[b]) >= 0 and side(ring[b], ring[c]) >= 0 and side(ring[c], ring[a]) >= 0 + + guard = 0 + while len(indices) > 3 and guard < 10 * n: + guard += 1 + clipped = False + for k in range(len(indices)): + a = indices[k - 1] + b = indices[k] + c = indices[(k + 1) % len(indices)] + if not convex(a, b, c): + continue + if any(inside(ring[m], a, b, c) for m in indices if m not in (a, b, c)): + continue + triangles.append((a, b, c)) + del indices[k] + clipped = True + break + if not clipped: + break + if len(indices) == 3: + triangles.append(tuple(indices)) + return triangles + + +def isConvex(ring): + n = len(ring) + sign = 0.0 + for i in range(n): + a = ring[i] + b = ring[(i + 1) % n] + c = ring[(i + 2) % n] + cross = (b[0] - a[0]) * (c[1] - b[1]) - (b[1] - a[1]) * (c[0] - b[0]) + if abs(cross) < 1e-9: + continue + if sign == 0.0: + sign = np.sign(cross) + elif np.sign(cross) != sign: + return False + return sign != 0.0 + + +def selfIntersects(ring): + # True when any two non-adjacent edges of the ring cross. + n = len(ring) + for i in range(n): + a0 = ring[i] + a1 = ring[(i + 1) % n] + for j in range(i + 2, n): + if i == 0 and j == n - 1: + continue + b0 = ring[j] + b1 = ring[(j + 1) % n] + d1 = (a1[0] - a0[0]) * (b0[1] - a0[1]) - (a1[1] - a0[1]) * (b0[0] - a0[0]) + d2 = (a1[0] - a0[0]) * (b1[1] - a0[1]) - (a1[1] - a0[1]) * (b1[0] - a0[0]) + d3 = (b1[0] - b0[0]) * (a0[1] - b0[1]) - (b1[1] - b0[1]) * (a0[0] - b0[0]) + d4 = (b1[0] - b0[0]) * (a1[1] - b0[1]) - (b1[1] - b0[1]) * (a1[0] - b0[0]) + if ((d1 > 0) != (d2 > 0)) and ((d3 > 0) != (d4 > 0)): + return True + return False + + +def removeSpikes(ring, degrees=8.0): + # Drops vertices where the outline turns back on itself, the hair-thin fingers a raster boundary + # leaves at one-pixel spurs. + limit = -np.cos(np.radians(degrees)) + changed = True + while changed and len(ring) > 3: + changed = False + n = len(ring) + for i in range(n): + d0 = ring[i] - ring[i - 1] + d1 = ring[(i + 1) % n] - ring[i] + l0 = np.hypot(*d0) + l1 = np.hypot(*d1) + if l0 < 1e-9 or l1 < 1e-9 or (d0 @ d1) / (l0 * l1) < limit: + ring = np.delete(ring, i, axis=0) + changed = True + break + return ring + + +def simplifyClean(points, tolerance): + # Simplify, drop spikes, and back off the tolerance until the ring does not cross itself. + while True: + ring = removeSpikes(simplify(points, tolerance)) + if not selfIntersects(ring) or tolerance < 0.2: + return ring + tolerance = tolerance / 2 diff --git a/util/plaqueModel.py b/util/plaqueModel.py new file mode 100644 index 000000000..749215ff5 --- /dev/null +++ b/util/plaqueModel.py @@ -0,0 +1,75 @@ +# Builds assets/Plaque.obj (+ .mtl) and one STL per colour: a single printable plaque with the Singe +# logotype raised on a backing plate and the dragon lying above the word as a bas-relief (its top +# half, every part filled down to the plate). It prints face up with no supports: the plate is the +# XY plane and Z is up. Five bodies: dark, champagne and glass tiles, the frame (dragon cores, walls, +# grout and letter walls) and the plate, for a four-colour printer with the plate sharing a filament. +# Usage: python3 util/plaqueModel.py pieces.json assets/SingeText.jpeg assets/Plaque.obj [--width 160] [--gap 10] [--stl assets] [--check] +import argparse +import json +import numpy as np +from objWriter import ObjWriterT, DRAGON_MATERIALS +from outline import traceBoundary, simplifyClean, signedArea +from dragonModel import DragonT +from textModel import LogoT, UPSCALE, PLATE_MARGIN, plateMask, writeOutputs + +parser = argparse.ArgumentParser() +parser.add_argument('pieces') +parser.add_argument('logo') +parser.add_argument('out') +parser.add_argument('--scale', type=float, default=0.01, help='model units per image pixel') +parser.add_argument('--gap', type=float, default=10.0, help='space between the letter tops and the dragon, image pixels') +parser.add_argument('--stl', default=None, help='directory to write one STL per body into') +parser.add_argument('--width', type=float, default=160.0, help='width of the plaque in the STL files, millimetres') +parser.add_argument('--check', action='store_true', help='voxelise the model and count separate pieces') +args = parser.parse_args() +scale = args.scale + +logo = LogoT(args.logo, scale) +dragon = DragonT(json.load(open(args.pieces)), scale, printable=True, relief=True) + +# Layout: the word rests on Y = 0, the dragon's feet a gap above the tallest letter, both centred on X. +letterTop = max(logo.toModel(x, y, 0.0)[1] for ring in logo.letters for x, y in ring) +lift = np.array([0.0, letterTop + args.gap * scale, 0.0]) + + +def raise_(p): + return p + lift + + +# One plate under both: compose the logo ink and the dragon footprint on a common canvas at one +# pixel per model pixel, then outline it the way the logo plate is outlined. +canvasW = 2400 +canvasH = 2600 +cx = canvasW // 2 +cy = canvasH - 400 +canvas = np.zeros((canvasH, canvasW), dtype=bool) + + +def paste(mask, shiftX, shiftY): + h, w = mask.shape + canvas[shiftY:shiftY + h, shiftX:shiftX + w] |= mask + + +ink = logo.ink.reshape(logo.ink.shape[0] // UPSCALE, UPSCALE, logo.ink.shape[1] // UPSCALE, UPSCALE).max(axis=(1, 3)) +paste(ink, int(round(cx - (logo.x0 + logo.x1) / 2)), int(round(cy - logo.y1))) +paste(dragon.footprint(), int(round(cx - (dragon.x0 + dragon.width / 2))), int(round(cy - dragon.y1 - lift[1] / scale))) +plate = plateMask(canvas, PLATE_MARGIN, 20) +ring = simplifyClean(traceBoundary(plate), 2.0) +# Canvas pixels back to logo image pixels, so the logo's own mapping places the plate. +ring = ring - np.array([cx - (logo.x0 + logo.x1) / 2, cy - logo.y1]) +xy = np.array([logo.toModel(x, y, 0.0)[:2] for x, y in ring]) +if signedArea(xy) < 0: + ring = ring[::-1] + +writer = ObjWriterT() +logo.buildPlateRing(writer, ring, 0.0) +logo.buildLetters(writer, True, 0.0) +dragon.build(writer, raise_) + +bodies = {name: name for name in ('dark', 'champagne', 'glass', 'frame', 'plate')} +materials = {name: DRAGON_MATERIALS[name] for name in bodies} +plateWidth = (ring[:, 0].max() - ring[:, 0].min()) * scale +print('%s: %d vertices, %d faces; plate %.2f x %.2f units' % (args.out, len(writer.vertices), writer.faceCount, plateWidth, (ring[:, 1].max() - ring[:, 1].min()) * scale)) +header = ['Singe plaque: the logotype and the dragon in relief on one plate, built by util/plaqueModel.py', + 'Units: %g per image pixel; plate in the XY plane resting on Y = 0, Z up (print orientation), centred on X = 0' % scale] +writeOutputs(writer, args.out, header, materials, bodies, args.stl, args.width, plateWidth, args.check, scale) diff --git a/util/previewObj.py b/util/previewObj.py new file mode 100644 index 000000000..16b8fd896 --- /dev/null +++ b/util/previewObj.py @@ -0,0 +1,149 @@ +# Minimal software renderer for checking an OBJ + MTL: perspective camera, flat shading with one key +# light and a fill light, z-buffer, translucent materials blended in a second pass. +# Usage: python3 util/previewObj.py model.obj out.png [--yaw 30] [--pitch 15] [--size 1200x800] +import argparse +import os +import numpy as np +from PIL import Image + +parser = argparse.ArgumentParser() +parser.add_argument('obj') +parser.add_argument('out') +parser.add_argument('--yaw', type=float, default=30.0) +parser.add_argument('--pitch', type=float, default=15.0) +parser.add_argument('--size', default='1200x800') +parser.add_argument('--background', default='#e8e8ec') +parser.add_argument('--distance', type=float, default=1.35, help='camera distance as a multiple of the model radius; large values approach orthographic') +args = parser.parse_args() +w, h = (int(v) for v in args.size.split('x')) + + +def loadMtl(path): + mats = {} + current = None + for line in open(path): + parts = line.split() + if not parts: + continue + if parts[0] == 'newmtl': + current = {'Kd': (0.8, 0.8, 0.8), 'Ks': (0.0, 0.0, 0.0), 'Ns': 10.0, 'd': 1.0} + mats[parts[1]] = current + elif current is not None and parts[0] in ('Kd', 'Ks'): + current[parts[0]] = tuple(float(v) for v in parts[1:4]) + elif current is not None and parts[0] in ('Ns', 'd'): + current[parts[0]] = float(parts[1]) + return mats + + +verts = [] +tris = [] +mats = {} +material = None +for line in open(args.obj): + parts = line.split() + if not parts: + continue + if parts[0] == 'mtllib': + mats = loadMtl(os.path.join(os.path.dirname(args.obj), parts[1])) + elif parts[0] == 'v': + verts.append([float(v) for v in parts[1:4]]) + elif parts[0] == 'usemtl': + material = parts[1] + elif parts[0] == 'f': + idx = [int(p.split('/')[0]) - 1 for p in parts[1:]] + for i in range(1, len(idx) - 1): + tris.append((idx[0], idx[i], idx[i + 1], material)) +verts = np.array(verts) +print('%d vertices, %d triangles' % (len(verts), len(tris))) + +# Camera orbiting the model centre. +centre = (verts.min(axis=0) + verts.max(axis=0)) / 2 +radius = np.linalg.norm(verts.max(axis=0) - verts.min(axis=0)) / 2 +yaw = np.radians(args.yaw) +pitch = np.radians(args.pitch) +eye = centre + radius * args.distance * np.array([np.sin(yaw) * np.cos(pitch), np.sin(pitch), np.cos(yaw) * np.cos(pitch)]) +forward = centre - eye +forward = forward / np.linalg.norm(forward) +right = np.cross(forward, [0.0, 1.0, 0.0]) +right = right / np.linalg.norm(right) +up = np.cross(right, forward) +focal = 1.6 * args.distance / 1.35 +keyLight = np.array([-0.4, 0.8, 0.6]) +keyLight = keyLight / np.linalg.norm(keyLight) +fillLight = np.array([0.6, 0.2, 0.5]) +fillLight = fillLight / np.linalg.norm(fillLight) + + +def project(p): + d = p - eye + x = d @ right + y = d @ up + z = d @ forward + return np.array([w / 2 + x / z * focal * h / 2, h / 2 - y / z * focal * h / 2, z]) + + +bg = tuple(int(args.background[i:i + 2], 16) for i in (1, 3, 5)) +colour = np.zeros((h, w, 3), dtype=float) + np.array(bg, dtype=float) +zbuf = np.full((h, w), np.inf) + + +def shade(a, b, c, m): + n = np.cross(b - a, c - a) + n = n / max(1e-9, np.linalg.norm(n)) + view = eye - (a + b + c) / 3 + view = view / np.linalg.norm(view) + kd = np.array(m['Kd']) + ks = np.array(m['Ks']) + diffuse = max(0.0, n @ keyLight) * 0.85 + max(0.0, n @ fillLight) * 0.35 + half = keyLight + view + half = half / np.linalg.norm(half) + spec = max(0.0, n @ half) ** (m['Ns'] / 4) * 0.6 + return np.clip((kd * (0.18 + diffuse) + ks * spec) * 255, 0, 255) + + +def raster(tri, write, alpha): + a, b, c, name = tri + m = mats.get(name, {'Kd': (0.8, 0.8, 0.8), 'Ks': (0, 0, 0), 'Ns': 10.0, 'd': 1.0}) + pa, pb, pc = project(verts[a]), project(verts[b]), project(verts[c]) + if min(pa[2], pb[2], pc[2]) <= 0: + return + xs = [pa[0], pb[0], pc[0]] + ys = [pa[1], pb[1], pc[1]] + xMin, xMax = max(0, int(min(xs))), min(w - 1, int(max(xs)) + 1) + yMin, yMax = max(0, int(min(ys))), min(h - 1, int(max(ys)) + 1) + if xMin >= xMax or yMin >= yMax: + return + gx, gy = np.meshgrid(np.arange(xMin, xMax + 1) + 0.5, np.arange(yMin, yMax + 1) + 0.5) + det = (pb[0] - pa[0]) * (pc[1] - pa[1]) - (pc[0] - pa[0]) * (pb[1] - pa[1]) + if abs(det) < 1e-9: + return + l1 = ((pb[0] - gx) * (pc[1] - gy) - (pc[0] - gx) * (pb[1] - gy)) / det + l2 = ((pc[0] - gx) * (pa[1] - gy) - (pa[0] - gx) * (pc[1] - gy)) / det + l3 = 1 - l1 - l2 + inside = (l1 >= 0) & (l2 >= 0) & (l3 >= 0) + if not inside.any(): + return + depth = l1 * pa[2] + l2 * pb[2] + l3 * pc[2] + region = zbuf[yMin:yMax + 1, xMin:xMax + 1] + visible = inside & (depth < region) + if not visible.any(): + return + rgb = shade(verts[a], verts[b], verts[c], m) + target = colour[yMin:yMax + 1, xMin:xMax + 1] + target[visible] = target[visible] * (1 - alpha) + rgb * alpha + if write: + region[visible] = depth[visible] + + +opaque = [t for t in tris if mats.get(t[3], {'d': 1.0})['d'] >= 1.0] +clear = [t for t in tris if mats.get(t[3], {'d': 1.0})['d'] < 1.0] +for t in opaque: + raster(t, True, 1.0) +# Translucent faces back to front, against the opaque depth buffer. +clear.sort(key=lambda t: -np.mean([project(verts[i])[2] for i in t[:3]])) +for t in clear: + raster(t, False, mats[t[3]]['d']) +for t in clear: + raster(t, True, 0.0) +Image.fromarray(colour.astype(np.uint8)).save(args.out) +print('wrote', args.out) diff --git a/util/textModel.py b/util/textModel.py new file mode 100644 index 000000000..569f62e73 --- /dev/null +++ b/util/textModel.py @@ -0,0 +1,176 @@ +# Builds assets/SingeText.obj (+ .mtl): the Singe logotype from SingeText.jpeg as solid letters, +# extruded with a chamfer on both faces, standing upright in the XY plane with depth along Z, +# resting on Y = 0 and centred on X = 0. Convert with util/objToGlb.py for Singe. +# +# --print makes the printable variant: the letters sit on a backing plate that joins them into one +# piece, with the back faces flat on the plate and the chamfer on the front only. It is split into +# three colour bodies (letter faces, letter walls, plate); --stl writes one STL per body in +# millimetres, --width mm wide, and --check voxelises the result and counts the pieces. +# +# Usage: python3 util/textModel.py assets/SingeText.jpeg assets/SingeText.obj [--depth 15] [--chamfer 5] +# python3 util/textModel.py assets/SingeText.jpeg assets/SingeTextPrint.obj --print --stl assets --width 150 --check +import argparse +import os +import numpy as np +from PIL import Image, ImageFilter +from scipy import ndimage +from objWriter import ObjWriterT, writeMtl, pieceCount, reportPieces, DRAGON_MATERIALS +from outline import traceBoundary, simplifyClean, signedArea, offsetRing, earClip + +FACE = 6.0 # thickness of the coloured face layer on printable letters, image pixels +PLATE = 16.0 # thickness of a backing plate, image pixels +PLATE_MARGIN = 12.0 # how far a plate reaches beyond what it carries, image pixels +PLATE_BRIDGE = 40 # closing radius (at UPSCALE) that joins separate letters into one plate outline +UPSCALE = 2 # the logo is traced at twice its size for smoother curves +BODIES = {'face': 'dark', 'walls': 'frame', 'plate': 'plate'} + + +def identity(p): + return p + + +def plateMask(mask, margin, bridge, upscale=1): + # A plate outline around a mask: gaps bridged, reaching margin beyond it, one island, spurs removed. + radius = bridge + while True: + plate = ndimage.binary_dilation(mask, iterations=radius) + plate = ndimage.binary_erosion(plate, iterations=int(radius - margin * upscale)) + plate = ndimage.binary_fill_holes(plate) + plate = ndimage.binary_opening(plate, iterations=3 * upscale) + if ndimage.label(plate)[1] == 1: + return plate + radius += 10 + + +class LogoT: + # The traced logotype: ink mask, one outline ring per letter, and the routines that extrude them. + def __init__(self, src, scale=0.01, depth=15.0, chamfer=5.0, tolerance=1.5): + self.scale = scale + self.depth = depth + self.chamfer = chamfer + grey = Image.open(src).convert('L') + big = grey.resize((grey.width * UPSCALE, grey.height * UPSCALE), Image.LANCZOS) + big = big.filter(ImageFilter.GaussianBlur(3.5 / (4 / UPSCALE))) + self.ink = np.asarray(big) < 128 + ys, xs = np.nonzero(self.ink) + self.setFootprint(xs.min() / UPSCALE, xs.max() / UPSCALE, ys.max() / UPSCALE) + labels, count = ndimage.label(self.ink) + self.holes = 0 + self.letters = [] + for index in range(1, count + 1): + component = labels == index + if component.sum() < 50 * UPSCALE * UPSCALE: + continue + filled = ndimage.binary_fill_holes(component) + self.holes += int((filled & ~component).sum() > 0) + self.letters.append(self.ringOf(filled, tolerance)) + self.letters.sort(key=lambda ring: ring[:, 0].min()) + self.writer = None + + def setFootprint(self, x0, x1, y1): + # Model space: centred on X between x0 and x1, resting on Y = 0 at image row y1. + self.x0 = x0 + self.x1 = x1 + self.y1 = y1 + + def toModel(self, x, y, z): + return np.array([(x - (self.x0 + self.x1) / 2) * self.scale, (self.y1 - y) * self.scale, z * self.scale]) + + def ringOf(self, mask, tolerance): + # Traced at UPSCALE, simplified, in image pixels, counter-clockwise as seen from +Z in model space. + ring = simplifyClean(traceBoundary(mask), tolerance * UPSCALE) / UPSCALE + xy = np.array([self.toModel(x, y, 0.0)[:2] for x, y in ring]) + return ring[::-1] if signedArea(xy) < 0 else ring + + def prism(self, name, ring, levels, capMaterial, transform=identity): + # levels: (ring, z, material of the walls rising from it) from back to front; caps close the ends. + rings = [[self.writer.vertex(transform(self.toModel(x, y, z))) for x, y in poly] for poly, z, material in levels] + self.writer.object(name) + self.writer.material(capMaterial) + ringCcw = signedArea(ring) > 0 + for a, b, c in earClip(ring): + tri = [a, b, c] if ringCcw else [c, b, a] + self.writer.face([rings[0][i] for i in tri[::-1]]) + self.writer.face([rings[-1][i] for i in tri]) + n = len(ring) + for level in range(len(rings) - 1): + self.writer.material(levels[level][2]) + a = rings[level] + b = rings[level + 1] + for i in range(n): + j = (i + 1) % n + self.writer.face([a[i], a[j], b[j], b[i]]) + + def buildLetters(self, writer, printable, zBase, transform=identity): + # Display: letters from zBase up, chamfered both sides. Printable: flat-backed letters from + # zBase up, walls in one body and the chamfered face in another. + self.writer = writer + depth = self.depth + chamfer = self.chamfer + for number, ring in enumerate(self.letters): + inset = offsetRing(ring, -chamfer) + name = 'letter%02d' % (number + 1) + if printable: + self.prism(name + 'Walls', ring, [(ring, zBase, BODIES['walls']), (ring, zBase + 2 * depth - FACE, BODIES['walls'])], BODIES['walls'], transform) + self.prism(name + 'Face', ring, [(ring, zBase + 2 * depth - FACE, BODIES['face']), (ring, zBase + 2 * depth - chamfer, BODIES['face']), (inset, zBase + 2 * depth, BODIES['face'])], BODIES['face'], transform) + else: + levels = [(inset, zBase, 'logo'), (ring, zBase + chamfer, 'logo'), (ring, zBase + 2 * depth - chamfer, 'logo'), (inset, zBase + 2 * depth, 'logo')] + self.prism(name, ring, levels, 'logo', transform) + + def buildPlate(self, writer, mask, zTop, transform=identity): + # A backing plate under a mask given at UPSCALE, from zTop - PLATE up to zTop. + self.buildPlateRing(writer, self.ringOf(mask, 2.0), zTop, transform) + + def buildPlateRing(self, writer, ring, zTop, transform=identity): + # The plate from an outline ring in this logo's image pixels. + self.writer = writer + self.prism('plate', ring, [(ring, zTop - PLATE, BODIES['plate']), (ring, zTop, BODIES['plate'])], BODIES['plate'], transform) + + +def writeOutputs(writer, out, header, materials, bodies=None, stlDir=None, widthMm=None, modelWidth=None, check=False, voxel=0.01): + mtlName = os.path.splitext(os.path.basename(out))[0] + '.mtl' + writer.write(out, mtlName, header) + writeMtl(os.path.join(os.path.dirname(out), mtlName), materials, 'Materials for %s' % os.path.basename(out)) + if stlDir: + stem = os.path.splitext(os.path.basename(out))[0] + unitScale = widthMm / modelWidth + for body, material in bodies.items(): + path = os.path.join(stlDir, '%s-%s.stl' % (stem, body)) + print('%s: %d triangles' % (path, writer.writeStl(path, {material}, unitScale, '%s %s' % (stem, body)))) + if check: + reportPieces(*pieceCount(writer, voxel)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('src') + parser.add_argument('out') + parser.add_argument('--scale', type=float, default=0.01, help='model units per image pixel') + parser.add_argument('--depth', type=float, default=15.0, help='half thickness of the letters in image pixels') + parser.add_argument('--chamfer', type=float, default=5.0, help='edge chamfer in image pixels') + parser.add_argument('--tolerance', type=float, default=1.5, help='outline simplification in image pixels') + parser.add_argument('--print', dest='printable', action='store_true', help='one-piece printable variant on a backing plate') + parser.add_argument('--stl', default=None, help='directory to write one STL per colour body into') + parser.add_argument('--width', type=float, default=150.0, help='width of the logo in the STL files, millimetres') + parser.add_argument('--check', action='store_true', help='voxelise the model and count separate pieces') + args = parser.parse_args() + logo = LogoT(args.src, args.scale, args.depth, args.chamfer, args.tolerance) + writer = ObjWriterT() + if args.printable: + plate = plateMask(logo.ink, PLATE_MARGIN, PLATE_BRIDGE, UPSCALE) + # The plate is the footprint now: it, not the letters, rests on Y = 0. + ys, xs = np.nonzero(plate) + logo.setFootprint(xs.min() / UPSCALE, xs.max() / UPSCALE, ys.max() / UPSCALE) + logo.buildPlate(writer, plate, -args.depth) + logo.buildLetters(writer, True, -args.depth) + materials = {BODIES[body]: DRAGON_MATERIALS[BODIES[body]] for body in BODIES} + bodies = BODIES + else: + logo.buildLetters(writer, False, -args.depth) + materials = {'logo': DRAGON_MATERIALS['dark']} + bodies = {'logo': 'logo'} + variant = 'printable ' if args.printable else '' + header = ['Singe logotype, %sextruded from SingeText.jpeg by util/textModel.py' % variant, + 'Units: %g per image pixel; Y up, front faces toward +Z, resting on Y = 0, centred on X = 0' % args.scale] + print('%s: %d letters, %d vertices, %d faces%s' % (args.out, len(logo.letters), len(writer.vertices), writer.faceCount, ', %d with holes (filled)' % logo.holes if logo.holes else '')) + writeOutputs(writer, args.out, header, materials, bodies, args.stl, args.width, (logo.x1 - logo.x0) * args.scale, args.check, args.scale) diff --git a/util/traceDragon.py b/util/traceDragon.py new file mode 100644 index 000000000..1907d8061 --- /dev/null +++ b/util/traceDragon.py @@ -0,0 +1,224 @@ +# Reconstructs assets/Dragon.jpeg as assets/Dragon.svg: segments the tiles by material, fits each as a +# convex polygon, samples a linear gradient per tile, and writes an SVG with a transparent background. +# Usage: python3 util/traceDragon.py assets/Dragon.jpeg assets/Dragon.svg [--debug x.png] [--preview x.png] [--pieces x.json] +# --pieces writes the tile polygons and colours for util/dragonObj.py. Needs numpy, scipy and Pillow. +import argparse +import json +import numpy as np +from PIL import Image, ImageDraw +from scipy import ndimage +from scipy.spatial import ConvexHull + +parser = argparse.ArgumentParser() +parser.add_argument('src') +parser.add_argument('out') +parser.add_argument('--debug') +parser.add_argument('--preview') +parser.add_argument('--pieces') +args = parser.parse_args() +src = args.src +out = args.out +debug = args.debug +preview = args.preview + +im = np.asarray(Image.open(src).convert('RGB')).astype(float) +h, w, _ = im.shape +r, g, b = im[..., 0], im[..., 1], im[..., 2] +bright = im.mean(axis=2) +chroma = im.max(axis=2) - im.min(axis=2) +warm = r - b + +# Everything that is not the white background; glass interiors that reach white are holes to fill. +nonbg = (bright < 246) | (chroma > 14) +nonbg = ndimage.binary_opening(nonbg, iterations=1) +nonbg = ndimage.binary_fill_holes(nonbg) + +dark = nonbg & (bright < 185) & (warm < 10) +champagne = nonbg & (warm >= 8) & (bright < 242) +glass = nonbg & ~dark & ~champagne + +# Edges between tiles: the brightness changes sharply along every tile boundary, whatever the +# materials either side, so cores are the class masks with the edges cut out. +sx = ndimage.sobel(bright, axis=1) +sy = ndimage.sobel(bright, axis=0) +edges = np.hypot(sx, sy) > 60 +edges = ndimage.binary_dilation(edges, iterations=1) + +def crisp(piece): + # Tiles have hard edges all round; the floor shadows fade out softly. + ring = ndimage.binary_dilation(piece, iterations=3) & ~piece + return (ring & edges).sum() / max(1, ring.sum()) + +def tiles(mask, erode, minArea, taken): + found = [] + for erodeNow, areaNow in ((erode, minArea), (0, 120)): + core = ndimage.binary_erosion(mask & ~edges, iterations=erodeNow) if erodeNow else (mask & ~edges) + labels, count = ndimage.label(core) + for index in range(1, count + 1): + piece = labels == index + if piece.sum() < areaNow: + continue + if (piece & taken).any(): + continue + if crisp(piece) < 0.35: + continue + # ...and they are not long thin smears either. + ys, xs = np.nonzero(piece) + span = max(xs.max() - xs.min(), ys.max() - ys.min()) + if span / max(1.0, ndimage.distance_transform_edt(piece).max()) > 20: + continue + # Grow the core back out within the class mask, but not into a neighbouring core. + grown = ndimage.binary_dilation(piece, iterations=erodeNow + 2) & mask + taken |= grown + found.append(grown) + return found + +def lineMeet(a0, a1, b0, b1): + da = a1 - a0 + db = b1 - b0 + den = da[0] * db[1] - da[1] * db[0] + if abs(den) < 1e-9: + return None + t = ((b0[0] - a0[0]) * db[1] - (b0[1] - a0[1]) * db[0]) / den + return a0 + da * t + +def polygon(mask): + ys, xs = np.nonzero(mask) + pts = np.column_stack([xs, ys]).astype(float) + hull = ConvexHull(pts) + verts = pts[hull.vertices] + # Drop near-collinear hull vertices until the corners remain. + changed = True + while changed and len(verts) > 3: + changed = False + best = None + for i in range(len(verts)): + p0, p1, p2 = verts[i - 1], verts[i], verts[(i + 1) % len(verts)] + d = p2 - p0 + n = np.hypot(*d) + if n == 0: + continue + dist = abs(d[0] * (p0[1] - p1[1]) - d[1] * (p0[0] - p1[0])) / n + if best is None or dist < best[0]: + best = (dist, i) + if best and best[0] < 3.5: + verts = np.delete(verts, best[1], axis=0) + changed = True + # The bevelled rims clip the sharp tips off the class masks; a short edge between two long + # ones is such a clipped tip, so extend the neighbours until they meet. + changed = True + while changed and len(verts) > 3: + changed = False + n = len(verts) + lengths = [np.hypot(*(verts[(i + 1) % n] - verts[i])) for i in range(n)] + i = int(np.argmin(lengths)) + if lengths[i] < 16: + a0, a1 = verts[i - 1], verts[i] + b0, b1 = verts[(i + 2) % n], verts[(i + 1) % n] + meet = lineMeet(a0, a1, b0, b1) + if meet is not None and np.hypot(*(meet - a1)) < 60: + verts[i] = meet + verts = np.delete(verts, (i + 1) % n, axis=0) + changed = True + # Push every edge outward so tiles reach their rims instead of stopping at the flat face. + n = len(verts) + area = 0.5 * sum(verts[i][0] * verts[(i + 1) % n][1] - verts[(i + 1) % n][0] * verts[i][1] for i in range(n)) + sign = 1.0 if area > 0 else -1.0 + grow = 4.0 + shifted = [] + for i in range(n): + a, b = verts[i], verts[(i + 1) % n] + d = b - a + d = d / np.hypot(*d) + normal = np.array([d[1], -d[0]]) * sign + shifted.append((a + normal * grow, b + normal * grow)) + result = [] + for i in range(n): + meet = lineMeet(*shifted[i - 1], *shifted[i]) + result.append(meet if meet is not None else verts[i]) + return np.array(result) + +def gradient(mask, verts): + ys, xs = np.nonzero(mask) + inner = ndimage.binary_erosion(mask, iterations=3) + if inner.sum() > 50: + ys, xs = np.nonzero(inner) + cols = im[ys, xs] + br = cols.mean(axis=1) + # Direction of the brightness trend across the tile. + A = np.column_stack([xs, ys, np.ones_like(xs)]).astype(float) + coef, *_ = np.linalg.lstsq(A, br, rcond=None) + d = np.array([coef[0], coef[1]]) + if np.hypot(*d) < 1e-6: + d = np.array([1.0, 0.0]) + d = d / np.hypot(*d) + proj = xs * d[0] + ys * d[1] + lo, hi = np.percentile(proj, 5), np.percentile(proj, 95) + c0 = cols[proj <= lo + (hi - lo) * 0.15].mean(axis=0) + c1 = cols[proj >= hi - (hi - lo) * 0.15].mean(axis=0) + centre = np.array([xs.mean(), ys.mean()]) + p0 = centre + d * (lo - proj.mean()) + p1 = centre + d * (hi - proj.mean()) + return p0, p1, c0, c1 + +def hexcol(c): + return '#%02x%02x%02x' % tuple(int(max(0, min(255, v))) for v in c) + +pieces = [] +taken = np.zeros_like(nonbg) +for name, mask, erode, minArea in (('dark', dark, 2, 300), ('champagne', champagne, 2, 300), ('glass', glass, 2, 300)): + for t in tiles(mask, erode, minArea, taken): + verts = polygon(t) + pieces.append((name, t, verts, gradient(t, verts))) +print('pieces', len(pieces), {n: sum(1 for p in pieces if p[0] == n) for n in ('dark', 'champagne', 'glass')}) + +# Bounds with a margin. +allv = np.vstack([p[2] for p in pieces]) +x0, y0 = np.floor(allv.min(axis=0)) - 8 +x1, y1 = np.ceil(allv.max(axis=0)) + 8 +svg = ['', + '' % (x0, y0, x1 - x0, y1 - y0, x1 - x0, y1 - y0), + ' ', + ' '] +for i, (name, mask, verts, (p0, p1, c0, c1)) in enumerate(pieces): + svg.append(' ' % (i, p0[0], p0[1], p1[0], p1[1], hexcol(c0), hexcol(c1))) +svg.append(' ') +# Brushed metal: horizontally stretched noise, clipped to the tile and blended in lightly. +svg.append(' ') +svg.append(' ') +svg.append(' Singe dragon') +svg.append(' ') +# Large tiles first so any small tile that sits on a neighbour (the eye) stays visible. +for i, (name, mask, verts, grad) in sorted(enumerate(pieces), key=lambda e: -e[1][1].sum()): + points = ' '.join('%.1f,%.1f' % (x, y) for x, y in verts) + if name == 'glass': + svg.append(' ' % (points, i)) + else: + rim = hexcol((np.maximum(grad[2], grad[3]) + 255) / 2) + svg.append(' ' % (points, i, rim)) +svg.append(' ') +svg.append('') +open(out, 'w').write('\n'.join(svg) + '\n') + +if preview: + pv = Image.new('RGB', (w, h), (200, 200, 200)) + pd = ImageDraw.Draw(pv) + for name, mask, verts, (p0, p1, c0, c1) in sorted(pieces, key=lambda e: -e[1].sum()): + mid = tuple(int(v) for v in (c0 + c1) / 2) + pd.polygon([tuple(v) for v in verts], fill=mid, outline=(90, 90, 90) if name != 'glass' else (170, 185, 178)) + pv.save(preview) + +if debug: + dbg = Image.open(src).convert('RGB') + draw = ImageDraw.Draw(dbg) + for name, mask, verts, grad in pieces: + colour = {'dark': (255, 0, 0), 'champagne': (0, 160, 0), 'glass': (0, 90, 255)}[name] + pts = [tuple(v) for v in verts] + [tuple(verts[0])] + draw.line(pts, fill=colour, width=2) + dbg.save(debug) + +if args.pieces: + data = {'width': w, 'height': h, 'bounds': [float(x0), float(y0), float(x1), float(y1)], 'pieces': []} + for name, mask, verts, (p0, p1, c0, c1) in pieces: + data['pieces'].append({'material': name, 'points': [[round(float(x), 2), round(float(y), 2)] for x, y in verts], 'colour0': [int(v) for v in c0], 'colour1': [int(v) for v in c1]}) + json.dump(data, open(args.pieces, 'w'), indent=1) diff --git a/util/traceText.py b/util/traceText.py new file mode 100644 index 000000000..ccf4f70be --- /dev/null +++ b/util/traceText.py @@ -0,0 +1,59 @@ +# Reconstructs assets/SingeText.jpeg as assets/SingeText.svg: thresholds the logotype, traces it +# with potrace into Bezier outlines, and writes a tightly cropped SVG with a transparent background. +# Usage: python3 util/traceText.py assets/SingeText.jpeg assets/SingeText.svg [potrace] [check.svg] +# Needs Pillow and potrace (https://potrace.sourceforge.net). check.svg keeps the full page so it +# can be rendered over the original for comparison. +import os +import re +import subprocess +import sys +import tempfile +from PIL import Image, ImageFilter + +src = sys.argv[1] +out = sys.argv[2] +potrace = sys.argv[3] if len(sys.argv) > 3 else 'potrace' +check = sys.argv[4] if len(sys.argv) > 4 else None + +# Trace at 4x so the anti-aliased edges of the JPEG turn into smooth curves rather than steps. +scale = 4 +BLUR = 3.5 +grey = Image.open(src).convert('L') +w, h = grey.size +big = grey.resize((w * scale, h * scale), Image.LANCZOS) +# A light blur removes JPEG ringing along the edges, which would otherwise trace as tiny wobbles. +big = big.filter(ImageFilter.GaussianBlur(BLUR)) +mono = big.point(lambda v: 255 if v >= 128 else 0).convert('1') + +work = tempfile.mkdtemp() +pbm = os.path.join(work, 'text.pbm') +mono.save(pbm) + +def trace(tight): + args = [potrace, pbm, '-s', '--flat', '-a', '1.0', '-O', '0.2', '-u', '1', '-t', str(4 * scale * scale), '-o', '-'] + if tight: + args.append('--tight') + svg = subprocess.run(args, check=True, capture_output=True, text=True).stdout + width = float(re.search(r'width="([\d.]+)pt"', svg).group(1)) + height = float(re.search(r'height="([\d.]+)pt"', svg).group(1)) + transform = re.search(r'transform="([^"]+)"', svg).group(1) + path = re.search(r']*\bd="([^"]+)"', svg, re.S).group(1) + path = re.sub(r'\s+', ' ', path).strip() + return width, height, transform, path + +def write(name, tight): + width, height, transform, path = trace(tight) + lines = ['', + '' % (width / scale, height / scale, width / scale, height / scale), + ' ', + ' Singe', + ' ' % (1.0 / scale, transform), + ' ' % path, + ' ', + ''] + open(name, 'w').write('\n'.join(lines) + '\n') + print('%s: %.0fx%.0f, %d bytes' % (name, width / scale, height / scale, os.path.getsize(name))) + +write(out, True) +if check: + write(check, False)