Bläddra i källkod

Updated spirv-cross.

Бранимир Караџић 6 år sedan
förälder
incheckning
3564e2a289
50 ändrade filer med 2741 tillägg och 643 borttagningar
  1. 14 0
      3rdparty/spirv-cross/CMakeLists.txt
  2. 13 18
      3rdparty/spirv-cross/README.md
  3. 1 0
      3rdparty/spirv-cross/build_glslang_spirv_tools.sh
  4. 9 3
      3rdparty/spirv-cross/checkout_glslang_spirv_tools.sh
  5. 192 184
      3rdparty/spirv-cross/main.cpp
  6. 13 0
      3rdparty/spirv-cross/reference/opt/shaders/legacy/fragment/fma.legacy.frag
  7. 42 0
      3rdparty/spirv-cross/reference/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp
  8. 2 1
      3rdparty/spirv-cross/reference/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp
  9. 52 0
      3rdparty/spirv-cross/reference/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag
  10. 30 0
      3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp
  11. 27 0
      3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp
  12. 42 0
      3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp.vk
  13. 33 0
      3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp
  14. 13 0
      3rdparty/spirv-cross/reference/shaders/legacy/fragment/fma.legacy.frag
  15. 131 0
      3rdparty/spirv-cross/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp
  16. 60 0
      3rdparty/spirv-cross/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag
  17. 55 0
      3rdparty/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp
  18. 54 0
      3rdparty/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp
  19. 131 0
      3rdparty/spirv-cross/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp
  20. 63 0
      3rdparty/spirv-cross/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp
  21. 11 0
      3rdparty/spirv-cross/shaders/legacy/fragment/fma.legacy.frag
  22. 2 2
      3rdparty/spirv-cross/spirv_cfg.cpp
  23. 7 7
      3rdparty/spirv-cross/spirv_cfg.hpp
  24. 111 132
      3rdparty/spirv-cross/spirv_common.hpp
  25. 2 2
      3rdparty/spirv-cross/spirv_cpp.cpp
  26. 3 4
      3rdparty/spirv-cross/spirv_cpp.hpp
  27. 65 20
      3rdparty/spirv-cross/spirv_cross.cpp
  28. 26 25
      3rdparty/spirv-cross/spirv_cross.hpp
  29. 26 26
      3rdparty/spirv-cross/spirv_cross_c.cpp
  30. 712 0
      3rdparty/spirv-cross/spirv_cross_containers.hpp
  31. 83 0
      3rdparty/spirv-cross/spirv_cross_error_handling.hpp
  32. 99 3
      3rdparty/spirv-cross/spirv_cross_parsed_ir.cpp
  33. 23 9
      3rdparty/spirv-cross/spirv_cross_parsed_ir.hpp
  34. 2 2
      3rdparty/spirv-cross/spirv_cross_util.cpp
  35. 2 1
      3rdparty/spirv-cross/spirv_cross_util.hpp
  36. 167 63
      3rdparty/spirv-cross/spirv_glsl.cpp
  37. 19 18
      3rdparty/spirv-cross/spirv_glsl.hpp
  38. 24 16
      3rdparty/spirv-cross/spirv_hlsl.cpp
  39. 4 5
      3rdparty/spirv-cross/spirv_hlsl.hpp
  40. 40 15
      3rdparty/spirv-cross/spirv_msl.cpp
  41. 6 5
      3rdparty/spirv-cross/spirv_msl.hpp
  42. 7 5
      3rdparty/spirv-cross/spirv_parser.cpp
  43. 2 3
      3rdparty/spirv-cross/spirv_parser.hpp
  44. 10 4
      3rdparty/spirv-cross/spirv_reflect.cpp
  45. 3 4
      3rdparty/spirv-cross/spirv_reflect.hpp
  46. 49 41
      3rdparty/spirv-cross/test_shaders.py
  47. 16 12
      3rdparty/spirv-cross/test_shaders.sh
  48. 226 0
      3rdparty/spirv-cross/tests-other/small_vector.cpp
  49. 16 12
      3rdparty/spirv-cross/update_test_shaders.sh
  50. 1 1
      tools/shaderc/shaderc_spirv.cpp

+ 14 - 0
3rdparty/spirv-cross/CMakeLists.txt

@@ -37,6 +37,7 @@ option(SPIRV_CROSS_SANITIZE_THREADS "Sanitize threads" OFF)
 option(SPIRV_CROSS_SANITIZE_UNDEFINED "Sanitize undefined" 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_NAMESPACE_OVERRIDE "" "Override the namespace used in the C++ API.")
+option(SPIRV_CROSS_FORCE_STL_TYPES "Force use of STL types instead of STL replacements in certain places. Might reduce performance." OFF)
 
 
 if(${CMAKE_GENERATOR} MATCHES "Makefile")
 if(${CMAKE_GENERATOR} MATCHES "Makefile")
 	if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
 	if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
@@ -52,6 +53,10 @@ if(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
 	set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
 	set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS)
 endif()
 endif()
 
 
+if(SPIRV_CROSS_FORCE_STL_TYPES)
+	set(spirv-compiler-defines ${spirv-compiler-defines} SPIRV_CROSS_FORCE_STL_TYPES)
+endif()
+
 if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
 if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
 	set(spirv-compiler-options ${spirv-compiler-options} -Wall -Wextra -Werror -Wshadow)
 	set(spirv-compiler-options ${spirv-compiler-options} -Wall -Wextra -Werror -Wshadow)
 
 
@@ -132,6 +137,8 @@ endmacro()
 set(spirv-cross-core-sources
 set(spirv-cross-core-sources
 		${CMAKE_CURRENT_SOURCE_DIR}/GLSL.std.450.h
 		${CMAKE_CURRENT_SOURCE_DIR}/GLSL.std.450.h
 		${CMAKE_CURRENT_SOURCE_DIR}/spirv_common.hpp
 		${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.hpp
 		${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.hpp
 		${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.hpp
 		${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.cpp
 		${CMAKE_CURRENT_SOURCE_DIR}/spirv_cross.cpp
@@ -415,11 +422,18 @@ if (SPIRV_CROSS_CLI)
 				add_executable(spirv-cross-c-api-test tests-other/c_api_test.c)
 				add_executable(spirv-cross-c-api-test tests-other/c_api_test.c)
 				target_link_libraries(spirv-cross-c-api-test spirv-cross-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}")
 				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}")
+
 				if (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang"))
 				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)
 					target_compile_options(spirv-cross-c-api-test PRIVATE -std=c89 -Wall -Wextra)
 				endif()
 				endif()
 				add_test(NAME spirv-cross-c-api-test
 				add_test(NAME spirv-cross-c-api-test
 						COMMAND $<TARGET_FILE:spirv-cross-c-api-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/c_api_test.spv)
 						COMMAND $<TARGET_FILE:spirv-cross-c-api-test> ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/c_api_test.spv)
+				add_test(NAME spirv-cross-small-vector-test
+						COMMAND $<TARGET_FILE:spirv-cross-small-vector-test>)
 				add_test(NAME spirv-cross-test
 				add_test(NAME spirv-cross-test
 						COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
 						COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/test_shaders.py --parallel
 						${spirv-cross-externals}
 						${spirv-cross-externals}

+ 13 - 18
3rdparty/spirv-cross/README.md

@@ -415,44 +415,39 @@ The reference files are stored inside the repository in order to be able to trac
 All pull requests should ensure that test output does not change unexpectedly. This can be tested with:
 All pull requests should ensure that test output does not change unexpectedly. This can be tested with:
 
 
 ```
 ```
-./test_shaders.py shaders || exit 1
-./test_shaders.py shaders --opt || exit 1
-./test_shaders.py shaders-no-opt || exit 1
-./test_shaders.py shaders-msl --msl || exit 1
-./test_shaders.py shaders-msl --msl --opt || exit 1
-./test_shaders.py shaders-msl-no-opt --msl || exit 1
-./test_shaders.py shaders-hlsl --hlsl || exit 1
-./test_shaders.py shaders-hlsl --hlsl --opt || exit 1
-./test_shaders.py shaders-hlsl-no-opt --hlsl || exit 1
-./test_shaders.py shaders-reflection --reflect || exit 1
+./checkout_glslang_spirv_tools.sh # Checks out glslang and SPIRV-Tools at a fixed revision which matches the reference output.
+                                  # NOTE: Some users have reported problems cloning from git:// paths. To use https:// instead pass in
+                                  # $ PROTOCOL=https ./checkout_glslang_spirv_tools.sh
+                                  # instead.
+./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.
 ```
 ```
 
 
-although there are a couple of convenience script for doing this:
+`./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:
 
 
 ```
 ```
-./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.
+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.
 However, when improving SPIRV-Cross there are of course legitimate cases where reference output should change.
 In these cases, run:
 In these cases, run:
 
 
 ```
 ```
-./update_test_shaders.sh
+./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.
 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.
 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`.
+See `checkout_glslang_spirv_tools.sh` which revisions are currently expected. The revisions change regularly.
 
 
 In short, the master branch should always be able to run `./test_shaders.py shaders` and friends without failure.
 In short, the master 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.
 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.
 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.
 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 method is compatible with MSVC.
+Travis CI runs the test suite with the CMake, by running `ctest`. This is a more straight-forward alternative to `./test_shaders.sh`.
 
 
 ### Licensing
 ### Licensing
 
 

+ 1 - 0
3rdparty/spirv-cross/build_glslang_spirv_tools.sh

@@ -1,6 +1,7 @@
 #!/bin/bash
 #!/bin/bash
 
 
 PROFILE=Release
 PROFILE=Release
+
 if [ ! -z $1 ]; then
 if [ ! -z $1 ]; then
 	PROFILE=$1
 	PROFILE=$1
 fi
 fi

+ 9 - 3
3rdparty/spirv-cross/checkout_glslang_spirv_tools.sh

@@ -4,6 +4,12 @@ GLSLANG_REV=ef807f4bc543e061f25dbbee6cb64dd5053b2adc
 SPIRV_TOOLS_REV=12e4a7b649e6fe28683de9fc352200c82948a1f0
 SPIRV_TOOLS_REV=12e4a7b649e6fe28683de9fc352200c82948a1f0
 SPIRV_HEADERS_REV=111a25e4ae45e2b4d7c18415e1d6884712b958c4
 SPIRV_HEADERS_REV=111a25e4ae45e2b4d7c18415e1d6884712b958c4
 
 
+if [ -z $PROTOCOL ]; then
+	PROTOCOL=git
+fi
+
+echo "Using protocol \"$PROTOCOL\" for checking out repositories. If this is problematic, try PROTOCOL=https $0."
+
 if [ -d external/glslang ]; then
 if [ -d external/glslang ]; then
 	echo "Updating glslang to revision $GLSLANG_REV."
 	echo "Updating glslang to revision $GLSLANG_REV."
 	cd external/glslang
 	cd external/glslang
@@ -13,7 +19,7 @@ else
 	echo "Cloning glslang revision $GLSLANG_REV."
 	echo "Cloning glslang revision $GLSLANG_REV."
 	mkdir -p external
 	mkdir -p external
 	cd external
 	cd external
-	git clone git://github.com/KhronosGroup/glslang.git
+	git clone $PROTOCOL://github.com/KhronosGroup/glslang.git
 	cd glslang
 	cd glslang
 	git checkout $GLSLANG_REV
 	git checkout $GLSLANG_REV
 fi
 fi
@@ -28,7 +34,7 @@ else
 	echo "Cloning SPIRV-Tools revision $SPIRV_TOOLS_REV."
 	echo "Cloning SPIRV-Tools revision $SPIRV_TOOLS_REV."
 	mkdir -p external
 	mkdir -p external
 	cd external
 	cd external
-	git clone git://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools
+	git clone $PROTOCOL://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools
 	cd spirv-tools
 	cd spirv-tools
 	git checkout $SPIRV_TOOLS_REV
 	git checkout $SPIRV_TOOLS_REV
 fi
 fi
@@ -39,7 +45,7 @@ if [ -d external/spirv-headers ]; then
 	git checkout $SPIRV_HEADERS_REV
 	git checkout $SPIRV_HEADERS_REV
 	cd ../..
 	cd ../..
 else
 else
-	git clone git://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers
+	git clone $PROTOCOL://github.com/KhronosGroup/SPIRV-Headers.git external/spirv-headers
 	cd external/spirv-headers
 	cd external/spirv-headers
 	git checkout $SPIRV_HEADERS_REV
 	git checkout $SPIRV_HEADERS_REV
 	cd ../..
 	cd ../..

+ 192 - 184
3rdparty/spirv-cross/main.cpp

@@ -221,7 +221,7 @@ static bool write_string_to_file(const char *path, const char *string)
 	return true;
 	return true;
 }
 }
 
 
-static void print_resources(const Compiler &compiler, const char *tag, const vector<Resource> &resources)
+static void print_resources(const Compiler &compiler, const char *tag, const SmallVector<Resource> &resources)
 {
 {
 	fprintf(stderr, "%s\n", tag);
 	fprintf(stderr, "%s\n", tag);
 	fprintf(stderr, "=============\n\n");
 	fprintf(stderr, "=============\n\n");
@@ -411,7 +411,7 @@ static void print_resources(const Compiler &compiler, const ShaderResources &res
 	print_resources(compiler, "acceleration structures", res.acceleration_structures);
 	print_resources(compiler, "acceleration structures", res.acceleration_structures);
 }
 }
 
 
-static void print_push_constant_resources(const Compiler &compiler, const vector<Resource> &res)
+static void print_push_constant_resources(const Compiler &compiler, const SmallVector<Resource> &res)
 {
 {
 	for (auto &block : res)
 	for (auto &block : res)
 	{
 	{
@@ -510,14 +510,14 @@ struct CLIArguments
 	bool msl_domain_lower_left = false;
 	bool msl_domain_lower_left = false;
 	bool msl_argument_buffers = false;
 	bool msl_argument_buffers = false;
 	bool glsl_emit_push_constant_as_ubo = false;
 	bool glsl_emit_push_constant_as_ubo = false;
-	vector<uint32_t> msl_discrete_descriptor_sets;
-	vector<PLSArg> pls_in;
-	vector<PLSArg> pls_out;
-	vector<Remap> remaps;
-	vector<string> extensions;
-	vector<VariableTypeRemap> variable_type_remaps;
-	vector<InterfaceVariableRename> interface_variable_renames;
-	vector<HLSLVertexAttributeRemap> hlsl_attr_remap;
+	SmallVector<uint32_t> msl_discrete_descriptor_sets;
+	SmallVector<PLSArg> pls_in;
+	SmallVector<PLSArg> pls_out;
+	SmallVector<Remap> remaps;
+	SmallVector<string> extensions;
+	SmallVector<VariableTypeRemap> variable_type_remaps;
+	SmallVector<InterfaceVariableRename> interface_variable_renames;
+	SmallVector<HLSLVertexAttributeRemap> hlsl_attr_remap;
 	string entry;
 	string entry;
 	string entry_stage;
 	string entry_stage;
 
 
@@ -527,7 +527,7 @@ struct CLIArguments
 		string new_name;
 		string new_name;
 		ExecutionModel execution_model;
 		ExecutionModel execution_model;
 	};
 	};
-	vector<Rename> entry_point_rename;
+	SmallVector<Rename> entry_point_rename;
 
 
 	uint32_t iterations = 1;
 	uint32_t iterations = 1;
 	bool cpp = false;
 	bool cpp = false;
@@ -595,7 +595,7 @@ static void print_help()
 	                "\n");
 	                "\n");
 }
 }
 
 
-static bool remap_generic(Compiler &compiler, const vector<Resource> &resources, const Remap &remap)
+static bool remap_generic(Compiler &compiler, const SmallVector<Resource> &resources, const Remap &remap)
 {
 {
 	auto itr =
 	auto itr =
 	    find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; });
 	    find_if(begin(resources), end(resources), [&remap](const Resource &res) { return res.name == remap.src_name; });
@@ -611,8 +611,8 @@ static bool remap_generic(Compiler &compiler, const vector<Resource> &resources,
 		return false;
 		return false;
 }
 }
 
 
-static vector<PlsRemap> remap_pls(const vector<PLSArg> &pls_variables, const vector<Resource> &resources,
-                                  const vector<Resource> *secondary_resources)
+static vector<PlsRemap> remap_pls(const SmallVector<PLSArg> &pls_variables, const SmallVector<Resource> &resources,
+                                  const SmallVector<Resource> *secondary_resources)
 {
 {
 	vector<PlsRemap> ret;
 	vector<PlsRemap> ret;
 
 
@@ -697,163 +697,11 @@ static ExecutionModel stage_to_execution_model(const std::string &stage)
 		SPIRV_CROSS_THROW("Invalid stage.");
 		SPIRV_CROSS_THROW("Invalid stage.");
 }
 }
 
 
-static int main_inner(int argc, char *argv[])
+static string compile_iteration(const CLIArguments &args, std::vector<uint32_t> spirv_file)
 {
 {
-	CLIArguments args;
-	CLICallbacks cbs;
-
-	cbs.add("--help", [](CLIParser &parser) {
-		print_help();
-		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("--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("--vulkan-semantics", [&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-discrete-descriptor-set",
-	        [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); });
-	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, 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(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({ move(src), 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({ move(var_name), 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, 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({ move(fmt), 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({ move(fmt), 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.default_handler = [&args](const char *value) { args.input = value; };
-	cbs.error_handler = [] { print_help(); };
-
-	CLIParser parser{ 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;
 	Parser spirv_parser(move(spirv_file));
 	Parser spirv_parser(move(spirv_file));
-
 	spirv_parser.parse();
 	spirv_parser.parse();
 
 
-	// Special case reflection because it has little to do with the path followed by code-outputting compilers
-	if (!args.reflect.empty())
-	{
-		CompilerReflection compiler(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;
-	}
-
 	unique_ptr<CompilerGLSL> compiler;
 	unique_ptr<CompilerGLSL> compiler;
 	bool combined_image_samplers = false;
 	bool combined_image_samplers = false;
 	bool build_dummy_sampler = false;
 	bool build_dummy_sampler = false;
@@ -929,7 +777,7 @@ static int main_inner(int argc, char *argv[])
 			if (entry_point.empty())
 			if (entry_point.empty())
 			{
 			{
 				fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str());
 				fprintf(stderr, "Could not find an entry point with stage: %s\n", args.entry_stage.c_str());
-				return EXIT_FAILURE;
+				exit(EXIT_FAILURE);
 			}
 			}
 		}
 		}
 		else
 		else
@@ -949,7 +797,7 @@ static int main_inner(int argc, char *argv[])
 			{
 			{
 				fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(),
 				fprintf(stderr, "Could not find an entry point %s with stage: %s\n", entry_point.c_str(),
 				        args.entry_stage.c_str());
 				        args.entry_stage.c_str());
-				return EXIT_FAILURE;
+				exit(EXIT_FAILURE);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -970,12 +818,12 @@ static int main_inner(int argc, char *argv[])
 		if (stage_count == 0)
 		if (stage_count == 0)
 		{
 		{
 			fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str());
 			fprintf(stderr, "There is no entry point with name: %s\n", entry_point.c_str());
-			return EXIT_FAILURE;
+			exit(EXIT_FAILURE);
 		}
 		}
 		else if (stage_count > 1)
 		else if (stage_count > 1)
 		{
 		{
 			fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str());
 			fprintf(stderr, "There is more than one entry point with name: %s. Use --stage.\n", entry_point.c_str());
-			return EXIT_FAILURE;
+			exit(EXIT_FAILURE);
 		}
 		}
 	}
 	}
 
 
@@ -986,7 +834,7 @@ static int main_inner(int argc, char *argv[])
 	{
 	{
 		fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
 		fprintf(stderr, "Didn't specify GLSL version and SPIR-V did not specify language.\n");
 		print_help();
 		print_help();
-		return EXIT_FAILURE;
+		exit(EXIT_FAILURE);
 	}
 	}
 
 
 	CompilerGLSL::Options opts = compiler->get_common_options();
 	CompilerGLSL::Options opts = compiler->get_common_options();
@@ -1015,7 +863,7 @@ static int main_inner(int argc, char *argv[])
 			if (args.shader_model < 30)
 			if (args.shader_model < 30)
 			{
 			{
 				fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n");
 				fprintf(stderr, "Shader model earlier than 30 (3.0) not supported.\n");
-				return EXIT_FAILURE;
+				exit(EXIT_FAILURE);
 			}
 			}
 
 
 			hlsl_opts.shader_model = args.shader_model;
 			hlsl_opts.shader_model = args.shader_model;
@@ -1095,7 +943,7 @@ static int main_inner(int argc, char *argv[])
 		else
 		else
 		{
 		{
 			fprintf(stderr, "error at --rename-interface-variable <in|out> ...\n");
 			fprintf(stderr, "error at --rename-interface-variable <in|out> ...\n");
-			return EXIT_FAILURE;
+			exit(EXIT_FAILURE);
 		}
 		}
 	}
 	}
 
 
@@ -1132,22 +980,182 @@ static int main_inner(int argc, char *argv[])
 		}
 		}
 	}
 	}
 
 
-	string glsl;
-	for (uint32_t i = 0; i < args.iterations; i++)
+	if (args.hlsl)
 	{
 	{
-		if (args.hlsl)
-		{
-			for (auto &remap : args.hlsl_attr_remap)
-				static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
-		}
+		for (auto &remap : args.hlsl_attr_remap)
+			static_cast<CompilerHLSL *>(compiler.get())->add_vertex_attribute_remap(remap);
+	}
+
+	return compiler->compile();
+}
+
+static int main_inner(int argc, char *argv[])
+{
+	CLIArguments args;
+	CLICallbacks cbs;
 
 
-		glsl = compiler->compile();
+	cbs.add("--help", [](CLIParser &parser) {
+		print_help();
+		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("--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("--vulkan-semantics", [&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-discrete-descriptor-set",
+	        [&args](CLIParser &parser) { args.msl_discrete_descriptor_sets.push_back(parser.next_uint()); });
+	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, 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(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({ move(src), 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({ move(var_name), 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, 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({ move(fmt), 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({ move(fmt), 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.default_handler = [&args](const char *value) { args.input = value; };
+	cbs.error_handler = [] { print_help(); };
+
+	CLIParser parser{ 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(move(spirv_file));
+		spirv_parser.parse();
+
+		CompilerReflection compiler(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, move(spirv_file));
+	else
+	{
+		for (unsigned i = 0; i < args.iterations; i++)
+			compiled_output = compile_iteration(args, spirv_file);
 	}
 	}
 
 
 	if (args.output)
 	if (args.output)
-		write_string_to_file(args.output, glsl.c_str());
+		write_string_to_file(args.output, compiled_output.c_str());
 	else
 	else
-		printf("%s", glsl.c_str());
+		printf("%s", compiled_output.c_str());
 
 
 	return EXIT_SUCCESS;
 	return EXIT_SUCCESS;
 }
 }

+ 13 - 0
3rdparty/spirv-cross/reference/opt/shaders/legacy/fragment/fma.legacy.frag

@@ -0,0 +1,13 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+varying highp vec4 vA;
+varying highp vec4 vB;
+varying highp vec4 vC;
+
+void main()
+{
+    gl_FragData[0] = vA * vB + vC;
+}
+

+ 42 - 0
3rdparty/spirv-cross/reference/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp

@@ -0,0 +1,42 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct SSBO
+{
+    int s32;
+    uint u32;
+    short s16;
+    ushort u16;
+    float f32;
+};
+
+kernel void main0(device SSBO& _4 [[buffer(0)]])
+{
+    int _29 = _4.s32;
+    uint _30 = _4.u32;
+    short _31 = _4.s16;
+    ushort _32 = _4.u16;
+    float _33 = _4.f32;
+    _4.s32 = int(_31);
+    _4.u32 = uint(_31);
+    _4.s32 = int(short(_32));
+    _4.u32 = uint(short(_32));
+    _4.u32 = uint(ushort(_31));
+    _4.u32 = uint(_32);
+    _4.s16 = short(_29);
+    _4.u16 = ushort(_29);
+    _4.s16 = short(_30);
+    _4.u16 = ushort(_30);
+    _4.u16 = ushort(_29);
+    _4.u16 = ushort(_30);
+    _4.f32 = float(_31);
+    _4.f32 = float(short(_32));
+    _4.f32 = float(ushort(_31));
+    _4.f32 = float(_32);
+    _4.s16 = short(_33);
+    _4.u16 = ushort(short(_33));
+    _4.u16 = ushort(_33);
+}
+

+ 2 - 1
3rdparty/spirv-cross/reference/shaders-msl-no-opt/asm/comp/variable-pointers.asm.comp

@@ -46,10 +46,11 @@ kernel void main0(device foo& buf [[buffer(0)]], constant bar& cb [[buffer(3)]],
     threadgroup int* cur = stgsm;
     threadgroup int* cur = stgsm;
     device int* _73;
     device int* _73;
     _73 = &buf.a[0u];
     _73 = &buf.a[0u];
+    threadgroup int* _76;
     int _77;
     int _77;
     for (;;)
     for (;;)
     {
     {
-        threadgroup int* _76 = cur;
+        _76 = cur;
         _77 = *_73;
         _77 = *_73;
         if (_77 != 0)
         if (_77 != 0)
         {
         {

+ 52 - 0
3rdparty/spirv-cross/reference/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag

@@ -0,0 +1,52 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+constant float _21 = {};
+
+struct main0_out
+{
+    float4 gl_Position [[position]];
+};
+
+// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
+template<typename T, uint N>
+void spvArrayCopyFromStack1(thread T (&dst)[N], thread const T (&src)[N])
+{
+    for (uint i = 0; i < N; dst[i] = src[i], i++);
+}
+
+template<typename T, uint N>
+void spvArrayCopyFromConstant1(thread T (&dst)[N], constant T (&src)[N])
+{
+    for (uint i = 0; i < N; dst[i] = src[i], i++);
+}
+
+vertex main0_out main0()
+{
+    main0_out out = {};
+    float _23[2];
+    for (int _25 = 0; _25 < 2; )
+    {
+        _23[_25] = 0.0;
+        _25++;
+        continue;
+    }
+    float _31[2];
+    spvArrayCopyFromStack1(_31, _23);
+    float _37;
+    if (as_type<uint>(3.0) != 0u)
+    {
+        _37 = _31[0];
+    }
+    else
+    {
+        _37 = _21;
+    }
+    out.gl_Position = float4(0.0, 0.0, 0.0, _37);
+    return out;
+}
+

+ 30 - 0
3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp

@@ -0,0 +1,30 @@
+#version 450
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+layout(binding = 0, std430) buffer SSBO
+{
+    int values[];
+} _4;
+
+void main()
+{
+    int _17 = 0;
+    for (;;)
+    {
+        if (_17 < 100)
+        {
+            int _24 = _4.values[_17];
+            _4.values[_24] = _17;
+            int _26 = _24 + 1;
+            int _18 = _4.values[_26];
+            _4.values[_17] = _18;
+            _17 = _18;
+            continue;
+        }
+        else
+        {
+            break;
+        }
+    }
+}
+

+ 27 - 0
3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp

@@ -0,0 +1,27 @@
+#version 450
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+layout(binding = 0, std430) buffer SSBO
+{
+    int values[];
+} _4;
+
+void main()
+{
+    int _17 = 0;
+    for (;;)
+    {
+        if (_17 < 100)
+        {
+            int _24 = _4.values[_17];
+            _4.values[_24] = _17;
+            _17 = _4.values[_24 + 1];
+            continue;
+        }
+        else
+        {
+            break;
+        }
+    }
+}
+

+ 42 - 0
3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp.vk

@@ -0,0 +1,42 @@
+#version 450
+#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require
+#extension GL_EXT_shader_16bit_storage : require
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+layout(set = 0, binding = 0, std430) buffer SSBO
+{
+    int s32;
+    uint u32;
+    int16_t s16;
+    uint16_t u16;
+    float f32;
+} _4;
+
+void main()
+{
+    int _29 = _4.s32;
+    uint _30 = _4.u32;
+    int16_t _31 = _4.s16;
+    uint16_t _32 = _4.u16;
+    float _33 = _4.f32;
+    _4.s32 = int(_31);
+    _4.u32 = uint(_31);
+    _4.s32 = int(int16_t(_32));
+    _4.u32 = uint(int16_t(_32));
+    _4.u32 = uint(uint16_t(_31));
+    _4.u32 = uint(_32);
+    _4.s16 = int16_t(_29);
+    _4.u16 = uint16_t(_29);
+    _4.s16 = int16_t(_30);
+    _4.u16 = uint16_t(_30);
+    _4.u16 = uint16_t(_29);
+    _4.u16 = uint16_t(_30);
+    _4.f32 = float(_31);
+    _4.f32 = float(int16_t(_32));
+    _4.f32 = float(uint16_t(_31));
+    _4.f32 = float(_32);
+    _4.s16 = int16_t(_33);
+    _4.u16 = uint16_t(int16_t(_33));
+    _4.u16 = uint16_t(_33);
+}
+

+ 33 - 0
3rdparty/spirv-cross/reference/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp

@@ -0,0 +1,33 @@
+#version 450
+#extension GL_ARB_gpu_shader_int64 : require
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+#ifndef SPIRV_CROSS_CONSTANT_ID_0
+#define SPIRV_CROSS_CONSTANT_ID_0 1
+#endif
+const int ConstantInt = SPIRV_CROSS_CONSTANT_ID_0;
+#ifndef SPIRV_CROSS_CONSTANT_ID_1
+#define SPIRV_CROSS_CONSTANT_ID_1 2u
+#endif
+const uint ConstantUint = SPIRV_CROSS_CONSTANT_ID_1;
+const int64_t ConstantInt64_1 = int64_t(ConstantInt);
+const int64_t ConstantInt64_2 = int64_t(int(ConstantUint));
+const uint64_t ConstantUint64_1 = uint64_t(ConstantInt);
+const uint64_t ConstantUint64_2 = uint64_t(int(ConstantUint));
+const int64_t _20 = (ConstantInt64_1 + ConstantInt64_2);
+const uint64_t _21 = (ConstantUint64_1 + ConstantUint64_2);
+const int _22 = int(_20);
+const uint _23 = uint(_21);
+
+layout(binding = 0, std430) buffer SSBO
+{
+    int s64;
+    uint u64;
+} _4;
+
+void main()
+{
+    _4.s64 = _22;
+    _4.u64 = _23;
+}
+

+ 13 - 0
3rdparty/spirv-cross/reference/shaders/legacy/fragment/fma.legacy.frag

@@ -0,0 +1,13 @@
+#version 100
+precision mediump float;
+precision highp int;
+
+varying highp vec4 vA;
+varying highp vec4 vB;
+varying highp vec4 vC;
+
+void main()
+{
+    gl_FragData[0] = vA * vB + vC;
+}
+

+ 131 - 0
3rdparty/spirv-cross/shaders-msl-no-opt/asm/comp/arithmetic-conversion-signs.asm.comp

@@ -0,0 +1,131 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 76
+; Schema: 0
+               OpCapability Shader
+               OpCapability Int16
+               OpCapability StorageBuffer16BitAccess
+               OpExtension "SPV_KHR_16bit_storage"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_int16"
+               OpName %main "main"
+               OpName %SSBO "SSBO"
+               OpMemberName %SSBO 0 "s32"
+               OpMemberName %SSBO 1 "u32"
+               OpMemberName %SSBO 2 "s16"
+               OpMemberName %SSBO 3 "u16"
+               OpMemberName %SSBO 4 "f32"
+               OpName %_ ""
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpMemberDecorate %SSBO 1 Offset 4
+               OpMemberDecorate %SSBO 2 Offset 8
+               OpMemberDecorate %SSBO 3 Offset 10
+               OpMemberDecorate %SSBO 4 Offset 12
+               OpDecorate %SSBO BufferBlock
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %_ Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+      %short = OpTypeInt 16 1
+     %ushort = OpTypeInt 16 0
+      %float = OpTypeFloat 32
+       %SSBO = OpTypeStruct %int %uint %short %ushort %float
+%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO
+          %_ = OpVariable %_ptr_Uniform_SSBO Uniform
+      %int_2 = OpConstant %int 2
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%_ptr_Uniform_short = OpTypePointer Uniform %short
+      %int_1 = OpConstant %int 1
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+      %int_3 = OpConstant %int 3
+%_ptr_Uniform_ushort = OpTypePointer Uniform %ushort
+      %int_4 = OpConstant %int 4
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %ptr_s32 = OpAccessChain %_ptr_Uniform_int %_ %int_0
+         %ptr_u32 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
+         %ptr_s16 = OpAccessChain %_ptr_Uniform_short %_ %int_2
+         %ptr_u16 = OpAccessChain %_ptr_Uniform_ushort %_ %int_3
+         %ptr_f32 = OpAccessChain %_ptr_Uniform_float %_ %int_4
+         %s32 = OpLoad %int %ptr_s32
+         %u32 = OpLoad %uint %ptr_u32
+         %s16 = OpLoad %short %ptr_s16
+         %u16 = OpLoad %ushort %ptr_u16
+		 %f32 = OpLoad %float %ptr_f32
+
+		; Sign-extend
+		 %s16_to_s32_signed = OpSConvert %int %s16
+		 OpStore %ptr_s32 %s16_to_s32_signed
+		 %s16_to_u32_signed = OpSConvert %uint %s16
+		 OpStore %ptr_u32 %s16_to_u32_signed
+
+		 %u16_to_s32_signed = OpSConvert %int %u16
+		 OpStore %ptr_s32 %u16_to_s32_signed
+		 %u16_to_u32_signed = OpSConvert %uint %u16
+		 OpStore %ptr_u32 %u16_to_u32_signed
+
+		; Zero-extend
+		; Result must be unsigned for OpUConvert.
+		 ;%s16_to_s32_unsigned = OpUConvert %int %s16
+		 ;OpStore %ptr_s32 %s16_to_s32_unsigned
+		 %s16_to_u32_unsigned = OpUConvert %uint %s16
+		 OpStore %ptr_u32 %s16_to_u32_unsigned
+
+		 ;%u16_to_s32_unsigned = OpUConvert %int %u16
+		 ;OpStore %ptr_s32 %u16_to_s32_unsigned
+		 %u16_to_u32_unsigned = OpUConvert %uint %u16
+		 OpStore %ptr_u32 %u16_to_u32_unsigned
+
+		; Truncate (SConvert == UConvert)
+		 %s32_to_s16_signed = OpSConvert %short %s32
+		 OpStore %ptr_s16 %s32_to_s16_signed
+		 %s32_to_u16_signed = OpSConvert %ushort %s32
+		 OpStore %ptr_u16 %s32_to_u16_signed
+
+		 %u32_to_s16_signed = OpSConvert %short %u32
+		 OpStore %ptr_s16 %u32_to_s16_signed
+		 %u32_to_u16_signed = OpSConvert %ushort %u32
+		 OpStore %ptr_u16 %u32_to_u16_signed
+
+		 ;%s32_to_s16_unsigned = OpUConvert %short %s32
+		 ;OpStore %ptr_s16 %s32_to_s16_unsigned
+		 %s32_to_u16_unsigned = OpUConvert %ushort %s32
+		 OpStore %ptr_u16 %s32_to_u16_unsigned
+
+		 ;%u32_to_s16_unsigned = OpUConvert %short %u32
+		 ;OpStore %ptr_s16 %u32_to_s16_unsigned
+		 %u32_to_u16_unsigned = OpUConvert %ushort %u32
+		 OpStore %ptr_u16 %u32_to_u16_unsigned
+
+		; SToF
+		%s16_to_f32_signed = OpConvertSToF %float %s16
+		OpStore %ptr_f32 %s16_to_f32_signed
+		%u16_to_f32_signed = OpConvertSToF %float %u16
+		OpStore %ptr_f32 %u16_to_f32_signed
+		%s16_to_f32_unsigned = OpConvertUToF %float %s16
+		OpStore %ptr_f32 %s16_to_f32_unsigned
+		%u16_to_f32_unsigned = OpConvertUToF %float %u16
+		OpStore %ptr_f32 %u16_to_f32_unsigned
+
+		; FToS
+		%f32_to_s16_signed = OpConvertFToS %short %f32
+		OpStore %ptr_s16 %f32_to_s16_signed
+		%f32_to_u16_signed = OpConvertFToS %ushort %f32
+		OpStore %ptr_u16 %f32_to_u16_signed
+
+		; FToU
+		%f32_to_u16_unsigned = OpConvertFToU %ushort %f32
+		OpStore %ptr_u16 %f32_to_u16_unsigned
+		; Result must be unsigned for FToU, so don't bother testing that.
+
+               OpReturn
+               OpFunctionEnd

+ 60 - 0
3rdparty/spirv-cross/shaders-msl-no-opt/asm/vert/op-load-forced-temporary-array.asm.frag

@@ -0,0 +1,60 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Google spiregg; 0
+; Bound: 39
+; Schema: 0
+               OpCapability Shader
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint Vertex %vs_main "main" %gl_Position
+               OpSource HLSL 600
+               OpName %vs_main "vs_main"
+               OpDecorate %gl_Position BuiltIn Position
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+      %int_2 = OpConstant %int 2
+      %float = OpTypeFloat 32
+    %float_0 = OpConstant %float 0
+      %int_1 = OpConstant %int 1
+    %float_3 = OpConstant %float 3
+       %uint = OpTypeInt 32 0
+     %uint_0 = OpConstant %uint 0
+    %v4float = OpTypeVector %float 4
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+       %void = OpTypeVoid
+         %15 = OpTypeFunction %void
+     %uint_2 = OpConstant %uint 2
+%_arr_float_uint_2 = OpTypeArray %float %uint_2
+%_ptr_Function__arr_float_uint_2 = OpTypePointer Function %_arr_float_uint_2
+%_ptr_Function_float = OpTypePointer Function %float
+       %bool = OpTypeBool
+%gl_Position = OpVariable %_ptr_Output_v4float Output
+         %21 = OpUndef %float
+    %vs_main = OpFunction %void None %15
+         %22 = OpLabel
+         %23 = OpVariable %_ptr_Function__arr_float_uint_2 Function
+               OpBranch %24
+         %24 = OpLabel
+         %25 = OpPhi %int %int_0 %22 %26 %27
+         %28 = OpSLessThan %bool %25 %int_2
+               OpLoopMerge %29 %27 None
+               OpBranchConditional %28 %27 %29
+         %27 = OpLabel
+         %30 = OpAccessChain %_ptr_Function_float %23 %25
+               OpStore %30 %float_0
+         %26 = OpIAdd %int %25 %int_1
+               OpBranch %24
+         %29 = OpLabel
+         %31 = OpLoad %_arr_float_uint_2 %23
+         %32 = OpBitcast %uint %float_3
+         %33 = OpINotEqual %bool %32 %uint_0
+               OpSelectionMerge %34 None
+               OpBranchConditional %33 %35 %34
+         %35 = OpLabel
+         %36 = OpCompositeExtract %float %31 0
+               OpBranch %34
+         %34 = OpLabel
+         %37 = OpPhi %float %21 %29 %36 %35
+         %38 = OpCompositeConstruct %v4float %float_0 %float_0 %float_0 %37
+               OpStore %gl_Position %38
+               OpReturn
+               OpFunctionEnd

+ 55 - 0
3rdparty/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body-2.asm.comp

@@ -0,0 +1,55 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 52
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %SSBO "SSBO"
+               OpMemberName %SSBO 0 "values"
+               OpName %_ ""
+               OpDecorate %_runtimearr_int ArrayStride 4
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %SSBO BufferBlock
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %_ Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %int_100 = OpConstant %int 100
+       %bool = OpTypeBool
+%_runtimearr_int = OpTypeRuntimeArray %int
+       %SSBO = OpTypeStruct %_runtimearr_int
+%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO
+          %_ = OpVariable %_ptr_Uniform_SSBO Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+      %int_1 = OpConstant %int 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpBranch %32
+         %32 = OpLabel
+         %51 = OpPhi %int %int_0 %5 %49 %loop_continue
+         %38 = OpSLessThan %bool %51 %int_100
+               OpLoopMerge %loop_merge %loop_continue None
+               OpBranchConditional %38 %loop_body %loop_merge
+         %loop_body = OpLabel
+         %40 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %51
+		 OpBranch %loop_continue
+         %loop_continue = OpLabel
+		 %41 = OpLoad %int %40
+         %44 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %41
+               OpStore %44 %51
+         %47 = OpIAdd %int %41 %int_1
+         %48 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %47
+         %49 = OpLoad %int %48
+         OpStore %40 %49
+               OpBranch %32
+         %loop_merge = OpLabel
+               OpReturn
+               OpFunctionEnd

+ 54 - 0
3rdparty/spirv-cross/shaders-no-opt/asm/comp/access-chain-dominator-in-loop-body.asm.comp

@@ -0,0 +1,54 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 52
+; Schema: 0
+               OpCapability Shader
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpName %main "main"
+               OpName %SSBO "SSBO"
+               OpMemberName %SSBO 0 "values"
+               OpName %_ ""
+               OpDecorate %_runtimearr_int ArrayStride 4
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpDecorate %SSBO BufferBlock
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %_ Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+      %int_0 = OpConstant %int 0
+    %int_100 = OpConstant %int 100
+       %bool = OpTypeBool
+%_runtimearr_int = OpTypeRuntimeArray %int
+       %SSBO = OpTypeStruct %_runtimearr_int
+%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO
+          %_ = OpVariable %_ptr_Uniform_SSBO Uniform
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+      %int_1 = OpConstant %int 1
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+               OpBranch %32
+         %32 = OpLabel
+         %51 = OpPhi %int %int_0 %5 %49 %loop_continue
+         %38 = OpSLessThan %bool %51 %int_100
+               OpLoopMerge %loop_merge %loop_continue None
+               OpBranchConditional %38 %loop_body %loop_merge
+         %loop_body = OpLabel
+         %40 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %51
+		 OpBranch %loop_continue
+         %loop_continue = OpLabel
+         %41 = OpLoad %int %40
+         %44 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %41
+               OpStore %44 %51
+         %47 = OpIAdd %int %41 %int_1
+         %48 = OpAccessChain %_ptr_Uniform_int %_ %int_0 %47
+         %49 = OpLoad %int %48
+               OpBranch %32
+         %loop_merge = OpLabel
+               OpReturn
+               OpFunctionEnd

+ 131 - 0
3rdparty/spirv-cross/shaders-no-opt/asm/comp/arithmetic-conversion-signs.asm.nocompat.vk.comp

@@ -0,0 +1,131 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 76
+; Schema: 0
+               OpCapability Shader
+               OpCapability Int16
+               OpCapability StorageBuffer16BitAccess
+               OpExtension "SPV_KHR_16bit_storage"
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_EXT_shader_explicit_arithmetic_types_int16"
+               OpName %main "main"
+               OpName %SSBO "SSBO"
+               OpMemberName %SSBO 0 "s32"
+               OpMemberName %SSBO 1 "u32"
+               OpMemberName %SSBO 2 "s16"
+               OpMemberName %SSBO 3 "u16"
+               OpMemberName %SSBO 4 "f32"
+               OpName %_ ""
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpMemberDecorate %SSBO 1 Offset 4
+               OpMemberDecorate %SSBO 2 Offset 8
+               OpMemberDecorate %SSBO 3 Offset 10
+               OpMemberDecorate %SSBO 4 Offset 12
+               OpDecorate %SSBO BufferBlock
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %_ Binding 0
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+      %short = OpTypeInt 16 1
+     %ushort = OpTypeInt 16 0
+      %float = OpTypeFloat 32
+       %SSBO = OpTypeStruct %int %uint %short %ushort %float
+%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO
+          %_ = OpVariable %_ptr_Uniform_SSBO Uniform
+      %int_2 = OpConstant %int 2
+      %int_0 = OpConstant %int 0
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+%_ptr_Uniform_short = OpTypePointer Uniform %short
+      %int_1 = OpConstant %int 1
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+      %int_3 = OpConstant %int 3
+%_ptr_Uniform_ushort = OpTypePointer Uniform %ushort
+      %int_4 = OpConstant %int 4
+%_ptr_Uniform_float = OpTypePointer Uniform %float
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %ptr_s32 = OpAccessChain %_ptr_Uniform_int %_ %int_0
+         %ptr_u32 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
+         %ptr_s16 = OpAccessChain %_ptr_Uniform_short %_ %int_2
+         %ptr_u16 = OpAccessChain %_ptr_Uniform_ushort %_ %int_3
+         %ptr_f32 = OpAccessChain %_ptr_Uniform_float %_ %int_4
+         %s32 = OpLoad %int %ptr_s32
+         %u32 = OpLoad %uint %ptr_u32
+         %s16 = OpLoad %short %ptr_s16
+         %u16 = OpLoad %ushort %ptr_u16
+		 %f32 = OpLoad %float %ptr_f32
+
+		; Sign-extend
+		 %s16_to_s32_signed = OpSConvert %int %s16
+		 OpStore %ptr_s32 %s16_to_s32_signed
+		 %s16_to_u32_signed = OpSConvert %uint %s16
+		 OpStore %ptr_u32 %s16_to_u32_signed
+
+		 %u16_to_s32_signed = OpSConvert %int %u16
+		 OpStore %ptr_s32 %u16_to_s32_signed
+		 %u16_to_u32_signed = OpSConvert %uint %u16
+		 OpStore %ptr_u32 %u16_to_u32_signed
+
+		; Zero-extend
+		; Result must be unsigned for OpUConvert.
+		 ;%s16_to_s32_unsigned = OpUConvert %int %s16
+		 ;OpStore %ptr_s32 %s16_to_s32_unsigned
+		 %s16_to_u32_unsigned = OpUConvert %uint %s16
+		 OpStore %ptr_u32 %s16_to_u32_unsigned
+
+		 ;%u16_to_s32_unsigned = OpUConvert %int %u16
+		 ;OpStore %ptr_s32 %u16_to_s32_unsigned
+		 %u16_to_u32_unsigned = OpUConvert %uint %u16
+		 OpStore %ptr_u32 %u16_to_u32_unsigned
+
+		; Truncate (SConvert == UConvert)
+		 %s32_to_s16_signed = OpSConvert %short %s32
+		 OpStore %ptr_s16 %s32_to_s16_signed
+		 %s32_to_u16_signed = OpSConvert %ushort %s32
+		 OpStore %ptr_u16 %s32_to_u16_signed
+
+		 %u32_to_s16_signed = OpSConvert %short %u32
+		 OpStore %ptr_s16 %u32_to_s16_signed
+		 %u32_to_u16_signed = OpSConvert %ushort %u32
+		 OpStore %ptr_u16 %u32_to_u16_signed
+
+		 ;%s32_to_s16_unsigned = OpUConvert %short %s32
+		 ;OpStore %ptr_s16 %s32_to_s16_unsigned
+		 %s32_to_u16_unsigned = OpUConvert %ushort %s32
+		 OpStore %ptr_u16 %s32_to_u16_unsigned
+
+		 ;%u32_to_s16_unsigned = OpUConvert %short %u32
+		 ;OpStore %ptr_s16 %u32_to_s16_unsigned
+		 %u32_to_u16_unsigned = OpUConvert %ushort %u32
+		 OpStore %ptr_u16 %u32_to_u16_unsigned
+
+		; SToF
+		%s16_to_f32_signed = OpConvertSToF %float %s16
+		OpStore %ptr_f32 %s16_to_f32_signed
+		%u16_to_f32_signed = OpConvertSToF %float %u16
+		OpStore %ptr_f32 %u16_to_f32_signed
+		%s16_to_f32_unsigned = OpConvertUToF %float %s16
+		OpStore %ptr_f32 %s16_to_f32_unsigned
+		%u16_to_f32_unsigned = OpConvertUToF %float %u16
+		OpStore %ptr_f32 %u16_to_f32_unsigned
+
+		; FToS
+		%f32_to_s16_signed = OpConvertFToS %short %f32
+		OpStore %ptr_s16 %f32_to_s16_signed
+		%f32_to_u16_signed = OpConvertFToS %ushort %f32
+		OpStore %ptr_u16 %f32_to_u16_signed
+
+		; FToU
+		%f32_to_u16_unsigned = OpConvertFToU %ushort %f32
+		OpStore %ptr_u16 %f32_to_u16_unsigned
+		; Result must be unsigned for FToU, so don't bother testing that.
+
+               OpReturn
+               OpFunctionEnd

+ 63 - 0
3rdparty/spirv-cross/shaders-no-opt/asm/comp/spec-constant-op-convert-sign.asm.comp

@@ -0,0 +1,63 @@
+; SPIR-V
+; Version: 1.0
+; Generator: Khronos Glslang Reference Front End; 7
+; Bound: 30
+; Schema: 0
+               OpCapability Shader
+               OpCapability Int64
+          %1 = OpExtInstImport "GLSL.std.450"
+               OpMemoryModel Logical GLSL450
+               OpEntryPoint GLCompute %main "main"
+               OpExecutionMode %main LocalSize 1 1 1
+               OpSource GLSL 450
+               OpSourceExtension "GL_ARB_gpu_shader_int64"
+               OpName %main "main"
+               OpName %SSBO "SSBO"
+               OpMemberName %SSBO 0 "s64"
+               OpMemberName %SSBO 1 "u64"
+               OpName %_ ""
+               OpName %ConstantInt "ConstantInt"
+               OpName %ConstantInt64_1 "ConstantInt64_1"
+               OpName %ConstantUint "ConstantUint"
+               OpName %ConstantInt64_2 "ConstantInt64_2"
+               OpName %ConstantUint64_1 "ConstantUint64_1"
+               OpName %ConstantUint64_2 "ConstantUint64_2"
+               OpMemberDecorate %SSBO 0 Offset 0
+               OpMemberDecorate %SSBO 1 Offset 4
+               OpDecorate %SSBO BufferBlock
+               OpDecorate %_ DescriptorSet 0
+               OpDecorate %_ Binding 0
+               OpDecorate %ConstantInt SpecId 0
+               OpDecorate %ConstantUint SpecId 1
+       %void = OpTypeVoid
+          %3 = OpTypeFunction %void
+        %int = OpTypeInt 32 1
+       %uint = OpTypeInt 32 0
+       %long = OpTypeInt 64 1
+      %ulong = OpTypeInt 64 0
+       %SSBO = OpTypeStruct %int %uint
+%_ptr_Uniform_SSBO = OpTypePointer Uniform %SSBO
+          %_ = OpVariable %_ptr_Uniform_SSBO Uniform
+      %int_0 = OpConstant %int 0
+    %ulong_0 = OpConstant %ulong 0
+%ConstantInt = OpSpecConstant %int 1
+%ConstantUint = OpSpecConstant %uint 2
+%ConstantInt64_1 = OpSpecConstantOp %long SConvert %ConstantInt
+%ConstantInt64_2 = OpSpecConstantOp %long SConvert %ConstantUint
+%ConstantUint64_1 = OpSpecConstantOp %ulong SConvert %ConstantInt
+%ConstantUint64_2 = OpSpecConstantOp %ulong SConvert %ConstantUint
+         %added_long = OpSpecConstantOp %long IAdd %ConstantInt64_1 %ConstantInt64_2
+         %added_ulong = OpSpecConstantOp %ulong IAdd %ConstantUint64_1 %ConstantUint64_2
+		 %trunc_long = OpSpecConstantOp %int SConvert %added_long
+		 %trunc_ulong = OpSpecConstantOp %uint SConvert %added_ulong
+%_ptr_Uniform_int = OpTypePointer Uniform %int
+      %int_1 = OpConstant %int 1
+%_ptr_Uniform_uint = OpTypePointer Uniform %uint
+       %main = OpFunction %void None %3
+          %5 = OpLabel
+         %22 = OpAccessChain %_ptr_Uniform_int %_ %int_0
+               OpStore %22 %trunc_long
+         %29 = OpAccessChain %_ptr_Uniform_uint %_ %int_1
+               OpStore %29 %trunc_ulong
+               OpReturn
+               OpFunctionEnd

+ 11 - 0
3rdparty/spirv-cross/shaders/legacy/fragment/fma.legacy.frag

@@ -0,0 +1,11 @@
+#version 450
+
+layout(location = 0) in vec4 vA;
+layout(location = 1) in vec4 vB;
+layout(location = 2) in vec4 vC;
+layout(location = 0) out vec4 FragColor;
+
+void main()
+{
+   FragColor = fma(vA, vB, vC);
+}

+ 2 - 2
3rdparty/spirv-cross/spirv_cfg.cpp

@@ -143,7 +143,7 @@ void CFG::build_post_order_visit_order()
 
 
 void CFG::add_branch(uint32_t from, uint32_t to)
 void CFG::add_branch(uint32_t from, uint32_t to)
 {
 {
-	const auto add_unique = [](vector<uint32_t> &l, uint32_t value) {
+	const auto add_unique = [](SmallVector<uint32_t> &l, uint32_t value) {
 		auto itr = find(begin(l), end(l), value);
 		auto itr = find(begin(l), end(l), value);
 		if (itr == end(l))
 		if (itr == end(l))
 			l.push_back(value);
 			l.push_back(value);
@@ -223,4 +223,4 @@ void DominatorBuilder::lift_continue_block_dominator()
 	if (back_edge_dominator)
 	if (back_edge_dominator)
 		dominator = cfg.get_function().entry_block;
 		dominator = cfg.get_function().entry_block;
 }
 }
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE

+ 7 - 7
3rdparty/spirv-cross/spirv_cfg.hpp

@@ -63,7 +63,7 @@ public:
 
 
 	uint32_t find_common_dominator(uint32_t a, uint32_t b) const;
 	uint32_t find_common_dominator(uint32_t a, uint32_t b) const;
 
 
-	const std::vector<uint32_t> &get_preceding_edges(uint32_t block) const
+	const SmallVector<uint32_t> &get_preceding_edges(uint32_t block) const
 	{
 	{
 		auto itr = preceding_edges.find(block);
 		auto itr = preceding_edges.find(block);
 		if (itr != std::end(preceding_edges))
 		if (itr != std::end(preceding_edges))
@@ -72,7 +72,7 @@ public:
 			return empty_vector;
 			return empty_vector;
 	}
 	}
 
 
-	const std::vector<uint32_t> &get_succeeding_edges(uint32_t block) const
+	const SmallVector<uint32_t> &get_succeeding_edges(uint32_t block) const
 	{
 	{
 		auto itr = succeeding_edges.find(block);
 		auto itr = succeeding_edges.find(block);
 		if (itr != std::end(succeeding_edges))
 		if (itr != std::end(succeeding_edges))
@@ -111,12 +111,12 @@ private:
 
 
 	Compiler &compiler;
 	Compiler &compiler;
 	const SPIRFunction &func;
 	const SPIRFunction &func;
-	std::unordered_map<uint32_t, std::vector<uint32_t>> preceding_edges;
-	std::unordered_map<uint32_t, std::vector<uint32_t>> succeeding_edges;
+	std::unordered_map<uint32_t, SmallVector<uint32_t>> preceding_edges;
+	std::unordered_map<uint32_t, SmallVector<uint32_t>> succeeding_edges;
 	std::unordered_map<uint32_t, uint32_t> immediate_dominators;
 	std::unordered_map<uint32_t, uint32_t> immediate_dominators;
 	std::unordered_map<uint32_t, VisitOrder> visit_order;
 	std::unordered_map<uint32_t, VisitOrder> visit_order;
-	std::vector<uint32_t> post_order;
-	std::vector<uint32_t> empty_vector;
+	SmallVector<uint32_t> post_order;
+	SmallVector<uint32_t> empty_vector;
 
 
 	void add_branch(uint32_t from, uint32_t to);
 	void add_branch(uint32_t from, uint32_t to);
 	void build_post_order_visit_order();
 	void build_post_order_visit_order();
@@ -144,6 +144,6 @@ private:
 	const CFG &cfg;
 	const CFG &cfg;
 	uint32_t dominator = 0;
 	uint32_t dominator = 0;
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 111 - 132
3rdparty/spirv-cross/spirv_common.hpp

@@ -18,22 +18,8 @@
 #define SPIRV_CROSS_COMMON_HPP
 #define SPIRV_CROSS_COMMON_HPP
 
 
 #include "spirv.hpp"
 #include "spirv.hpp"
-
-#include <algorithm>
-#include <cstdio>
-#include <cstring>
-#include <functional>
-#include <memory>
-#include <sstream>
-#include <stack>
-#include <stdexcept>
-#include <stdint.h>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
+#include "spirv_cross_containers.hpp"
+#include "spirv_cross_error_handling.hpp"
 
 
 // A bit crude, but allows projects which embed SPIRV-Cross statically to
 // A bit crude, but allows projects which embed SPIRV-Cross statically to
 // effectively hide all the symbols from other projects.
 // effectively hide all the symbols from other projects.
@@ -53,68 +39,16 @@
 
 
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
-
-#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
-#ifndef _MSC_VER
-[[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)
-	{
-	}
-};
-
-#define SPIRV_CROSS_THROW(x) throw CompilerError(x)
-#endif
-
-//#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
-
-// 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 inner
 namespace inner
 {
 {
 template <typename T>
 template <typename T>
-void join_helper(std::ostringstream &stream, T &&t)
+void join_helper(StringStream<> &stream, T &&t)
 {
 {
 	stream << std::forward<T>(t);
 	stream << std::forward<T>(t);
 }
 }
 
 
 template <typename T, typename... Ts>
 template <typename T, typename... Ts>
-void join_helper(std::ostringstream &stream, T &&t, Ts &&... ts)
+void join_helper(StringStream<> &stream, T &&t, Ts &&... ts)
 {
 {
 	stream << std::forward<T>(t);
 	stream << std::forward<T>(t);
 	join_helper(stream, std::forward<Ts>(ts)...);
 	join_helper(stream, std::forward<Ts>(ts)...);
@@ -217,7 +151,7 @@ public:
 
 
 		// Need to enforce an order here for reproducible results,
 		// Need to enforce an order here for reproducible results,
 		// but hitting this path should happen extremely rarely, so having this slow path is fine.
 		// but hitting this path should happen extremely rarely, so having this slow path is fine.
-		std::vector<uint32_t> bits;
+		SmallVector<uint32_t> bits;
 		bits.reserve(higher.size());
 		bits.reserve(higher.size());
 		for (auto &v : higher)
 		for (auto &v : higher)
 			bits.push_back(v);
 			bits.push_back(v);
@@ -244,21 +178,21 @@ private:
 template <typename... Ts>
 template <typename... Ts>
 std::string join(Ts &&... ts)
 std::string join(Ts &&... ts)
 {
 {
-	std::ostringstream stream;
+	StringStream<> stream;
 	inner::join_helper(stream, std::forward<Ts>(ts)...);
 	inner::join_helper(stream, std::forward<Ts>(ts)...);
 	return stream.str();
 	return stream.str();
 }
 }
 
 
-inline std::string merge(const std::vector<std::string> &list)
+inline std::string merge(const SmallVector<std::string> &list)
 {
 {
-	std::string s;
+	StringStream<> stream;
 	for (auto &elem : list)
 	for (auto &elem : list)
 	{
 	{
-		s += elem;
+		stream << elem;
 		if (&elem != &list.back())
 		if (&elem != &list.back())
-			s += ", ";
+			stream << ", ";
 	}
 	}
-	return s;
+	return stream.str();
 }
 }
 
 
 // Make sure we don't accidentally call this with float or doubles with SFINAE.
 // Make sure we don't accidentally call this with float or doubles with SFINAE.
@@ -340,15 +274,14 @@ struct Instruction
 struct IVariant
 struct IVariant
 {
 {
 	virtual ~IVariant() = default;
 	virtual ~IVariant() = default;
-	virtual std::unique_ptr<IVariant> clone() = 0;
-
+	virtual IVariant *clone(ObjectPoolBase *pool) = 0;
 	uint32_t self = 0;
 	uint32_t self = 0;
 };
 };
 
 
-#define SPIRV_CROSS_DECLARE_CLONE(T)                    \
-	std::unique_ptr<IVariant> clone() override          \
-	{                                                   \
-		return std::unique_ptr<IVariant>(new T(*this)); \
+#define SPIRV_CROSS_DECLARE_CLONE(T)                                \
+	IVariant *clone(ObjectPoolBase *pool) override                  \
+	{                                                               \
+		return static_cast<ObjectPool<T> *>(pool)->allocate(*this); \
 	}
 	}
 
 
 enum Types
 enum Types
@@ -421,7 +354,7 @@ struct SPIRConstantOp : IVariant
 	}
 	}
 
 
 	spv::Op opcode;
 	spv::Op opcode;
-	std::vector<uint32_t> arguments;
+	SmallVector<uint32_t> arguments;
 	uint32_t basetype;
 	uint32_t basetype;
 
 
 	SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp)
 	SPIRV_CROSS_DECLARE_CLONE(SPIRConstantOp)
@@ -469,14 +402,14 @@ struct SPIRType : IVariant
 	uint32_t columns = 1;
 	uint32_t columns = 1;
 
 
 	// Arrays, support array of arrays by having a vector of array sizes.
 	// Arrays, support array of arrays by having a vector of array sizes.
-	std::vector<uint32_t> array;
+	SmallVector<uint32_t> array;
 
 
 	// Array elements can be either specialization constants or specialization ops.
 	// Array elements can be either specialization constants or specialization ops.
 	// This array determines how to interpret the array size.
 	// This array determines how to interpret the array size.
 	// If an element is true, the element is a literal,
 	// If an element is true, the element is a literal,
 	// otherwise, it's an expression, which must be resolved on demand.
 	// otherwise, it's an expression, which must be resolved on demand.
 	// The actual size is not really known until runtime.
 	// The actual size is not really known until runtime.
-	std::vector<bool> array_size_literal;
+	SmallVector<bool> array_size_literal;
 
 
 	// Pointers
 	// Pointers
 	// Keep track of how many pointer layers we have.
 	// Keep track of how many pointer layers we have.
@@ -485,7 +418,7 @@ struct SPIRType : IVariant
 
 
 	spv::StorageClass storage = spv::StorageClassGeneric;
 	spv::StorageClass storage = spv::StorageClassGeneric;
 
 
-	std::vector<uint32_t> member_types;
+	SmallVector<uint32_t> member_types;
 
 
 	struct ImageType
 	struct ImageType
 	{
 	{
@@ -556,7 +489,7 @@ struct SPIREntryPoint
 	uint32_t self = 0;
 	uint32_t self = 0;
 	std::string name;
 	std::string name;
 	std::string orig_name;
 	std::string orig_name;
-	std::vector<uint32_t> interface_variables;
+	SmallVector<uint32_t> interface_variables;
 
 
 	Bitset flags;
 	Bitset flags;
 	struct
 	struct
@@ -610,11 +543,11 @@ struct SPIRExpression : IVariant
 	bool access_chain = false;
 	bool access_chain = false;
 
 
 	// A list of expressions which this expression depends on.
 	// A list of expressions which this expression depends on.
-	std::vector<uint32_t> expression_dependencies;
+	SmallVector<uint32_t> expression_dependencies;
 
 
 	// By reading this expression, we implicitly read these expressions as well.
 	// 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.
 	// Used by access chain Store and Load since we read multiple expressions in this case.
-	std::vector<uint32_t> implied_read_expressions;
+	SmallVector<uint32_t> implied_read_expressions;
 
 
 	SPIRV_CROSS_DECLARE_CLONE(SPIRExpression)
 	SPIRV_CROSS_DECLARE_CLONE(SPIRExpression)
 };
 };
@@ -632,7 +565,7 @@ struct SPIRFunctionPrototype : IVariant
 	}
 	}
 
 
 	uint32_t return_type;
 	uint32_t return_type;
-	std::vector<uint32_t> parameter_types;
+	SmallVector<uint32_t> parameter_types;
 
 
 	SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype)
 	SPIRV_CROSS_DECLARE_CLONE(SPIRFunctionPrototype)
 };
 };
@@ -716,7 +649,7 @@ struct SPIRBlock : IVariant
 	uint32_t false_block = 0;
 	uint32_t false_block = 0;
 	uint32_t default_block = 0;
 	uint32_t default_block = 0;
 
 
-	std::vector<Instruction> ops;
+	SmallVector<Instruction> ops;
 
 
 	struct Phi
 	struct Phi
 	{
 	{
@@ -726,22 +659,22 @@ struct SPIRBlock : IVariant
 	};
 	};
 
 
 	// Before entering this block flush out local variables to magical "phi" variables.
 	// Before entering this block flush out local variables to magical "phi" variables.
-	std::vector<Phi> phi_variables;
+	SmallVector<Phi> phi_variables;
 
 
 	// Declare these temporaries before beginning the block.
 	// Declare these temporaries before beginning the block.
 	// Used for handling complex continue blocks which have side effects.
 	// Used for handling complex continue blocks which have side effects.
-	std::vector<std::pair<uint32_t, uint32_t>> declare_temporary;
+	SmallVector<std::pair<uint32_t, uint32_t>> declare_temporary;
 
 
 	// Declare these temporaries, but only conditionally if this block turns out to be
 	// Declare these temporaries, but only conditionally if this block turns out to be
 	// a complex loop header.
 	// a complex loop header.
-	std::vector<std::pair<uint32_t, uint32_t>> potential_declare_temporary;
+	SmallVector<std::pair<uint32_t, uint32_t>> potential_declare_temporary;
 
 
 	struct Case
 	struct Case
 	{
 	{
 		uint32_t value;
 		uint32_t value;
 		uint32_t block;
 		uint32_t block;
 	};
 	};
-	std::vector<Case> cases;
+	SmallVector<Case> cases;
 
 
 	// If we have tried to optimize code for this block but failed,
 	// If we have tried to optimize code for this block but failed,
 	// keep track of this.
 	// keep track of this.
@@ -759,17 +692,17 @@ struct SPIRBlock : IVariant
 
 
 	// All access to these variables are dominated by this block,
 	// All access to these variables are dominated by this block,
 	// so before branching anywhere we need to make sure that we declare these variables.
 	// so before branching anywhere we need to make sure that we declare these variables.
-	std::vector<uint32_t> dominated_variables;
+	SmallVector<uint32_t> dominated_variables;
 
 
 	// These are variables which should be declared in a for loop header, if we
 	// These are variables which should be declared in a for loop header, if we
 	// fail to use a classic for-loop,
 	// fail to use a classic for-loop,
 	// we remove these variables, and fall back to regular variables outside the loop.
 	// we remove these variables, and fall back to regular variables outside the loop.
-	std::vector<uint32_t> loop_variables;
+	SmallVector<uint32_t> loop_variables;
 
 
 	// Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or
 	// Some expressions are control-flow dependent, i.e. any instruction which relies on derivatives or
 	// sub-group-like operations.
 	// sub-group-like operations.
 	// Make sure that we only use these expressions in the original block.
 	// Make sure that we only use these expressions in the original block.
-	std::vector<uint32_t> invalidate_expressions;
+	SmallVector<uint32_t> invalidate_expressions;
 
 
 	SPIRV_CROSS_DECLARE_CLONE(SPIRBlock)
 	SPIRV_CROSS_DECLARE_CLONE(SPIRBlock)
 };
 };
@@ -822,16 +755,16 @@ struct SPIRFunction : IVariant
 
 
 	uint32_t return_type;
 	uint32_t return_type;
 	uint32_t function_type;
 	uint32_t function_type;
-	std::vector<Parameter> arguments;
+	SmallVector<Parameter> arguments;
 
 
 	// Can be used by backends to add magic arguments.
 	// Can be used by backends to add magic arguments.
 	// Currently used by combined image/sampler implementation.
 	// Currently used by combined image/sampler implementation.
 
 
-	std::vector<Parameter> shadow_arguments;
-	std::vector<uint32_t> local_variables;
+	SmallVector<Parameter> shadow_arguments;
+	SmallVector<uint32_t> local_variables;
 	uint32_t entry_block = 0;
 	uint32_t entry_block = 0;
-	std::vector<uint32_t> blocks;
-	std::vector<CombinedImageSamplerParameter> combined_parameters;
+	SmallVector<uint32_t> blocks;
+	SmallVector<CombinedImageSamplerParameter> combined_parameters;
 
 
 	void add_local_variable(uint32_t id)
 	void add_local_variable(uint32_t id)
 	{
 	{
@@ -847,17 +780,19 @@ struct SPIRFunction : IVariant
 	// Hooks to be run when the function returns.
 	// Hooks to be run when the function returns.
 	// Mostly used for lowering internal data structures onto flattened structures.
 	// Mostly used for lowering internal data structures onto flattened structures.
 	// Need to defer this, because they might rely on things which change during compilation.
 	// Need to defer this, because they might rely on things which change during compilation.
-	std::vector<std::function<void()>> fixup_hooks_out;
+	// Intentionally not a small vector, this one is rare, and std::function can be large.
+	Vector<std::function<void()>> fixup_hooks_out;
 
 
 	// Hooks to be run when the function begins.
 	// Hooks to be run when the function begins.
 	// Mostly used for populating internal data structures from flattened structures.
 	// Mostly used for populating internal data structures from flattened structures.
 	// Need to defer this, because they might rely on things which change during compilation.
 	// Need to defer this, because they might rely on things which change during compilation.
-	std::vector<std::function<void()>> fixup_hooks_in;
+	// Intentionally not a small vector, this one is rare, and std::function can be large.
+	Vector<std::function<void()>> fixup_hooks_in;
 
 
 	// On function entry, make sure to copy a constant array into thread addr space to work around
 	// 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
 	// the case where we are passing a constant array by value to a function on backends which do not
 	// consider arrays value types.
 	// consider arrays value types.
-	std::vector<uint32_t> constant_arrays_needed_on_stack;
+	SmallVector<uint32_t> constant_arrays_needed_on_stack;
 
 
 	bool active = false;
 	bool active = false;
 	bool flush_undeclared = true;
 	bool flush_undeclared = true;
@@ -901,7 +836,7 @@ struct SPIRAccessChain : IVariant
 
 
 	// By reading this expression, we implicitly read these expressions as well.
 	// 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.
 	// Used by access chain Store and Load since we read multiple expressions in this case.
-	std::vector<uint32_t> implied_read_expressions;
+	SmallVector<uint32_t> implied_read_expressions;
 
 
 	SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain)
 	SPIRV_CROSS_DECLARE_CLONE(SPIRAccessChain)
 };
 };
@@ -928,7 +863,7 @@ struct SPIRVariable : IVariant
 	uint32_t initializer = 0;
 	uint32_t initializer = 0;
 	uint32_t basevariable = 0;
 	uint32_t basevariable = 0;
 
 
-	std::vector<uint32_t> dereference_chain;
+	SmallVector<uint32_t> dereference_chain;
 	bool compat_builtin = false;
 	bool compat_builtin = false;
 
 
 	// If a variable is shadowed, we only statically assign to it
 	// If a variable is shadowed, we only statically assign to it
@@ -939,7 +874,7 @@ struct SPIRVariable : IVariant
 	uint32_t static_expression = 0;
 	uint32_t static_expression = 0;
 
 
 	// Temporaries which can remain forwarded as long as this variable is not modified.
 	// Temporaries which can remain forwarded as long as this variable is not modified.
-	std::vector<uint32_t> dependees;
+	SmallVector<uint32_t> dependees;
 	bool forwardable = true;
 	bool forwardable = true;
 
 
 	bool deferred_declaration = false;
 	bool deferred_declaration = false;
@@ -1178,7 +1113,7 @@ struct SPIRConstant : IVariant
 	    : constant_type(constant_type_)
 	    : constant_type(constant_type_)
 	    , specialization(specialized)
 	    , specialization(specialized)
 	{
 	{
-		subconstants.insert(end(subconstants), elements, elements + num_elements);
+		subconstants.insert(std::end(subconstants), elements, elements + num_elements);
 		specialization = specialized;
 		specialization = specialized;
 	}
 	}
 
 
@@ -1247,7 +1182,7 @@ struct SPIRConstant : IVariant
 	bool is_used_as_lut = false;
 	bool is_used_as_lut = false;
 
 
 	// For composites which are constant arrays, etc.
 	// For composites which are constant arrays, etc.
-	std::vector<uint32_t> subconstants;
+	SmallVector<uint32_t> subconstants;
 
 
 	// Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant,
 	// Non-Vulkan GLSL, HLSL and sometimes MSL emits defines for each specialization constant,
 	// and uses them to initialize the constant. This allows the user
 	// and uses them to initialize the constant. This allows the user
@@ -1258,11 +1193,25 @@ struct SPIRConstant : IVariant
 	SPIRV_CROSS_DECLARE_CLONE(SPIRConstant)
 	SPIRV_CROSS_DECLARE_CLONE(SPIRConstant)
 };
 };
 
 
+// Variants have a very specific allocation scheme.
+struct ObjectPoolGroup
+{
+	std::unique_ptr<ObjectPoolBase> pools[TypeCount];
+};
+
 class Variant
 class Variant
 {
 {
 public:
 public:
-	// MSVC 2013 workaround, we shouldn't need these constructors.
-	Variant() = default;
+	explicit Variant(ObjectPoolGroup *group_)
+	    : group(group_)
+	{
+	}
+
+	~Variant()
+	{
+		if (holder)
+			group->pools[type]->free_opaque(holder);
+	}
 
 
 	// Marking custom move constructor as noexcept is important.
 	// Marking custom move constructor as noexcept is important.
 	Variant(Variant &&other) SPIRV_CROSS_NOEXCEPT
 	Variant(Variant &&other) SPIRV_CROSS_NOEXCEPT
@@ -1270,19 +1219,23 @@ public:
 		*this = std::move(other);
 		*this = std::move(other);
 	}
 	}
 
 
-	Variant(const Variant &variant)
-	{
-		*this = variant;
-	}
+	// 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.
 	// Marking custom move constructor as noexcept is important.
 	Variant &operator=(Variant &&other) SPIRV_CROSS_NOEXCEPT
 	Variant &operator=(Variant &&other) SPIRV_CROSS_NOEXCEPT
 	{
 	{
 		if (this != &other)
 		if (this != &other)
 		{
 		{
-			holder = std::move(other.holder);
+			if (holder)
+				group->pools[type]->free_opaque(holder);
+			holder = other.holder;
+			group = other.group;
 			type = other.type;
 			type = other.type;
 			allow_type_rewrite = other.allow_type_rewrite;
 			allow_type_rewrite = other.allow_type_rewrite;
+
+			other.holder = nullptr;
 			other.type = TypeNone;
 			other.type = TypeNone;
 		}
 		}
 		return *this;
 		return *this;
@@ -1293,29 +1246,52 @@ public:
 	// This should never happen.
 	// This should never happen.
 	Variant &operator=(const Variant &other)
 	Variant &operator=(const Variant &other)
 	{
 	{
+//#define SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
 #ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
 #ifdef SPIRV_CROSS_COPY_CONSTRUCTOR_SANITIZE
 		abort();
 		abort();
 #endif
 #endif
 		if (this != &other)
 		if (this != &other)
 		{
 		{
-			holder.reset();
+			if (holder)
+				group->pools[type]->free_opaque(holder);
+
 			if (other.holder)
 			if (other.holder)
-				holder = other.holder->clone();
+				holder = other.holder->clone(group->pools[other.type].get());
+			else
+				holder = nullptr;
+
 			type = other.type;
 			type = other.type;
 			allow_type_rewrite = other.allow_type_rewrite;
 			allow_type_rewrite = other.allow_type_rewrite;
 		}
 		}
 		return *this;
 		return *this;
 	}
 	}
 
 
-	void set(std::unique_ptr<IVariant> val, Types new_type)
+	void set(IVariant *val, Types new_type)
 	{
 	{
-		holder = std::move(val);
+		if (holder)
+			group->pools[type]->free_opaque(holder);
+		holder = nullptr;
+
 		if (!allow_type_rewrite && type != TypeNone && type != new_type)
 		if (!allow_type_rewrite && type != TypeNone && type != new_type)
+		{
+			if (val)
+				group->pools[new_type]->free_opaque(val);
 			SPIRV_CROSS_THROW("Overwriting a variant with new type.");
 			SPIRV_CROSS_THROW("Overwriting a variant with new type.");
+		}
+
+		holder = val;
 		type = new_type;
 		type = new_type;
 		allow_type_rewrite = false;
 		allow_type_rewrite = false;
 	}
 	}
 
 
+	template <typename T, typename... Ts>
+	T *allocate_and_set(Types new_type, Ts &&... ts)
+	{
+		T *val = static_cast<ObjectPool<T> &>(*group->pools[new_type]).allocate(std::forward<Ts>(ts)...);
+		set(val, new_type);
+		return val;
+	}
+
 	template <typename T>
 	template <typename T>
 	T &get()
 	T &get()
 	{
 	{
@@ -1323,7 +1299,7 @@ public:
 			SPIRV_CROSS_THROW("nullptr");
 			SPIRV_CROSS_THROW("nullptr");
 		if (static_cast<Types>(T::type) != type)
 		if (static_cast<Types>(T::type) != type)
 			SPIRV_CROSS_THROW("Bad cast");
 			SPIRV_CROSS_THROW("Bad cast");
-		return *static_cast<T *>(holder.get());
+		return *static_cast<T *>(holder);
 	}
 	}
 
 
 	template <typename T>
 	template <typename T>
@@ -1333,7 +1309,7 @@ public:
 			SPIRV_CROSS_THROW("nullptr");
 			SPIRV_CROSS_THROW("nullptr");
 		if (static_cast<Types>(T::type) != type)
 		if (static_cast<Types>(T::type) != type)
 			SPIRV_CROSS_THROW("Bad cast");
 			SPIRV_CROSS_THROW("Bad cast");
-		return *static_cast<const T *>(holder.get());
+		return *static_cast<const T *>(holder);
 	}
 	}
 
 
 	Types get_type() const
 	Types get_type() const
@@ -1353,7 +1329,9 @@ public:
 
 
 	void reset()
 	void reset()
 	{
 	{
-		holder.reset();
+		if (holder)
+			group->pools[type]->free_opaque(holder);
+		holder = nullptr;
 		type = TypeNone;
 		type = TypeNone;
 	}
 	}
 
 
@@ -1363,7 +1341,8 @@ public:
 	}
 	}
 
 
 private:
 private:
-	std::unique_ptr<IVariant> holder;
+	ObjectPoolGroup *group = nullptr;
+	IVariant *holder = nullptr;
 	Types type = TypeNone;
 	Types type = TypeNone;
 	bool allow_type_rewrite = false;
 	bool allow_type_rewrite = false;
 };
 };
@@ -1383,9 +1362,7 @@ const T &variant_get(const Variant &var)
 template <typename T, typename... P>
 template <typename T, typename... P>
 T &variant_set(Variant &var, P &&... args)
 T &variant_set(Variant &var, P &&... args)
 {
 {
-	auto uptr = std::unique_ptr<T>(new T(std::forward<P>(args)...));
-	auto ptr = uptr.get();
-	var.set(std::move(uptr), static_cast<Types>(T::type));
+	auto *ptr = var.allocate_and_set<T>(static_cast<Types>(T::type), std::forward<P>(args)...);
 	return *ptr;
 	return *ptr;
 }
 }
 
 
@@ -1430,7 +1407,9 @@ struct Meta
 	};
 	};
 
 
 	Decoration decoration;
 	Decoration decoration;
-	std::vector<Decoration> members;
+
+	// Intentionally not a SmallVector. Decoration is large and somewhat rare.
+	Vector<Decoration> members;
 
 
 	std::unordered_map<uint32_t, uint32_t> decoration_word_offset;
 	std::unordered_map<uint32_t, uint32_t> decoration_word_offset;
 
 
@@ -1529,6 +1508,6 @@ static inline bool opcode_is_sign_invariant(spv::Op opcode)
 		return false;
 		return false;
 	}
 	}
 }
 }
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 2 - 2
3rdparty/spirv-cross/spirv_cpp.cpp

@@ -334,7 +334,7 @@ string CompilerCPP::compile()
 		reset();
 		reset();
 
 
 		// Move constructor for this type is broken on GCC 4.9 ...
 		// Move constructor for this type is broken on GCC 4.9 ...
-		buffer = unique_ptr<ostringstream>(new ostringstream());
+		buffer.reset();
 
 
 		emit_header();
 		emit_header();
 		emit_resources();
 		emit_resources();
@@ -355,7 +355,7 @@ string CompilerCPP::compile()
 	// Entry point in CPP is always main() for the time being.
 	// Entry point in CPP is always main() for the time being.
 	get_entry_point().name = "main";
 	get_entry_point().name = "main";
 
 
-	return buffer->str();
+	return buffer.str();
 }
 }
 
 
 void CompilerCPP::emit_c_linkage()
 void CompilerCPP::emit_c_linkage()

+ 3 - 4
3rdparty/spirv-cross/spirv_cpp.hpp

@@ -19,7 +19,6 @@
 
 
 #include "spirv_glsl.hpp"
 #include "spirv_glsl.hpp"
 #include <utility>
 #include <utility>
-#include <vector>
 
 
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
@@ -27,7 +26,7 @@ class CompilerCPP : public CompilerGLSL
 {
 {
 public:
 public:
 	explicit CompilerCPP(std::vector<uint32_t> spirv_)
 	explicit CompilerCPP(std::vector<uint32_t> spirv_)
-	    : CompilerGLSL(move(spirv_))
+	    : CompilerGLSL(std::move(spirv_))
 	{
 	{
 	}
 	}
 
 
@@ -75,13 +74,13 @@ private:
 
 
 	std::string argument_decl(const SPIRFunction::Parameter &arg);
 	std::string argument_decl(const SPIRFunction::Parameter &arg);
 
 
-	std::vector<std::string> resource_registrations;
+	SmallVector<std::string> resource_registrations;
 	std::string impl_type;
 	std::string impl_type;
 	std::string resource_type;
 	std::string resource_type;
 	uint32_t shared_counter = 0;
 	uint32_t shared_counter = 0;
 
 
 	std::string interface_name;
 	std::string interface_name;
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 65 - 20
3rdparty/spirv-cross/spirv_cross.cpp

@@ -1897,9 +1897,9 @@ bool Compiler::BufferAccessHandler::handle(Op opcode, const uint32_t *args, uint
 	return true;
 	return true;
 }
 }
 
 
-std::vector<BufferRange> Compiler::get_active_buffer_ranges(uint32_t id) const
+SmallVector<BufferRange> Compiler::get_active_buffer_ranges(uint32_t id) const
 {
 {
-	std::vector<BufferRange> ranges;
+	SmallVector<BufferRange> ranges;
 	BufferAccessHandler handler(*this, ranges, id);
 	BufferAccessHandler handler(*this, ranges, id);
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
 	return ranges;
 	return ranges;
@@ -2126,9 +2126,9 @@ void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_exp
 	e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps));
 	e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps));
 }
 }
 
 
-vector<EntryPoint> Compiler::get_entry_points_and_stages() const
+SmallVector<EntryPoint> Compiler::get_entry_points_and_stages() const
 {
 {
-	vector<EntryPoint> entries;
+	SmallVector<EntryPoint> entries;
 	for (auto &entry : ir.entry_points)
 	for (auto &entry : ir.entry_points)
 		entries.push_back({ entry.second.orig_name, entry.second.model });
 		entries.push_back({ entry.second.orig_name, entry.second.model });
 	return entries;
 	return entries;
@@ -2715,9 +2715,9 @@ void Compiler::build_combined_image_samplers()
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
 }
 }
 
 
-vector<SpecializationConstant> Compiler::get_specialization_constants() const
+SmallVector<SpecializationConstant> Compiler::get_specialization_constants() const
 {
 {
-	vector<SpecializationConstant> spec_consts;
+	SmallVector<SpecializationConstant> spec_consts;
 	ir.for_each_typed_id<SPIRConstant>([&](uint32_t, const SPIRConstant &c) {
 	ir.for_each_typed_id<SPIRConstant>([&](uint32_t, const SPIRConstant &c) {
 		if (c.specialization && has_decoration(c.self, DecorationSpecId))
 		if (c.specialization && has_decoration(c.self, DecorationSpecId))
 			spec_consts.push_back({ c.self, get_decoration(c.self, DecorationSpecId) });
 			spec_consts.push_back({ c.self, get_decoration(c.self, DecorationSpecId) });
@@ -2874,6 +2874,9 @@ void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBl
 
 
 void Compiler::AnalyzeVariableScopeAccessHandler::notify_variable_access(uint32_t id, uint32_t block)
 void Compiler::AnalyzeVariableScopeAccessHandler::notify_variable_access(uint32_t id, uint32_t block)
 {
 {
+	if (id == 0)
+		return;
+
 	if (id_is_phi_variable(id))
 	if (id_is_phi_variable(id))
 		accessed_variables_to_block[id].insert(block);
 		accessed_variables_to_block[id].insert(block);
 	else if (id_is_potential_temporary(id))
 	else if (id_is_potential_temporary(id))
@@ -2924,6 +2927,8 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
 				partial_write_variables_to_block[var->self].insert(current_block->self);
 				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.
 		// Might try to store a Phi variable here.
 		notify_variable_access(args[1], current_block->self);
 		notify_variable_access(args[1], current_block->self);
 		break;
 		break;
@@ -2941,9 +2946,16 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
 		if (var)
 		if (var)
 			accessed_variables_to_block[var->self].insert(current_block->self);
 			accessed_variables_to_block[var->self].insert(current_block->self);
 
 
-		for (uint32_t i = 3; i < length; i++)
+		// 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);
 			notify_variable_access(args[i], current_block->self);
 
 
+		// 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.
 		// The result of an access chain is a fixed expression and is not really considered a temporary.
 		auto &e = compiler.set<SPIRExpression>(args[1], "", args[0], true);
 		auto &e = compiler.set<SPIRExpression>(args[1], "", args[0], true);
 		auto *backing_variable = compiler.maybe_get_backing_variable(ptr);
 		auto *backing_variable = compiler.maybe_get_backing_variable(ptr);
@@ -2951,6 +2963,7 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
 
 
 		// Other backends might use SPIRAccessChain for this later.
 		// Other backends might use SPIRAccessChain for this later.
 		compiler.ir.ids[args[1]].set_allow_type_rewrite();
 		compiler.ir.ids[args[1]].set_allow_type_rewrite();
+		access_chain_expressions.insert(args[1]);
 		break;
 		break;
 	}
 	}
 
 
@@ -2973,6 +2986,10 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
 				partial_write_variables_to_block[var->self].insert(current_block->self);
 				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);
 		var = compiler.maybe_get_backing_variable(rhs);
 		if (var)
 		if (var)
 			accessed_variables_to_block[var->self].insert(current_block->self);
 			accessed_variables_to_block[var->self].insert(current_block->self);
@@ -2988,6 +3005,11 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
 		if (var)
 		if (var)
 			accessed_variables_to_block[var->self].insert(current_block->self);
 			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.
 		// Might try to copy a Phi variable here.
 		notify_variable_access(args[2], current_block->self);
 		notify_variable_access(args[2], current_block->self);
 		break;
 		break;
@@ -3004,6 +3026,9 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
 
 
 		// Loaded value is a temporary.
 		// Loaded value is a temporary.
 		notify_variable_access(args[1], current_block->self);
 		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);
 		break;
 		break;
 	}
 	}
 
 
@@ -3370,7 +3395,14 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeA
 			// If a temporary is used in more than one block, we might have to lift continue block
 			// If a temporary is used in more than one block, we might have to lift continue block
 			// access up to loop header like we did for variables.
 			// access up to loop header like we did for variables.
 			if (blocks.size() != 1 && is_continue(block))
 			if (blocks.size() != 1 && is_continue(block))
-				builder.add_block(ir.continue_block_to_loop_header[block]);
+			{
+				auto &loop_header_block = get<SPIRBlock>(ir.continue_block_to_loop_header[block]);
+				assert(loop_header_block.merge == SPIRBlock::MergeLoop);
+
+				// Only relevant if the loop is not marked as complex.
+				if (!loop_header_block.complex_continue)
+					builder.add_block(loop_header_block.self);
+			}
 			else if (blocks.size() != 1 && is_single_block_loop(block))
 			else if (blocks.size() != 1 && is_single_block_loop(block))
 			{
 			{
 				// Awkward case, because the loop header is also the continue block.
 				// Awkward case, because the loop header is also the continue block.
@@ -3387,14 +3419,27 @@ void Compiler::analyze_variable_scope(SPIRFunction &entry, AnalyzeVariableScopeA
 
 
 			if (!first_use_is_dominator || force_temporary)
 			if (!first_use_is_dominator || force_temporary)
 			{
 			{
-				// 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<SPIRBlock>(dominating_block).declare_temporary;
-				block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
+				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 a complex loop to make sure access chains are created and consumed
+					// in expected order.
+					auto &loop_header_block = get<SPIRBlock>(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<SPIRBlock>(dominating_block).declare_temporary;
+					block_temporaries.emplace_back(handler.result_id_to_type[var.first], var.first);
+				}
 			}
 			}
 			else if (blocks.size() > 1)
 			else if (blocks.size() > 1)
 			{
 			{
@@ -3966,7 +4011,7 @@ void Compiler::make_constant_null(uint32_t id, uint32_t type)
 		if (!constant_type.array_size_literal.back())
 		if (!constant_type.array_size_literal.back())
 			SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
 			SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
 
 
-		vector<uint32_t> elements(constant_type.array.back());
+		SmallVector<uint32_t> elements(constant_type.array.back());
 		for (uint32_t i = 0; i < constant_type.array.back(); i++)
 		for (uint32_t i = 0; i < constant_type.array.back(); i++)
 			elements[i] = parent_id;
 			elements[i] = parent_id;
 		set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
 		set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
@@ -3974,7 +4019,7 @@ void Compiler::make_constant_null(uint32_t id, uint32_t type)
 	else if (!constant_type.member_types.empty())
 	else if (!constant_type.member_types.empty())
 	{
 	{
 		uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size()));
 		uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size()));
-		vector<uint32_t> elements(constant_type.member_types.size());
+		SmallVector<uint32_t> elements(constant_type.member_types.size());
 		for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
 		for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
 		{
 		{
 			make_constant_null(member_ids + i, constant_type.member_types[i]);
 			make_constant_null(member_ids + i, constant_type.member_types[i]);
@@ -3989,12 +4034,12 @@ void Compiler::make_constant_null(uint32_t id, uint32_t type)
 	}
 	}
 }
 }
 
 
-const std::vector<spv::Capability> &Compiler::get_declared_capabilities() const
+const SmallVector<spv::Capability> &Compiler::get_declared_capabilities() const
 {
 {
 	return ir.declared_capabilities;
 	return ir.declared_capabilities;
 }
 }
 
 
-const std::vector<std::string> &Compiler::get_declared_extensions() const
+const SmallVector<std::string> &Compiler::get_declared_extensions() const
 {
 {
 	return ir.declared_extensions;
 	return ir.declared_extensions;
 }
 }

+ 26 - 25
3rdparty/spirv-cross/spirv_cross.hpp

@@ -54,24 +54,24 @@ struct Resource
 
 
 struct ShaderResources
 struct ShaderResources
 {
 {
-	std::vector<Resource> uniform_buffers;
-	std::vector<Resource> storage_buffers;
-	std::vector<Resource> stage_inputs;
-	std::vector<Resource> stage_outputs;
-	std::vector<Resource> subpass_inputs;
-	std::vector<Resource> storage_images;
-	std::vector<Resource> sampled_images;
-	std::vector<Resource> atomic_counters;
-	std::vector<Resource> acceleration_structures;
+	SmallVector<Resource> uniform_buffers;
+	SmallVector<Resource> storage_buffers;
+	SmallVector<Resource> stage_inputs;
+	SmallVector<Resource> stage_outputs;
+	SmallVector<Resource> subpass_inputs;
+	SmallVector<Resource> storage_images;
+	SmallVector<Resource> sampled_images;
+	SmallVector<Resource> atomic_counters;
+	SmallVector<Resource> acceleration_structures;
 
 
 	// There can only be one push constant block,
 	// There can only be one push constant block,
 	// but keep the vector in case this restriction is lifted in the future.
 	// but keep the vector in case this restriction is lifted in the future.
-	std::vector<Resource> push_constant_buffers;
+	SmallVector<Resource> push_constant_buffers;
 
 
 	// For Vulkan GLSL and HLSL source,
 	// For Vulkan GLSL and HLSL source,
 	// these correspond to separate texture2D and samplers respectively.
 	// these correspond to separate texture2D and samplers respectively.
-	std::vector<Resource> separate_images;
-	std::vector<Resource> separate_samplers;
+	SmallVector<Resource> separate_images;
+	SmallVector<Resource> separate_samplers;
 };
 };
 
 
 struct CombinedImageSampler
 struct CombinedImageSampler
@@ -235,7 +235,7 @@ public:
 	// SPIR-V shader. The granularity of this analysis is per-member of a struct.
 	// 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.
 	// This can be used for Buffer (UBO), BufferBlock/StorageBuffer (SSBO) and PushConstant blocks.
 	// ID is the Resource::id obtained from get_shader_resources().
 	// ID is the Resource::id obtained from get_shader_resources().
-	std::vector<BufferRange> get_active_buffer_ranges(uint32_t id) const;
+	SmallVector<BufferRange> get_active_buffer_ranges(uint32_t id) const;
 
 
 	// Returns the effective size of a buffer block.
 	// Returns the effective size of a buffer block.
 	size_t get_declared_struct_size(const SPIRType &struct_type) const;
 	size_t get_declared_struct_size(const SPIRType &struct_type) const;
@@ -308,7 +308,7 @@ public:
 	// New variants of entry point query and reflection.
 	// 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.
 	// 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.
 	// To disambiguate, we must pass along with the entry point names the execution model.
-	std::vector<EntryPoint> get_entry_points_and_stages() const;
+	SmallVector<EntryPoint> get_entry_points_and_stages() const;
 	void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model);
 	void set_entry_point(const std::string &entry, spv::ExecutionModel execution_model);
 
 
 	// Renames an entry point from old_name to new_name.
 	// Renames an entry point from old_name to new_name.
@@ -392,7 +392,7 @@ public:
 	void build_combined_image_samplers();
 	void build_combined_image_samplers();
 
 
 	// Gets a remapping for the combined image samplers.
 	// Gets a remapping for the combined image samplers.
-	const std::vector<CombinedImageSampler> &get_combined_image_samplers() const
+	const SmallVector<CombinedImageSampler> &get_combined_image_samplers() const
 	{
 	{
 		return combined_image_samplers;
 		return combined_image_samplers;
 	}
 	}
@@ -417,7 +417,7 @@ public:
 	// For composite types, the subconstants can be iterated over and modified.
 	// For composite types, the subconstants can be iterated over and modified.
 	// constant_type is the SPIRType for the specialization constant,
 	// constant_type is the SPIRType for the specialization constant,
 	// which can be queried to determine which fields in the unions should be poked at.
 	// which can be queried to determine which fields in the unions should be poked at.
-	std::vector<SpecializationConstant> get_specialization_constants() const;
+	SmallVector<SpecializationConstant> get_specialization_constants() const;
 	SPIRConstant &get_constant(uint32_t id);
 	SPIRConstant &get_constant(uint32_t id);
 	const SPIRConstant &get_constant(uint32_t id) const;
 	const SPIRConstant &get_constant(uint32_t id) const;
 
 
@@ -468,10 +468,10 @@ public:
 	bool buffer_get_hlsl_counter_buffer(uint32_t id, uint32_t &counter_id) const;
 	bool buffer_get_hlsl_counter_buffer(uint32_t id, uint32_t &counter_id) const;
 
 
 	// Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module.
 	// Gets the list of all SPIR-V Capabilities which were declared in the SPIR-V module.
-	const std::vector<spv::Capability> &get_declared_capabilities() const;
+	const SmallVector<spv::Capability> &get_declared_capabilities() const;
 
 
 	// Gets the list of all SPIR-V extensions which were declared in the SPIR-V module.
 	// Gets the list of all SPIR-V extensions which were declared in the SPIR-V module.
-	const std::vector<std::string> &get_declared_extensions() const;
+	const SmallVector<std::string> &get_declared_extensions() const;
 
 
 	// When declaring buffer blocks in GLSL, the name declared in the GLSL source
 	// 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.
 	// might not be the same as the name declared in the SPIR-V module due to naming conflicts.
@@ -511,8 +511,8 @@ protected:
 	ParsedIR ir;
 	ParsedIR ir;
 	// Marks variables which have global scope and variables which can alias with other variables
 	// Marks variables which have global scope and variables which can alias with other variables
 	// (SSBO, image load store, etc)
 	// (SSBO, image load store, etc)
-	std::vector<uint32_t> global_variables;
-	std::vector<uint32_t> aliased_variables;
+	SmallVector<uint32_t> global_variables;
+	SmallVector<uint32_t> aliased_variables;
 
 
 	SPIRFunction *current_function = nullptr;
 	SPIRFunction *current_function = nullptr;
 	SPIRBlock *current_block = nullptr;
 	SPIRBlock *current_block = nullptr;
@@ -686,7 +686,7 @@ protected:
 	// variable is part of that entry points interface.
 	// variable is part of that entry points interface.
 	bool interface_variable_exists_in_entry_point(uint32_t id) const;
 	bool interface_variable_exists_in_entry_point(uint32_t id) const;
 
 
-	std::vector<CombinedImageSampler> combined_image_samplers;
+	SmallVector<CombinedImageSampler> combined_image_samplers;
 
 
 	void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const
 	void remap_variable_type_name(const SPIRType &type, const std::string &var_name, std::string &type_name) const
 	{
 	{
@@ -729,7 +729,7 @@ protected:
 
 
 	struct BufferAccessHandler : OpcodeHandler
 	struct BufferAccessHandler : OpcodeHandler
 	{
 	{
-		BufferAccessHandler(const Compiler &compiler_, std::vector<BufferRange> &ranges_, uint32_t id_)
+		BufferAccessHandler(const Compiler &compiler_, SmallVector<BufferRange> &ranges_, uint32_t id_)
 		    : compiler(compiler_)
 		    : compiler(compiler_)
 		    , ranges(ranges_)
 		    , ranges(ranges_)
 		    , id(id_)
 		    , id(id_)
@@ -739,7 +739,7 @@ protected:
 		bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
 		bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
 
 
 		const Compiler &compiler;
 		const Compiler &compiler;
-		std::vector<BufferRange> &ranges;
+		SmallVector<BufferRange> &ranges;
 		uint32_t id;
 		uint32_t id;
 
 
 		std::unordered_set<uint32_t> seen;
 		std::unordered_set<uint32_t> seen;
@@ -810,7 +810,7 @@ protected:
 	bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const;
 	bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const;
 	bool traverse_all_reachable_opcodes(const SPIRFunction &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.
 	// This must be an ordered data structure so we always pick the same type aliases.
-	std::vector<uint32_t> global_struct_cache;
+	SmallVector<uint32_t> global_struct_cache;
 
 
 	ShaderResources get_shader_resources(const std::unordered_set<uint32_t> *active_variables) const;
 	ShaderResources get_shader_resources(const std::unordered_set<uint32_t> *active_variables) const;
 
 
@@ -916,6 +916,7 @@ protected:
 		std::unordered_map<uint32_t, uint32_t> result_id_to_type;
 		std::unordered_map<uint32_t, uint32_t> result_id_to_type;
 		std::unordered_map<uint32_t, std::unordered_set<uint32_t>> complete_write_variables_to_block;
 		std::unordered_map<uint32_t, std::unordered_set<uint32_t>> complete_write_variables_to_block;
 		std::unordered_map<uint32_t, std::unordered_set<uint32_t>> partial_write_variables_to_block;
 		std::unordered_map<uint32_t, std::unordered_set<uint32_t>> partial_write_variables_to_block;
+		std::unordered_set<uint32_t> access_chain_expressions;
 		const SPIRBlock *current_block = nullptr;
 		const SPIRBlock *current_block = nullptr;
 	};
 	};
 
 
@@ -967,6 +968,6 @@ private:
 	bool type_is_block_like(const SPIRType &type) const;
 	bool type_is_block_like(const SPIRType &type) const;
 	bool type_is_opaque_value(const SPIRType &type) const;
 	bool type_is_opaque_value(const SPIRType &type) const;
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 26 - 26
3rdparty/spirv-cross/spirv_cross_c.cpp

@@ -34,9 +34,9 @@
 #include "spirv_reflect.hpp"
 #include "spirv_reflect.hpp"
 #endif
 #endif
 #include "spirv_parser.hpp"
 #include "spirv_parser.hpp"
-#include <string.h>
 #include <memory>
 #include <memory>
 #include <new>
 #include <new>
+#include <string.h>
 
 
 // clang-format off
 // clang-format off
 
 
@@ -88,7 +88,7 @@ struct StringAllocation : ScratchMemoryAllocation
 template <typename T>
 template <typename T>
 struct TemporaryBuffer : ScratchMemoryAllocation
 struct TemporaryBuffer : ScratchMemoryAllocation
 {
 {
-	std::vector<T> buffer;
+	SmallVector<T> buffer;
 };
 };
 
 
 template <typename T, typename... Ts>
 template <typename T, typename... Ts>
@@ -100,7 +100,7 @@ static inline std::unique_ptr<T> spvc_allocate(Ts &&... ts)
 struct spvc_context_s
 struct spvc_context_s
 {
 {
 	string last_error;
 	string last_error;
-	vector<unique_ptr<ScratchMemoryAllocation>> allocations;
+	SmallVector<unique_ptr<ScratchMemoryAllocation>> allocations;
 	const char *allocate_name(const std::string &name);
 	const char *allocate_name(const std::string &name);
 
 
 	spvc_error_callback callback = nullptr;
 	spvc_error_callback callback = nullptr;
@@ -173,20 +173,20 @@ struct spvc_constant_s : SPIRConstant
 struct spvc_resources_s : ScratchMemoryAllocation
 struct spvc_resources_s : ScratchMemoryAllocation
 {
 {
 	spvc_context context = nullptr;
 	spvc_context context = nullptr;
-	std::vector<spvc_reflected_resource> uniform_buffers;
-	std::vector<spvc_reflected_resource> storage_buffers;
-	std::vector<spvc_reflected_resource> stage_inputs;
-	std::vector<spvc_reflected_resource> stage_outputs;
-	std::vector<spvc_reflected_resource> subpass_inputs;
-	std::vector<spvc_reflected_resource> storage_images;
-	std::vector<spvc_reflected_resource> sampled_images;
-	std::vector<spvc_reflected_resource> atomic_counters;
-	std::vector<spvc_reflected_resource> push_constant_buffers;
-	std::vector<spvc_reflected_resource> separate_images;
-	std::vector<spvc_reflected_resource> separate_samplers;
-	std::vector<spvc_reflected_resource> acceleration_structures;
-
-	bool copy_resources(std::vector<spvc_reflected_resource> &outputs, const std::vector<Resource> &inputs);
+	SmallVector<spvc_reflected_resource> uniform_buffers;
+	SmallVector<spvc_reflected_resource> storage_buffers;
+	SmallVector<spvc_reflected_resource> stage_inputs;
+	SmallVector<spvc_reflected_resource> stage_outputs;
+	SmallVector<spvc_reflected_resource> subpass_inputs;
+	SmallVector<spvc_reflected_resource> storage_images;
+	SmallVector<spvc_reflected_resource> sampled_images;
+	SmallVector<spvc_reflected_resource> atomic_counters;
+	SmallVector<spvc_reflected_resource> push_constant_buffers;
+	SmallVector<spvc_reflected_resource> separate_images;
+	SmallVector<spvc_reflected_resource> separate_samplers;
+	SmallVector<spvc_reflected_resource> acceleration_structures;
+
+	bool copy_resources(SmallVector<spvc_reflected_resource> &outputs, const SmallVector<Resource> &inputs);
 	bool copy_resources(const ShaderResources &resources);
 	bool copy_resources(const ShaderResources &resources);
 };
 };
 
 
@@ -634,7 +634,7 @@ spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler,
 	}
 	}
 
 
 	auto &hlsl = *static_cast<CompilerHLSL *>(compiler->compiler.get());
 	auto &hlsl = *static_cast<CompilerHLSL *>(compiler->compiler.get());
-	std::vector<RootConstants> roots;
+	vector<RootConstants> roots;
 	roots.reserve(count);
 	roots.reserve(count);
 	for (size_t i = 0; i < count; i++)
 	for (size_t i = 0; i < count; i++)
 	{
 	{
@@ -980,8 +980,8 @@ spvc_result spvc_compiler_compile(spvc_compiler compiler, const char **source)
 	SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV)
 	SPVC_END_SAFE_SCOPE(compiler->context, SPVC_ERROR_UNSUPPORTED_SPIRV)
 }
 }
 
 
-bool spvc_resources_s::copy_resources(std::vector<spvc_reflected_resource> &outputs,
-                                      const std::vector<Resource> &inputs)
+bool spvc_resources_s::copy_resources(SmallVector<spvc_reflected_resource> &outputs,
+                                      const SmallVector<Resource> &inputs)
 {
 {
 	for (auto &i : inputs)
 	for (auto &i : inputs)
 	{
 	{
@@ -1117,7 +1117,7 @@ spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources,
                                                       const spvc_reflected_resource **resource_list,
                                                       const spvc_reflected_resource **resource_list,
                                                       size_t *resource_size)
                                                       size_t *resource_size)
 {
 {
-	const std::vector<spvc_reflected_resource> *list = nullptr;
+	const SmallVector<spvc_reflected_resource> *list = nullptr;
 	switch (type)
 	switch (type)
 	{
 	{
 	case SPVC_RESOURCE_TYPE_UNIFORM_BUFFER:
 	case SPVC_RESOURCE_TYPE_UNIFORM_BUFFER:
@@ -1275,7 +1275,7 @@ spvc_result spvc_compiler_get_entry_points(spvc_compiler compiler, const spvc_en
 	SPVC_BEGIN_SAFE_SCOPE
 	SPVC_BEGIN_SAFE_SCOPE
 	{
 	{
 		auto entries = compiler->compiler->get_entry_points_and_stages();
 		auto entries = compiler->compiler->get_entry_points_and_stages();
-		std::vector<spvc_entry_point> translated;
+		SmallVector<spvc_entry_point> translated;
 		translated.reserve(entries.size());
 		translated.reserve(entries.size());
 
 
 		for (auto &entry : entries)
 		for (auto &entry : entries)
@@ -1406,7 +1406,7 @@ unsigned spvc_type_get_bit_width(spvc_type type)
 	return type->width;
 	return type->width;
 }
 }
 
 
-unsigned spvc_type_get_vector_size(spvc_type type)
+unsigned spvc_type_get_SmallVector_size(spvc_type type)
 {
 {
 	return type->vecsize;
 	return type->vecsize;
 }
 }
@@ -1566,7 +1566,7 @@ spvc_result spvc_compiler_get_combined_image_samplers(spvc_compiler compiler,
 	SPVC_BEGIN_SAFE_SCOPE
 	SPVC_BEGIN_SAFE_SCOPE
 	{
 	{
 		auto combined = compiler->compiler->get_combined_image_samplers();
 		auto combined = compiler->compiler->get_combined_image_samplers();
-		std::vector<spvc_combined_image_sampler> translated;
+		SmallVector<spvc_combined_image_sampler> translated;
 		translated.reserve(combined.size());
 		translated.reserve(combined.size());
 		for (auto &c : combined)
 		for (auto &c : combined)
 		{
 		{
@@ -1591,7 +1591,7 @@ spvc_result spvc_compiler_get_specialization_constants(spvc_compiler compiler,
 	SPVC_BEGIN_SAFE_SCOPE
 	SPVC_BEGIN_SAFE_SCOPE
 	{
 	{
 		auto spec_constants = compiler->compiler->get_specialization_constants();
 		auto spec_constants = compiler->compiler->get_specialization_constants();
-		std::vector<spvc_specialization_constant> translated;
+		SmallVector<spvc_specialization_constant> translated;
 		translated.reserve(spec_constants.size());
 		translated.reserve(spec_constants.size());
 		for (auto &c : spec_constants)
 		for (auto &c : spec_constants)
 		{
 		{
@@ -1743,7 +1743,7 @@ spvc_result spvc_compiler_get_declared_extensions(spvc_compiler compiler, const
 	SPVC_BEGIN_SAFE_SCOPE
 	SPVC_BEGIN_SAFE_SCOPE
 	{
 	{
 		auto &exts = compiler->compiler->get_declared_extensions();
 		auto &exts = compiler->compiler->get_declared_extensions();
-		std::vector<const char *> duped;
+		SmallVector<const char *> duped;
 		duped.reserve(exts.size());
 		duped.reserve(exts.size());
 		for (auto &ext : exts)
 		for (auto &ext : exts)
 			duped.push_back(compiler->context->allocate_name(ext));
 			duped.push_back(compiler->context->allocate_name(ext));

+ 712 - 0
3rdparty/spirv-cross/spirv_cross_containers.hpp

@@ -0,0 +1,712 @@
+/*
+ * Copyright 2019 Hans-Kristian Arntzen
+ *
+ * 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_CONTAINERS_HPP
+#define SPIRV_CROSS_CONTAINERS_HPP
+
+#include "spirv_cross_error_handling.hpp"
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <stack>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <type_traits>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+#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 <typename T, size_t N>
+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<T *>(u.aligned_char);
+#else
+		return reinterpret_cast<T *>(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 <typename T>
+class AlignedBuffer<T, 0>
+{
+public:
+	T *data()
+	{
+		return nullptr;
+	}
+};
+
+// An immutable version of SmallVector which erases type information about storage.
+template <typename T>
+class VectorView
+{
+public:
+	T &operator[](size_t i)
+	{
+		return ptr[i];
+	}
+
+	const T &operator[](size_t i) const
+	{
+		return ptr[i];
+	}
+
+	bool empty() const
+	{
+		return buffer_size == 0;
+	}
+
+	size_t size() const
+	{
+		return buffer_size;
+	}
+
+	T *data()
+	{
+		return ptr;
+	}
+
+	const T *data() const
+	{
+		return ptr;
+	}
+
+	T *begin()
+	{
+		return ptr;
+	}
+
+	T *end()
+	{
+		return ptr + buffer_size;
+	}
+
+	const T *begin() const
+	{
+		return ptr;
+	}
+
+	const T *end() const
+	{
+		return ptr + buffer_size;
+	}
+
+	T &front()
+	{
+		return ptr[0];
+	}
+
+	const T &front() const
+	{
+		return ptr[0];
+	}
+
+	T &back()
+	{
+		return ptr[buffer_size - 1];
+	}
+
+	const T &back() const
+	{
+		return ptr[buffer_size - 1];
+	}
+
+	// Makes it easier to consume SmallVector.
+#if defined(_MSC_VER) && _MSC_VER < 1900
+	explicit operator std::vector<T>() const
+	{
+		// Another MSVC 2013 workaround. It does not understand lvalue/rvalue qualified operations.
+		return std::vector<T>(ptr, ptr + buffer_size);
+	}
+#else
+	// Makes it easier to consume SmallVector.
+	explicit operator std::vector<T>() const &
+	{
+		return std::vector<T>(ptr, ptr + buffer_size);
+	}
+
+	// If we are converting as an r-value, we can pilfer our elements.
+	explicit operator std::vector<T>() &&
+	{
+		return std::vector<T>(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 <typename T, size_t N = 8>
+class SmallVector : public VectorView<T>
+{
+public:
+	SmallVector()
+	{
+		this->ptr = stack_storage.data();
+		buffer_capacity = N;
+	}
+
+	SmallVector(const T *arg_list_begin, const T *arg_list_end)
+	    : 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;
+	}
+
+	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)
+	    : SmallVector()
+	{
+		*this = other;
+	}
+
+	SmallVector &operator=(const SmallVector &other)
+	{
+		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)
+	    : SmallVector()
+	{
+		resize(count);
+	}
+
+	~SmallVector()
+	{
+		clear();
+		if (this->ptr != stack_storage.data())
+			free(this->ptr);
+	}
+
+	void clear()
+	{
+		for (size_t i = 0; i < this->buffer_size; i++)
+			this->ptr[i].~T();
+		this->buffer_size = 0;
+	}
+
+	void push_back(const T &t)
+	{
+		reserve(this->buffer_size + 1);
+		new (&this->ptr[this->buffer_size]) T(t);
+		this->buffer_size++;
+	}
+
+	void push_back(T &&t)
+	{
+		reserve(this->buffer_size + 1);
+		new (&this->ptr[this->buffer_size]) T(std::move(t));
+		this->buffer_size++;
+	}
+
+	void pop_back()
+	{
+		resize(this->buffer_size - 1);
+	}
+
+	template <typename... Ts>
+	void emplace_back(Ts &&... ts)
+	{
+		reserve(this->buffer_size + 1);
+		new (&this->ptr[this->buffer_size]) T(std::forward<Ts>(ts)...);
+		this->buffer_size++;
+	}
+
+	void reserve(size_t count)
+	{
+		if (count > buffer_capacity)
+		{
+			size_t target_capacity = buffer_capacity;
+			if (target_capacity == 0)
+				target_capacity = 1;
+			if (target_capacity < N)
+				target_capacity = N;
+
+			while (target_capacity < count)
+				target_capacity <<= 1u;
+
+			T *new_buffer =
+			    target_capacity > N ? static_cast<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data();
+
+			if (!new_buffer)
+				SPIRV_CROSS_THROW("Out of memory.");
+
+			// 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)
+	{
+		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<T *>(malloc(target_capacity * sizeof(T))) : stack_storage.data();
+				if (!new_buffer)
+					SPIRV_CROSS_THROW("Out of memory.");
+
+				// 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;
+		}
+	}
+
+	T *erase(T *itr)
+	{
+		std::move(itr + 1, this->end(), itr);
+		this->ptr[--this->buffer_size].~T();
+		return itr;
+	}
+
+	void erase(T *start_erase, T *end_erase)
+	{
+		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)
+	{
+		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<T, N> 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 <typename T>
+using Vector = SmallVector<T, 0>;
+
+#else // SPIRV_CROSS_FORCE_STL_TYPES
+
+template <typename T, size_t N = 8>
+using SmallVector = std::vector<T>;
+template <typename T>
+using Vector = std::vector<T>;
+template <typename T>
+using VectorView = std::vector<T>;
+
+#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 free_opaque(void *ptr) = 0;
+};
+
+template <typename T>
+class ObjectPool : public ObjectPoolBase
+{
+public:
+	explicit ObjectPool(unsigned start_object_count_ = 16)
+	    : start_object_count(start_object_count_)
+	{
+	}
+
+	template <typename... P>
+	T *allocate(P &&... p)
+	{
+		if (vacants.empty())
+		{
+			unsigned num_objects = start_object_count << memory.size();
+			T *ptr = static_cast<T *>(malloc(num_objects * sizeof(T)));
+			if (!ptr)
+				return nullptr;
+
+			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>(p)...);
+		return ptr;
+	}
+
+	void free(T *ptr)
+	{
+		ptr->~T();
+		vacants.push_back(ptr);
+	}
+
+	void free_opaque(void *ptr) override
+	{
+		free(static_cast<T *>(ptr));
+	}
+
+	void clear()
+	{
+		vacants.clear();
+		memory.clear();
+	}
+
+protected:
+	Vector<T *> vacants;
+
+	struct MallocDeleter
+	{
+		void operator()(T *ptr)
+		{
+			::free(ptr);
+		}
+	};
+
+	SmallVector<std::unique_ptr<T, MallocDeleter>> memory;
+	unsigned start_object_count;
+};
+
+template <size_t StackSize = 4096, size_t BlockSize = 4096>
+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 <typename T, typename std::enable_if<!std::is_floating_point<T>::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 <size_t N>
+	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<Buffer> 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<char *>(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

+ 83 - 0
3rdparty/spirv-cross/spirv_cross_error_handling.hpp

@@ -0,0 +1,83 @@
+/*
+ * Copyright 2015-2019 Arm Limited
+ *
+ * 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_ERROR_HANDLING
+#define SPIRV_CROSS_ERROR_HANDLING
+
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+
+#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]]
+#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)
+	{
+	}
+};
+
+#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

+ 99 - 3
3rdparty/spirv-cross/spirv_cross_parsed_ir.cpp

@@ -23,9 +23,101 @@ using namespace spv;
 
 
 namespace SPIRV_CROSS_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<SPIRType>);
+	pool_group->pools[TypeVariable].reset(new ObjectPool<SPIRVariable>);
+	pool_group->pools[TypeConstant].reset(new ObjectPool<SPIRConstant>);
+	pool_group->pools[TypeFunction].reset(new ObjectPool<SPIRFunction>);
+	pool_group->pools[TypeFunctionPrototype].reset(new ObjectPool<SPIRFunctionPrototype>);
+	pool_group->pools[TypeBlock].reset(new ObjectPool<SPIRBlock>);
+	pool_group->pools[TypeExtension].reset(new ObjectPool<SPIRExtension>);
+	pool_group->pools[TypeExpression].reset(new ObjectPool<SPIRExpression>);
+	pool_group->pools[TypeConstantOp].reset(new ObjectPool<SPIRConstantOp>);
+	pool_group->pools[TypeCombinedImageSampler].reset(new ObjectPool<SPIRCombinedImageSampler>);
+	pool_group->pools[TypeAccessChain].reset(new ObjectPool<SPIRAccessChain>);
+	pool_group->pools[TypeUndef].reset(new ObjectPool<SPIRUndef>);
+}
+
+// Should have been default-implemented, but need this on MSVC 2013.
+ParsedIR::ParsedIR(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
+{
+	*this = move(other);
+}
+
+ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
+{
+	if (this != &other)
+	{
+		pool_group = move(other.pool_group);
+		spirv = move(other.spirv);
+		meta = move(other.meta);
+		for (int i = 0; i < TypeCount; i++)
+			ids_for_type[i] = move(other.ids_for_type[i]);
+		ids_for_constant_or_type = move(other.ids_for_constant_or_type);
+		ids_for_constant_or_variable = move(other.ids_for_constant_or_variable);
+		declared_capabilities = move(other.declared_capabilities);
+		declared_extensions = move(other.declared_extensions);
+		block_meta = move(other.block_meta);
+		continue_block_to_loop_header = move(other.continue_block_to_loop_header);
+		entry_points = move(other.entry_points);
+		ids = move(other.ids);
+
+		default_entry_point = other.default_entry_point;
+		source = other.source;
+		loop_iteration_depth = other.loop_iteration_depth;
+	}
+	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_or_type = other.ids_for_constant_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;
+		source = other.source;
+		loop_iteration_depth = other.loop_iteration_depth;
+
+		// 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)
 void ParsedIR::set_id_bounds(uint32_t bounds)
 {
 {
-	ids.resize(bounds);
+	ids.reserve(bounds);
+	while (ids.size() < bounds)
+		ids.emplace_back(pool_group.get());
+
 	block_meta.resize(bounds);
 	block_meta.resize(bounds);
 }
 }
 
 
@@ -571,7 +663,11 @@ uint32_t ParsedIR::increase_bound_by(uint32_t incr_amount)
 {
 {
 	auto curr_bound = ids.size();
 	auto curr_bound = ids.size();
 	auto new_bound = curr_bound + incr_amount;
 	auto new_bound = curr_bound + incr_amount;
-	ids.resize(new_bound);
+
+	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);
 	block_meta.resize(new_bound);
 	return uint32_t(curr_bound);
 	return uint32_t(curr_bound);
 }
 }
@@ -645,4 +741,4 @@ Meta *ParsedIR::find_meta(uint32_t id)
 		return nullptr;
 		return nullptr;
 }
 }
 
 
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE

+ 23 - 9
3rdparty/spirv-cross/spirv_cross_parsed_ir.hpp

@@ -20,7 +20,6 @@
 #include "spirv_common.hpp"
 #include "spirv_common.hpp"
 #include <stdint.h>
 #include <stdint.h>
 #include <unordered_map>
 #include <unordered_map>
-#include <vector>
 
 
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
@@ -32,7 +31,22 @@ namespace SPIRV_CROSS_NAMESPACE
 
 
 class ParsedIR
 class ParsedIR
 {
 {
+private:
+	// This must be destroyed after the "ids" vector.
+	std::unique_ptr<ObjectPoolGroup> pool_group;
+
 public:
 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.
 	// Resizes ids, meta and block_meta.
 	void set_id_bounds(uint32_t bounds);
 	void set_id_bounds(uint32_t bounds);
 
 
@@ -40,7 +54,7 @@ public:
 	std::vector<uint32_t> spirv;
 	std::vector<uint32_t> spirv;
 
 
 	// Holds various data structures which inherit from IVariant.
 	// Holds various data structures which inherit from IVariant.
-	std::vector<Variant> ids;
+	SmallVector<Variant> ids;
 
 
 	// Various meta data for IDs, decorations, names, etc.
 	// Various meta data for IDs, decorations, names, etc.
 	std::unordered_map<uint32_t, Meta> meta;
 	std::unordered_map<uint32_t, Meta> meta;
@@ -48,19 +62,19 @@ public:
 	// Holds all IDs which have a certain type.
 	// Holds all IDs which have a certain type.
 	// This is needed so we can iterate through a specific kind of resource quickly,
 	// This is needed so we can iterate through a specific kind of resource quickly,
 	// and in-order of module declaration.
 	// and in-order of module declaration.
-	std::vector<uint32_t> ids_for_type[TypeCount];
+	SmallVector<uint32_t> ids_for_type[TypeCount];
 
 
 	// Special purpose lists which contain a union of types.
 	// Special purpose lists which contain a union of types.
 	// This is needed so we can declare specialization constants and structs in an interleaved fashion,
 	// This is needed so we can declare specialization constants and structs in an interleaved fashion,
 	// among other things.
 	// among other things.
 	// Constants can be of struct type, and struct array sizes can use specialization constants.
 	// Constants can be of struct type, and struct array sizes can use specialization constants.
-	std::vector<uint32_t> ids_for_constant_or_type;
-	std::vector<uint32_t> ids_for_constant_or_variable;
+	SmallVector<uint32_t> ids_for_constant_or_type;
+	SmallVector<uint32_t> ids_for_constant_or_variable;
 
 
 	// Declared capabilities and extensions in the SPIR-V module.
 	// Declared capabilities and extensions in the SPIR-V module.
 	// Not really used except for reflection at the moment.
 	// Not really used except for reflection at the moment.
-	std::vector<spv::Capability> declared_capabilities;
-	std::vector<std::string> declared_extensions;
+	SmallVector<spv::Capability> declared_capabilities;
+	SmallVector<std::string> declared_extensions;
 
 
 	// Meta data about blocks. The cross-compiler needs to query if a block is either of these types.
 	// 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.
 	// It is a bitset as there can be more than one tag per block.
@@ -73,7 +87,7 @@ public:
 		BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
 		BLOCK_META_MULTISELECT_MERGE_BIT = 1 << 4
 	};
 	};
 	using BlockMetaFlags = uint8_t;
 	using BlockMetaFlags = uint8_t;
-	std::vector<BlockMetaFlags> block_meta;
+	SmallVector<BlockMetaFlags> block_meta;
 	std::unordered_map<uint32_t, uint32_t> continue_block_to_loop_header;
 	std::unordered_map<uint32_t, uint32_t> continue_block_to_loop_header;
 
 
 	// Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
 	// Normally, we'd stick SPIREntryPoint in ids array, but it conflicts with SPIRFunction.
@@ -181,6 +195,6 @@ private:
 	std::string empty_string;
 	std::string empty_string;
 	Bitset cleared_bitset;
 	Bitset cleared_bitset;
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 2 - 2
3rdparty/spirv-cross/spirv_cross_util.cpp

@@ -22,8 +22,8 @@ using namespace SPIRV_CROSS_NAMESPACE;
 
 
 namespace spirv_cross_util
 namespace spirv_cross_util
 {
 {
-void rename_interface_variable(Compiler &compiler, const std::vector<Resource> &resources,
-                               uint32_t location, const std::string &name)
+void rename_interface_variable(Compiler &compiler, const SmallVector<Resource> &resources, uint32_t location,
+                               const std::string &name)
 {
 {
 	for (auto &v : resources)
 	for (auto &v : resources)
 	{
 	{

+ 2 - 1
3rdparty/spirv-cross/spirv_cross_util.hpp

@@ -21,7 +21,8 @@
 
 
 namespace spirv_cross_util
 namespace spirv_cross_util
 {
 {
-void rename_interface_variable(SPIRV_CROSS_NAMESPACE::Compiler &compiler, const std::vector<SPIRV_CROSS_NAMESPACE::Resource> &resources,
+void rename_interface_variable(SPIRV_CROSS_NAMESPACE::Compiler &compiler,
+                               const SPIRV_CROSS_NAMESPACE::SmallVector<SPIRV_CROSS_NAMESPACE::Resource> &resources,
                                uint32_t location, const std::string &name);
                                uint32_t location, const std::string &name);
 void inherit_combined_sampler_bindings(SPIRV_CROSS_NAMESPACE::Compiler &compiler);
 void inherit_combined_sampler_bindings(SPIRV_CROSS_NAMESPACE::Compiler &compiler);
 } // namespace spirv_cross_util
 } // namespace spirv_cross_util

+ 167 - 63
3rdparty/spirv-cross/spirv_glsl.cpp

@@ -454,8 +454,7 @@ string CompilerGLSL::compile()
 
 
 		reset();
 		reset();
 
 
-		// Move constructor for this type is broken on GCC 4.9 ...
-		buffer = unique_ptr<ostringstream>(new ostringstream());
+		buffer.reset();
 
 
 		emit_header();
 		emit_header();
 		emit_resources();
 		emit_resources();
@@ -468,15 +467,15 @@ string CompilerGLSL::compile()
 	// Entry point in GLSL is always main().
 	// Entry point in GLSL is always main().
 	get_entry_point().name = "main";
 	get_entry_point().name = "main";
 
 
-	return buffer->str();
+	return buffer.str();
 }
 }
 
 
 std::string CompilerGLSL::get_partial_source()
 std::string CompilerGLSL::get_partial_source()
 {
 {
-	return buffer ? buffer->str() : "No compiled source available yet.";
+	return buffer.str();
 }
 }
 
 
-void CompilerGLSL::build_workgroup_size(vector<string> &arguments, const SpecializationConstant &wg_x,
+void CompilerGLSL::build_workgroup_size(SmallVector<string> &arguments, const SpecializationConstant &wg_x,
                                         const SpecializationConstant &wg_y, const SpecializationConstant &wg_z)
                                         const SpecializationConstant &wg_y, const SpecializationConstant &wg_z)
 {
 {
 	auto &execution = get_entry_point();
 	auto &execution = get_entry_point();
@@ -573,8 +572,8 @@ void CompilerGLSL::emit_header()
 	for (auto &header : header_lines)
 	for (auto &header : header_lines)
 		statement(header);
 		statement(header);
 
 
-	vector<string> inputs;
-	vector<string> outputs;
+	SmallVector<string> inputs;
+	SmallVector<string> outputs;
 
 
 	switch (execution.model)
 	switch (execution.model)
 	{
 	{
@@ -798,7 +797,7 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index)
 		return "";
 		return "";
 	auto &dec = memb[index];
 	auto &dec = memb[index];
 
 
-	vector<string> attr;
+	SmallVector<string> attr;
 
 
 	// We can only apply layouts on members in block interfaces.
 	// 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 a bit problematic because in SPIR-V decorations are applied on the struct types directly.
@@ -1294,7 +1293,7 @@ string CompilerGLSL::layout_for_variable(const SPIRVariable &var)
 	if (is_legacy())
 	if (is_legacy())
 		return "";
 		return "";
 
 
-	vector<string> attr;
+	SmallVector<string> attr;
 
 
 	auto &dec = ir.meta[var.self].decoration;
 	auto &dec = ir.meta[var.self].decoration;
 	auto &type = get<SPIRType>(var.basetype);
 	auto &type = get<SPIRType>(var.basetype);
@@ -2325,7 +2324,7 @@ void CompilerGLSL::emit_resources()
 
 
 		if ((wg_x.id != 0) || (wg_y.id != 0) || (wg_z.id != 0))
 		if ((wg_x.id != 0) || (wg_y.id != 0) || (wg_z.id != 0))
 		{
 		{
-			vector<string> inputs;
+			SmallVector<string> inputs;
 			build_workgroup_size(inputs, wg_x, wg_y, wg_z);
 			build_workgroup_size(inputs, wg_x, wg_y, wg_z);
 			statement("layout(", merge(inputs), ") in;");
 			statement("layout(", merge(inputs), ") in;");
 			statement("");
 			statement("");
@@ -2869,7 +2868,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
 	}
 	}
 
 
 	uint32_t bit_width = 0;
 	uint32_t bit_width = 0;
-	if (unary || binary)
+	if (unary || binary || cop.opcode == OpSConvert || cop.opcode == OpUConvert)
 		bit_width = expression_type(cop.arguments[0]).width;
 		bit_width = expression_type(cop.arguments[0]).width;
 
 
 	SPIRType::BaseType input_type;
 	SPIRType::BaseType input_type;
@@ -2889,6 +2888,8 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
 	case OpSMod:
 	case OpSMod:
 	case OpSDiv:
 	case OpSDiv:
 	case OpShiftRightArithmetic:
 	case OpShiftRightArithmetic:
+	case OpSConvert:
+	case OpSNegate:
 		input_type = to_signed_basetype(bit_width);
 		input_type = to_signed_basetype(bit_width);
 		break;
 		break;
 
 
@@ -2899,6 +2900,7 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
 	case OpUMod:
 	case OpUMod:
 	case OpUDiv:
 	case OpUDiv:
 	case OpShiftRightLogical:
 	case OpShiftRightLogical:
+	case OpUConvert:
 		input_type = to_unsigned_basetype(bit_width);
 		input_type = to_unsigned_basetype(bit_width);
 		break;
 		break;
 
 
@@ -2940,6 +2942,21 @@ string CompilerGLSL::constant_op_expression(const SPIRConstantOp &cop)
 		// Works around various casting scenarios in glslang as there is no OpBitcast for specialization constants.
 		// Works around various casting scenarios in glslang as there is no OpBitcast for specialization constants.
 		return join("(", op, bitcast_glsl(type, cop.arguments[0]), ")");
 		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
 	else
 	{
 	{
 		if (cop.arguments.size() < 1)
 		if (cop.arguments.size() < 1)
@@ -3581,6 +3598,41 @@ string CompilerGLSL::constant_expression_vector(const SPIRConstant &c, uint32_t
 	return 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<SPIRExpression>(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 (current_continue_block && !hoisted_temporaries.count(result_id))
+	{
+		auto &header = get<SPIRBlock>(current_continue_block->loop_dominator);
+		if (find_if(begin(header.declare_temporary), end(header.declare_temporary),
+		            [result_type, result_id](const pair<uint32_t, uint32_t> &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<SPIRType>(result_type);
+		auto &flags = ir.meta[result_id].decoration.decoration_flags;
+
+		// The result_id has not been made into an expression yet, so use flags interface.
+		add_local_variable_name(result_id);
+		statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), ";");
+	}
+}
+
 string CompilerGLSL::declare_temporary(uint32_t result_type, uint32_t result_id)
 string CompilerGLSL::declare_temporary(uint32_t result_type, uint32_t result_id)
 {
 {
 	auto &type = get<SPIRType>(result_type);
 	auto &type = get<SPIRType>(result_type);
@@ -3788,14 +3840,19 @@ void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result
                                            SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type)
                                            SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type)
 {
 {
 	auto &out_type = get<SPIRType>(result_type);
 	auto &out_type = get<SPIRType>(result_type);
+	auto &expr_type = expression_type(op0);
 	auto expected_type = out_type;
 	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.basetype = input_type;
-	string cast_op = expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_expression(op0);
+	expected_type.width = expr_type.width;
+	string cast_op = expr_type.basetype != input_type ? bitcast_glsl(expected_type, op0) : to_unpacked_expression(op0);
 
 
 	string expr;
 	string expr;
 	if (out_type.basetype != expected_result_type)
 	if (out_type.basetype != expected_result_type)
 	{
 	{
 		expected_type.basetype = expected_result_type;
 		expected_type.basetype = expected_result_type;
+		expected_type.width = out_type.width;
 		expr = bitcast_glsl_op(out_type, expected_type);
 		expr = bitcast_glsl_op(out_type, expected_type);
 		expr += '(';
 		expr += '(';
 		expr += join(op, "(", cast_op, ")");
 		expr += join(op, "(", cast_op, ")");
@@ -3810,17 +3867,18 @@ void CompilerGLSL::emit_unary_func_op_cast(uint32_t result_type, uint32_t result
 	inherit_expression_dependencies(result_id, op0);
 	inherit_expression_dependencies(result_id, op0);
 }
 }
 
 
-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)
+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<SPIRType>(result_type);
 	auto &out_type = get<SPIRType>(result_type);
 	auto expected_type = out_type;
 	auto expected_type = out_type;
 	expected_type.basetype = input_type;
 	expected_type.basetype = input_type;
-	string cast_op0 = expression_type(op0).basetype != input_type ? bitcast_glsl(expected_type, op0) : to_expression(op0);
-	string cast_op1 = expression_type(op1).basetype != input_type ? bitcast_glsl(expected_type, op1) : to_expression(op1);
-	string cast_op2 = expression_type(op2).basetype != input_type ? bitcast_glsl(expected_type, op2) : to_expression(op2);
+	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;
 	string expr;
 	if (out_type.basetype != input_type)
 	if (out_type.basetype != input_type)
@@ -4276,7 +4334,7 @@ void CompilerGLSL::emit_texture_op(const Instruction &i)
 	auto op = static_cast<Op>(i.op);
 	auto op = static_cast<Op>(i.op);
 	uint32_t length = i.length;
 	uint32_t length = i.length;
 
 
-	vector<uint32_t> inherited_expressions;
+	SmallVector<uint32_t> inherited_expressions;
 
 
 	uint32_t result_type = ops[0];
 	uint32_t result_type = ops[0];
 	uint32_t id = ops[1];
 	uint32_t id = ops[1];
@@ -4818,7 +4876,18 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 		emit_unary_func_op(result_type, id, args[0], "degrees");
 		emit_unary_func_op(result_type, id, args[0], "degrees");
 		break;
 		break;
 	case GLSLstd450Fma:
 	case GLSLstd450Fma:
-		emit_trinary_func_op(result_type, id, args[0], args[1], args[2], "fma");
+		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;
 		break;
 	case GLSLstd450Modf:
 	case GLSLstd450Modf:
 		register_call_out_argument(args[1]);
 		register_call_out_argument(args[1]);
@@ -4830,10 +4899,7 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 	{
 	{
 		forced_temporaries.insert(id);
 		forced_temporaries.insert(id);
 		auto &type = get<SPIRType>(result_type);
 		auto &type = get<SPIRType>(result_type);
-		auto &flags = ir.meta[id].decoration.decoration_flags;
-		statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(id)), ";");
-		set<SPIRExpression>(id, to_name(id), result_type, true);
-
+		emit_uninitialized_temporary_expression(result_type, id);
 		statement(to_expression(id), ".", to_member_name(type, 0), " = ", "modf(", to_expression(args[0]), ", ",
 		statement(to_expression(id), ".", to_member_name(type, 0), " = ", "modf(", to_expression(args[0]), ", ",
 		          to_expression(id), ".", to_member_name(type, 1), ");");
 		          to_expression(id), ".", to_member_name(type, 1), ");");
 		break;
 		break;
@@ -4973,10 +5039,7 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 	{
 	{
 		forced_temporaries.insert(id);
 		forced_temporaries.insert(id);
 		auto &type = get<SPIRType>(result_type);
 		auto &type = get<SPIRType>(result_type);
-		auto &flags = ir.meta[id].decoration.decoration_flags;
-		statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(id)), ";");
-		set<SPIRExpression>(id, to_name(id), result_type, true);
-
+		emit_uninitialized_temporary_expression(result_type, id);
 		statement(to_expression(id), ".", to_member_name(type, 0), " = ", "frexp(", to_expression(args[0]), ", ",
 		statement(to_expression(id), ".", to_member_name(type, 0), " = ", "frexp(", to_expression(args[0]), ", ",
 		          to_expression(id), ".", to_member_name(type, 1), ");");
 		          to_expression(id), ".", to_member_name(type, 1), ");");
 		break;
 		break;
@@ -5056,7 +5119,8 @@ void CompilerGLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
 		break;
 		break;
 
 
 	case GLSLstd450FindUMsb:
 	case GLSLstd450FindUMsb:
-		emit_unary_func_op_cast(result_type, id, args[0], "findMSB", uint_type, int_type); // findMSB always returns int.
+		emit_unary_func_op_cast(result_type, id, args[0], "findMSB", uint_type,
+		                        int_type); // findMSB always returns int.
 		break;
 		break;
 
 
 	// Multisampled varying
 	// Multisampled varying
@@ -5580,9 +5644,9 @@ string CompilerGLSL::bitcast_glsl(const SPIRType &result_type, uint32_t argument
 {
 {
 	auto op = bitcast_glsl_op(result_type, expression_type(argument));
 	auto op = bitcast_glsl_op(result_type, expression_type(argument));
 	if (op.empty())
 	if (op.empty())
-		return to_enclosed_expression(argument);
+		return to_enclosed_unpacked_expression(argument);
 	else
 	else
-		return join(op, "(", to_expression(argument), ")");
+		return join(op, "(", to_unpacked_expression(argument), ")");
 }
 }
 
 
 std::string CompilerGLSL::bitcast_expression(SPIRType::BaseType target_type, uint32_t arg)
 std::string CompilerGLSL::bitcast_expression(SPIRType::BaseType target_type, uint32_t arg)
@@ -7035,6 +7099,10 @@ uint32_t CompilerGLSL::get_integer_width_for_instruction(const Instruction &inst
 
 
 	switch (instr.op)
 	switch (instr.op)
 	{
 	{
+	case OpSConvert:
+	case OpConvertSToF:
+	case OpUConvert:
+	case OpConvertUToF:
 	case OpIEqual:
 	case OpIEqual:
 	case OpINotEqual:
 	case OpINotEqual:
 	case OpSLessThan:
 	case OpSLessThan:
@@ -7158,8 +7226,19 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		bool usage_tracking = ptr_expression && flattened_buffer_blocks.count(ptr_expression->loaded_from) != 0 &&
 		bool usage_tracking = ptr_expression && flattened_buffer_blocks.count(ptr_expression->loaded_from) != 0 &&
 		                      (type.basetype == SPIRType::Struct || (type.columns > 1));
 		                      (type.basetype == SPIRType::Struct || (type.columns > 1));
 
 
-		auto &e = emit_op(result_type, id, expr, forward, !usage_tracking);
-		e.need_transpose = need_transpose;
+		SPIRExpression *e = nullptr;
+		if (!backend.array_is_value_type && !type.array.empty() && !forward)
+		{
+			// 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(to_expression(id), ptr);
+		}
+		else
+			e = &emit_op(result_type, id, expr, forward, !usage_tracking);
+
+		e->need_transpose = need_transpose;
 		register_read(id, ptr, forward);
 		register_read(id, ptr, forward);
 
 
 		// Pass through whether the result is of a packed type.
 		// Pass through whether the result is of a packed type.
@@ -7172,7 +7251,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 
 
 		inherit_expression_dependencies(id, ptr);
 		inherit_expression_dependencies(id, ptr);
 		if (forward)
 		if (forward)
-			add_implied_read_expression(e, ptr);
+			add_implied_read_expression(*e, ptr);
 		break;
 		break;
 	}
 	}
 
 
@@ -7290,7 +7369,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 			register_impure_function_call();
 			register_impure_function_call();
 
 
 		string funexpr;
 		string funexpr;
-		vector<string> arglist;
+		SmallVector<string> arglist;
 		funexpr += to_name(func) + "(";
 		funexpr += to_name(func) + "(";
 
 
 		if (emit_return_value_as_argument)
 		if (emit_return_value_as_argument)
@@ -7419,10 +7498,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		{
 		{
 			// We cannot construct array of arrays because we cannot treat the inputs
 			// We cannot construct array of arrays because we cannot treat the inputs
 			// as value types. Need to declare the array-of-arrays, and copy in elements one by one.
 			// as value types. Need to declare the array-of-arrays, and copy in elements one by one.
-			forced_temporaries.insert(id);
-			auto &flags = ir.meta[id].decoration.decoration_flags;
-			statement(flags_to_precision_qualifiers_glsl(out_type, flags), variable_decl(out_type, to_name(id)), ";");
-			set<SPIRExpression>(id, to_name(id), result_type, true);
+			emit_uninitialized_temporary_expression(result_type, id);
 			for (uint32_t i = 0; i < length; i++)
 			for (uint32_t i = 0; i < length; i++)
 				emit_array_copy(join(to_expression(id), "[", i, "]"), elems[i]);
 				emit_array_copy(join(to_expression(id), "[", i, "]"), elems[i]);
 		}
 		}
@@ -7656,7 +7732,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 			trivial_forward = !expression_is_forwarded(vec0) && !expression_is_forwarded(vec1);
 			trivial_forward = !expression_is_forwarded(vec0) && !expression_is_forwarded(vec1);
 
 
 			// Constructor style and shuffling from two different vectors.
 			// Constructor style and shuffling from two different vectors.
-			vector<string> args;
+			SmallVector<string> args;
 			for (uint32_t i = 0; i < length; i++)
 			for (uint32_t i = 0; i < length; i++)
 			{
 			{
 				if (elems[i] == 0xffffffffu)
 				if (elems[i] == 0xffffffffu)
@@ -7821,12 +7897,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		uint32_t result_id = ops[1];
 		uint32_t result_id = ops[1];
 		uint32_t op0 = ops[2];
 		uint32_t op0 = ops[2];
 		uint32_t op1 = ops[3];
 		uint32_t op1 = ops[3];
-		forced_temporaries.insert(result_id);
 		auto &type = get<SPIRType>(result_type);
 		auto &type = get<SPIRType>(result_type);
-		auto &flags = ir.meta[result_id].decoration.decoration_flags;
-		statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), ";");
-		set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
-
+		emit_uninitialized_temporary_expression(result_type, result_id);
 		const char *op = opcode == OpIAddCarry ? "uaddCarry" : "usubBorrow";
 		const char *op = opcode == OpIAddCarry ? "uaddCarry" : "usubBorrow";
 
 
 		statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", op, "(", to_expression(op0), ", ",
 		statement(to_expression(result_id), ".", to_member_name(type, 0), " = ", op, "(", to_expression(op0), ", ",
@@ -7848,10 +7920,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		uint32_t op1 = ops[3];
 		uint32_t op1 = ops[3];
 		forced_temporaries.insert(result_id);
 		forced_temporaries.insert(result_id);
 		auto &type = get<SPIRType>(result_type);
 		auto &type = get<SPIRType>(result_type);
-		auto &flags = ir.meta[result_id].decoration.decoration_flags;
-		statement(flags_to_precision_qualifiers_glsl(type, flags), variable_decl(type, to_name(result_id)), ";");
-		set<SPIRExpression>(result_id, to_name(result_id), result_type, true);
-
+		emit_uninitialized_temporary_expression(result_type, result_id);
 		const char *op = opcode == OpUMulExtended ? "umulExtended" : "imulExtended";
 		const char *op = opcode == OpUMulExtended ? "umulExtended" : "imulExtended";
 
 
 		statement(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(result_id), ".",
 		statement(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(result_id), ".",
@@ -8107,12 +8176,45 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 	}
 	}
 
 
 	// Conversion
 	// Conversion
-	case OpConvertFToU:
-	case OpConvertFToS:
+	case OpSConvert:
 	case OpConvertSToF:
 	case OpConvertSToF:
-	case OpConvertUToF:
 	case OpUConvert:
 	case OpUConvert:
-	case OpSConvert:
+	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<SPIRType>(result_type);
+		auto &arg_type = expression_type(ops[2]);
+		auto func = type_to_glsl_constructor(type);
+
+		// If we're sign-extending or zero-extending, we need to make sure we cast from the correct type.
+		// For truncation, it does not matter, so don't emit useless casts.
+		if (arg_type.width < type.width)
+			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<SPIRType>(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 OpFConvert:
 	case OpFConvert:
 	{
 	{
 		uint32_t result_type = ops[0];
 		uint32_t result_type = ops[0];
@@ -9191,6 +9293,10 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 		statement("executeCallableNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");");
 		statement("executeCallableNV(", to_expression(ops[0]), ", ", to_expression(ops[1]), ");");
 		break;
 		break;
 
 
+	case OpUndef:
+		// Undefined value has been declared.
+		break;
+
 	default:
 	default:
 		statement("// unimplemented op ", instruction.op);
 		statement("// unimplemented op ", instruction.op);
 		break;
 		break;
@@ -9205,7 +9311,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
 // access to shader input content from within a function (eg. Metal). Each additional
 // 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
 // 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.
 // functions and function calls all the way up the nesting chain.
-void CompilerGLSL::append_global_func_args(const SPIRFunction &func, uint32_t index, vector<string> &arglist)
+void CompilerGLSL::append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector<string> &arglist)
 {
 {
 	auto &args = func.arguments;
 	auto &args = func.arguments;
 	uint32_t arg_cnt = uint32_t(args.size());
 	uint32_t arg_cnt = uint32_t(args.size());
@@ -10040,7 +10146,7 @@ void CompilerGLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret
 		decl += to_name(func.self);
 		decl += to_name(func.self);
 
 
 	decl += "(";
 	decl += "(";
-	vector<string> arglist;
+	SmallVector<string> arglist;
 	for (auto &arg : func.arguments)
 	for (auto &arg : func.arguments)
 	{
 	{
 		// Do not pass in separate images or samplers if we're remapping
 		// Do not pass in separate images or samplers if we're remapping
@@ -10498,7 +10604,7 @@ string CompilerGLSL::emit_continue_block(uint32_t continue_block, bool follow_tr
 	// if we have to emit temporaries.
 	// if we have to emit temporaries.
 	current_continue_block = block;
 	current_continue_block = block;
 
 
-	vector<string> statements;
+	SmallVector<string> statements;
 
 
 	// Capture all statements into our list.
 	// Capture all statements into our list.
 	auto *old = redirect_statement;
 	auto *old = redirect_statement;
@@ -10821,7 +10927,7 @@ void CompilerGLSL::flush_undeclared_variables(SPIRBlock &block)
 		flush_variable_declaration(v);
 		flush_variable_declaration(v);
 }
 }
 
 
-void CompilerGLSL::emit_hoisted_temporaries(vector<pair<uint32_t, uint32_t>> &temporaries)
+void CompilerGLSL::emit_hoisted_temporaries(SmallVector<pair<uint32_t, uint32_t>> &temporaries)
 {
 {
 	// If we need to force temporaries for certain IDs due to continue blocks, do it before starting loop header.
 	// 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.
 	// Need to sort these to ensure that reference output is stable.
@@ -11196,7 +11302,7 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
 			assert(block.merge == SPIRBlock::MergeSelection);
 			assert(block.merge == SPIRBlock::MergeSelection);
 			branch_to_continue(block.self, block.next_block);
 			branch_to_continue(block.self, block.next_block);
 		}
 		}
-		else
+		else if (block.self != block.next_block)
 			emit_block_chain(get<SPIRBlock>(block.next_block));
 			emit_block_chain(get<SPIRBlock>(block.next_block));
 	}
 	}
 
 
@@ -11371,8 +11477,7 @@ void CompilerGLSL::unroll_array_from_complex_load(uint32_t target_id, uint32_t s
 	}
 	}
 }
 }
 
 
-void CompilerGLSL::bitcast_from_builtin_load(uint32_t source_id, std::string &expr,
-                                             const SPIRType &expr_type)
+void CompilerGLSL::bitcast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type)
 {
 {
 	auto *var = maybe_get_backing_variable(source_id);
 	auto *var = maybe_get_backing_variable(source_id);
 	if (var)
 	if (var)
@@ -11419,8 +11524,7 @@ void CompilerGLSL::bitcast_from_builtin_load(uint32_t source_id, std::string &ex
 		expr = bitcast_expression(expr_type, expected_type, expr);
 		expr = bitcast_expression(expr_type, expected_type, expr);
 }
 }
 
 
-void CompilerGLSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr,
-                                            const SPIRType &expr_type)
+void CompilerGLSL::bitcast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type)
 {
 {
 	// Only interested in standalone builtin variables.
 	// Only interested in standalone builtin variables.
 	if (!has_decoration(target_id, DecorationBuiltIn))
 	if (!has_decoration(target_id, DecorationBuiltIn))

+ 19 - 18
3rdparty/spirv-cross/spirv_glsl.hpp

@@ -17,9 +17,8 @@
 #ifndef SPIRV_CROSS_GLSL_HPP
 #ifndef SPIRV_CROSS_GLSL_HPP
 #define SPIRV_CROSS_GLSL_HPP
 #define SPIRV_CROSS_GLSL_HPP
 
 
-#include "spirv_cross.hpp"
 #include "GLSL.std.450.h"
 #include "GLSL.std.450.h"
-#include <sstream>
+#include "spirv_cross.hpp"
 #include <unordered_map>
 #include <unordered_map>
 #include <unordered_set>
 #include <unordered_set>
 #include <utility>
 #include <utility>
@@ -142,7 +141,7 @@ public:
 	}
 	}
 
 
 	explicit CompilerGLSL(std::vector<uint32_t> spirv_)
 	explicit CompilerGLSL(std::vector<uint32_t> spirv_)
-	    : Compiler(move(spirv_))
+	    : Compiler(std::move(spirv_))
 	{
 	{
 		init();
 		init();
 	}
 	}
@@ -230,7 +229,7 @@ protected:
 	virtual void emit_spv_amd_gcn_shader_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
 	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);
 	                                        uint32_t count);
 	virtual void emit_header();
 	virtual void emit_header();
-	void build_workgroup_size(std::vector<std::string> &arguments, const SpecializationConstant &x,
+	void build_workgroup_size(SmallVector<std::string> &arguments, const SpecializationConstant &x,
 	                          const SpecializationConstant &y, const SpecializationConstant &z);
 	                          const SpecializationConstant &y, const SpecializationConstant &z);
 
 
 	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_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id);
@@ -260,19 +259,19 @@ protected:
 	virtual void emit_uniform(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 packed_type_id);
 	virtual std::string unpack_expression_type(std::string expr_str, const SPIRType &type, uint32_t packed_type_id);
 
 
-	std::unique_ptr<std::ostringstream> buffer;
+	StringStream<> buffer;
 
 
 	template <typename T>
 	template <typename T>
 	inline void statement_inner(T &&t)
 	inline void statement_inner(T &&t)
 	{
 	{
-		(*buffer) << std::forward<T>(t);
+		buffer << std::forward<T>(t);
 		statement_count++;
 		statement_count++;
 	}
 	}
 
 
 	template <typename T, typename... Ts>
 	template <typename T, typename... Ts>
 	inline void statement_inner(T &&t, Ts &&... ts)
 	inline void statement_inner(T &&t, Ts &&... ts)
 	{
 	{
-		(*buffer) << std::forward<T>(t);
+		buffer << std::forward<T>(t);
 		statement_count++;
 		statement_count++;
 		statement_inner(std::forward<Ts>(ts)...);
 		statement_inner(std::forward<Ts>(ts)...);
 	}
 	}
@@ -296,9 +295,9 @@ protected:
 		else
 		else
 		{
 		{
 			for (uint32_t i = 0; i < indent; i++)
 			for (uint32_t i = 0; i < indent; i++)
-				(*buffer) << "    ";
+				buffer << "    ";
 			statement_inner(std::forward<Ts>(ts)...);
 			statement_inner(std::forward<Ts>(ts)...);
-			(*buffer) << '\n';
+			buffer << '\n';
 		}
 		}
 	}
 	}
 
 
@@ -314,7 +313,7 @@ protected:
 	// Used for implementing continue blocks where
 	// Used for implementing continue blocks where
 	// we want to obtain a list of statements we can merge
 	// we want to obtain a list of statements we can merge
 	// on a single line separated by comma.
 	// on a single line separated by comma.
-	std::vector<std::string> *redirect_statement = nullptr;
+	SmallVector<std::string> *redirect_statement = nullptr;
 	const SPIRBlock *current_continue_block = nullptr;
 	const SPIRBlock *current_continue_block = nullptr;
 
 
 	void begin_scope();
 	void begin_scope();
@@ -406,7 +405,7 @@ protected:
 	void emit_interface_block(const SPIRVariable &type);
 	void emit_interface_block(const SPIRVariable &type);
 	void emit_flattened_io_block(const SPIRVariable &var, const char *qual);
 	void emit_flattened_io_block(const SPIRVariable &var, const char *qual);
 	void emit_block_chain(SPIRBlock &block);
 	void emit_block_chain(SPIRBlock &block);
-	void emit_hoisted_temporaries(std::vector<std::pair<uint32_t, uint32_t>> &temporaries);
+	void emit_hoisted_temporaries(SmallVector<std::pair<uint32_t, uint32_t>> &temporaries);
 	std::string constant_value_macro_name(uint32_t id);
 	std::string constant_value_macro_name(uint32_t id);
 	void emit_constant(const SPIRConstant &constant);
 	void emit_constant(const SPIRConstant &constant);
 	void emit_specialization_constant_op(const SPIRConstantOp &constant);
 	void emit_specialization_constant_op(const SPIRConstantOp &constant);
@@ -437,8 +436,8 @@ protected:
 	                             SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type);
 	                             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,
 	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);
 	                              SPIRType::BaseType input_type, bool skip_cast_if_equal_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_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_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op);
 	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_unrolled_unary_op(uint32_t result_type, uint32_t result_id, uint32_t operand, const char *op);
@@ -484,7 +483,9 @@ protected:
 	const char *index_to_swizzle(uint32_t index);
 	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 remap_swizzle(const SPIRType &result_type, uint32_t input_components, const std::string &expr);
 	std::string declare_temporary(uint32_t type, uint32_t id);
 	std::string declare_temporary(uint32_t type, uint32_t id);
-	void append_global_func_args(const SPIRFunction &func, uint32_t index, std::vector<std::string> &arglist);
+	void emit_uninitialized_temporary(uint32_t type, uint32_t id);
+	SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id);
+	void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector<std::string> &arglist);
 	std::string to_expression(uint32_t id, bool register_expression_read = true);
 	std::string to_expression(uint32_t id, bool register_expression_read = true);
 	std::string to_enclosed_expression(uint32_t id, bool register_expression_read = true);
 	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_expression(uint32_t id, bool register_expression_read = true);
@@ -560,15 +561,15 @@ protected:
 	std::unordered_map<uint32_t, uint32_t> expression_usage_counts;
 	std::unordered_map<uint32_t, uint32_t> expression_usage_counts;
 	void track_expression_read(uint32_t id);
 	void track_expression_read(uint32_t id);
 
 
-	std::vector<std::string> forced_extensions;
-	std::vector<std::string> header_lines;
+	SmallVector<std::string> forced_extensions;
+	SmallVector<std::string> header_lines;
 
 
 	// Used when expressions emit extra opcodes with their own unique IDs,
 	// Used when expressions emit extra opcodes with their own unique IDs,
 	// and we need to reuse the IDs across recompilation loops.
 	// and we need to reuse the IDs across recompilation loops.
 	// Currently used by NMin/Max/Clamp implementations.
 	// Currently used by NMin/Max/Clamp implementations.
 	std::unordered_map<uint32_t, uint32_t> extra_sub_expressions;
 	std::unordered_map<uint32_t, uint32_t> extra_sub_expressions;
 
 
-	uint32_t statement_count;
+	uint32_t statement_count = 0;
 
 
 	inline bool is_legacy() const
 	inline bool is_legacy() const
 	{
 	{
@@ -653,6 +654,6 @@ protected:
 private:
 private:
 	void init();
 	void init();
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 24 - 16
3rdparty/spirv-cross/spirv_hlsl.cpp

@@ -723,17 +723,25 @@ string CompilerHLSL::to_interpolation_qualifiers(const Bitset &flags)
 	return res;
 	return res;
 }
 }
 
 
-std::string CompilerHLSL::to_semantic(uint32_t vertex_location)
+std::string CompilerHLSL::to_semantic(uint32_t location, ExecutionModel em, StorageClass sc)
 {
 {
-	for (auto &attribute : remap_vertex_attributes)
-		if (attribute.location == vertex_location)
-			return attribute.semantic;
+	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;
+	}
 
 
-	return join("TEXCOORD", vertex_location);
+	// Not a vertex attribute, or no remap_vertex_attributes entry.
+	return join("TEXCOORD", location);
 }
 }
 
 
 void CompilerHLSL::emit_io_block(const SPIRVariable &var)
 void CompilerHLSL::emit_io_block(const SPIRVariable &var)
 {
 {
+	auto &execution = get_entry_point();
+
 	auto &type = get<SPIRType>(var.basetype);
 	auto &type = get<SPIRType>(var.basetype);
 	add_resource_name(type.self);
 	add_resource_name(type.self);
 
 
@@ -749,7 +757,7 @@ void CompilerHLSL::emit_io_block(const SPIRVariable &var)
 		if (has_member_decoration(type.self, i, DecorationLocation))
 		if (has_member_decoration(type.self, i, DecorationLocation))
 		{
 		{
 			uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
 			uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
-			semantic = join(" : ", to_semantic(location));
+			semantic = join(" : ", to_semantic(location, execution.model, var.storage));
 		}
 		}
 		else
 		else
 		{
 		{
@@ -757,7 +765,7 @@ void CompilerHLSL::emit_io_block(const SPIRVariable &var)
 			// There could be a conflict if the block members partially specialize the locations.
 			// There could be a conflict if the block members partially specialize the locations.
 			// It is unclear how SPIR-V deals with this. Assume this does not happen for now.
 			// It is unclear how SPIR-V deals with this. Assume this does not happen for now.
 			uint32_t location = base_location + i;
 			uint32_t location = base_location + i;
-			semantic = join(" : ", to_semantic(location));
+			semantic = join(" : ", to_semantic(location, execution.model, var.storage));
 		}
 		}
 
 
 		add_member_name(type, i);
 		add_member_name(type, i);
@@ -820,7 +828,7 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
 			location_number = get_vacant_location();
 			location_number = get_vacant_location();
 
 
 		// Allow semantic remap if specified.
 		// Allow semantic remap if specified.
-		auto semantic = to_semantic(location_number);
+		auto semantic = to_semantic(location_number, execution.model, var.storage);
 
 
 		if (need_matrix_unroll && type.columns > 1)
 		if (need_matrix_unroll && type.columns > 1)
 		{
 		{
@@ -1210,8 +1218,8 @@ void CompilerHLSL::emit_resources()
 	require_output = false;
 	require_output = false;
 	unordered_set<uint32_t> active_inputs;
 	unordered_set<uint32_t> active_inputs;
 	unordered_set<uint32_t> active_outputs;
 	unordered_set<uint32_t> active_outputs;
-	vector<SPIRVariable *> input_variables;
-	vector<SPIRVariable *> output_variables;
+	SmallVector<SPIRVariable *> input_variables;
+	SmallVector<SPIRVariable *> output_variables;
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
 		auto &type = this->get<SPIRType>(var.basetype);
 		auto &type = this->get<SPIRType>(var.basetype);
 		bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
 		bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
@@ -2024,7 +2032,7 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret
 		decl += to_name(func.self);
 		decl += to_name(func.self);
 
 
 	decl += "(";
 	decl += "(";
-	vector<string> arglist;
+	SmallVector<string> arglist;
 
 
 	if (!type.array.empty())
 	if (!type.array.empty())
 	{
 	{
@@ -2092,7 +2100,7 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret
 
 
 void CompilerHLSL::emit_hlsl_entry_point()
 void CompilerHLSL::emit_hlsl_entry_point()
 {
 {
-	vector<string> arguments;
+	SmallVector<string> arguments;
 
 
 	if (require_input)
 	if (require_input)
 		arguments.push_back("SPIRV_Cross_Input stage_input");
 		arguments.push_back("SPIRV_Cross_Input stage_input");
@@ -2425,7 +2433,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
 	auto op = static_cast<Op>(i.op);
 	auto op = static_cast<Op>(i.op);
 	uint32_t length = i.length;
 	uint32_t length = i.length;
 
 
-	vector<uint32_t> inherited_expressions;
+	SmallVector<uint32_t> inherited_expressions;
 
 
 	uint32_t result_type = ops[0];
 	uint32_t result_type = ops[0];
 	uint32_t id = ops[1];
 	uint32_t id = ops[1];
@@ -4565,7 +4573,7 @@ void CompilerHLSL::require_texture_query_variant(const SPIRType &type)
 	}
 	}
 }
 }
 
 
-void CompilerHLSL::set_root_constant_layouts(vector<RootConstants> layout)
+void CompilerHLSL::set_root_constant_layouts(std::vector<RootConstants> layout)
 {
 {
 	root_constants_layout = move(layout);
 	root_constants_layout = move(layout);
 }
 }
@@ -4664,7 +4672,7 @@ string CompilerHLSL::compile()
 		reset();
 		reset();
 
 
 		// Move constructor for this type is broken on GCC 4.9 ...
 		// Move constructor for this type is broken on GCC 4.9 ...
-		buffer = unique_ptr<ostringstream>(new ostringstream());
+		buffer.reset();
 
 
 		emit_header();
 		emit_header();
 		emit_resources();
 		emit_resources();
@@ -4678,7 +4686,7 @@ string CompilerHLSL::compile()
 	// Entry point in HLSL is always main() for the time being.
 	// Entry point in HLSL is always main() for the time being.
 	get_entry_point().name = "main";
 	get_entry_point().name = "main";
 
 
-	return buffer->str();
+	return buffer.str();
 }
 }
 
 
 void CompilerHLSL::emit_block_hints(const SPIRBlock &block)
 void CompilerHLSL::emit_block_hints(const SPIRBlock &block)

+ 4 - 5
3rdparty/spirv-cross/spirv_hlsl.hpp

@@ -19,7 +19,6 @@
 
 
 #include "spirv_glsl.hpp"
 #include "spirv_glsl.hpp"
 #include <utility>
 #include <utility>
-#include <vector>
 
 
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
@@ -63,7 +62,7 @@ public:
 	};
 	};
 
 
 	explicit CompilerHLSL(std::vector<uint32_t> spirv_)
 	explicit CompilerHLSL(std::vector<uint32_t> spirv_)
-	    : CompilerGLSL(move(spirv_))
+	    : CompilerGLSL(std::move(spirv_))
 	{
 	{
 	}
 	}
 
 
@@ -209,12 +208,12 @@ private:
 	void emit_builtin_variables();
 	void emit_builtin_variables();
 	bool require_output = false;
 	bool require_output = false;
 	bool require_input = false;
 	bool require_input = false;
-	std::vector<HLSLVertexAttributeRemap> remap_vertex_attributes;
+	SmallVector<HLSLVertexAttributeRemap> remap_vertex_attributes;
 
 
 	uint32_t type_to_consumed_locations(const SPIRType &type) const;
 	uint32_t type_to_consumed_locations(const SPIRType &type) const;
 
 
 	void emit_io_block(const SPIRVariable &var);
 	void emit_io_block(const SPIRVariable &var);
-	std::string to_semantic(uint32_t vertex_location);
+	std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc);
 
 
 	uint32_t num_workgroups_builtin = 0;
 	uint32_t num_workgroups_builtin = 0;
 
 
@@ -222,6 +221,6 @@ private:
 	// when translating push constant ranges.
 	// when translating push constant ranges.
 	std::vector<RootConstants> root_constants_layout;
 	std::vector<RootConstants> root_constants_layout;
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 40 - 15
3rdparty/spirv-cross/spirv_msl.cpp

@@ -30,7 +30,7 @@ static const uint32_t k_unknown_component = ~0u;
 
 
 static const uint32_t k_aux_mbr_idx_swizzle_const = 0u;
 static const uint32_t k_aux_mbr_idx_swizzle_const = 0u;
 
 
-CompilerMSL::CompilerMSL(vector<uint32_t> spirv_)
+CompilerMSL::CompilerMSL(std::vector<uint32_t> spirv_)
     : CompilerGLSL(move(spirv_))
     : CompilerGLSL(move(spirv_))
 {
 {
 }
 }
@@ -423,7 +423,7 @@ void CompilerMSL::emit_entry_point_declarations()
 		if (type.basetype == SPIRType::Sampler)
 		if (type.basetype == SPIRType::Sampler)
 			add_resource_name(samp.first);
 			add_resource_name(samp.first);
 
 
-		vector<string> args;
+		SmallVector<string> args;
 		auto &s = samp.second;
 		auto &s = samp.second;
 
 
 		if (s.coord != MSL_SAMPLER_COORD_NORMALIZED)
 		if (s.coord != MSL_SAMPLER_COORD_NORMALIZED)
@@ -659,7 +659,7 @@ string CompilerMSL::compile()
 		next_metal_resource_index_sampler = 0;
 		next_metal_resource_index_sampler = 0;
 
 
 		// Move constructor for this type is broken on GCC 4.9 ...
 		// Move constructor for this type is broken on GCC 4.9 ...
-		buffer = unique_ptr<ostringstream>(new ostringstream());
+		buffer.reset();
 
 
 		emit_header();
 		emit_header();
 		emit_specialization_constants_and_structs();
 		emit_specialization_constants_and_structs();
@@ -670,7 +670,7 @@ string CompilerMSL::compile()
 		pass_count++;
 		pass_count++;
 	} while (is_forcing_recompilation());
 	} while (is_forcing_recompilation());
 
 
-	return buffer->str();
+	return buffer.str();
 }
 }
 
 
 // Register the need to output any custom functions.
 // Register the need to output any custom functions.
@@ -679,8 +679,7 @@ void CompilerMSL::preprocess_op_codes()
 	OpCodePreprocessor preproc(*this);
 	OpCodePreprocessor preproc(*this);
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), preproc);
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), preproc);
 
 
-	if (preproc.suppress_missing_prototypes)
-		add_pragma_line("#pragma clang diagnostic ignored \"-Wmissing-prototypes\"");
+	suppress_missing_prototypes = preproc.suppress_missing_prototypes;
 
 
 	if (preproc.uses_atomics)
 	if (preproc.uses_atomics)
 	{
 	{
@@ -1823,7 +1822,7 @@ void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t
 uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
 uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
 {
 {
 	// Accumulate the variables that should appear in the interface struct
 	// Accumulate the variables that should appear in the interface struct
-	vector<SPIRVariable *> vars;
+	SmallVector<SPIRVariable *> vars;
 	bool incl_builtins = (storage == StorageClassOutput || is_tessellation_shader());
 	bool incl_builtins = (storage == StorageClassOutput || is_tessellation_shader());
 
 
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) {
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t var_id, SPIRVariable &var) {
@@ -2351,10 +2350,13 @@ string CompilerMSL::unpack_expression_type(string expr_str, const SPIRType &type
 // Emits the file header info
 // Emits the file header info
 void CompilerMSL::emit_header()
 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)
+		statement("#pragma clang diagnostic ignored \"-Wmissing-prototypes\"");
 	for (auto &pragma : pragma_lines)
 	for (auto &pragma : pragma_lines)
 		statement(pragma);
 		statement(pragma);
 
 
-	if (!pragma_lines.empty())
+	if (!pragma_lines.empty() || suppress_missing_prototypes)
 		statement("");
 		statement("");
 
 
 	statement("#include <metal_stdlib>");
 	statement("#include <metal_stdlib>");
@@ -2952,8 +2954,8 @@ void CompilerMSL::emit_specialization_constants_and_structs()
 				// TODO: This can be expressed as a [[threads_per_threadgroup]] input semantic, but we need to know
 				// 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 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.
 				// The work group size may be a specialization constant.
-				statement("constant uint3 ", builtin_to_glsl(BuiltInWorkgroupSize, StorageClassWorkgroup), " [[maybe_unused]] = ",
-				          constant_expression(get<SPIRConstant>(workgroup_size_id)), ";");
+				statement("constant uint3 ", builtin_to_glsl(BuiltInWorkgroupSize, StorageClassWorkgroup),
+				          " [[maybe_unused]] = ", constant_expression(get<SPIRConstant>(workgroup_size_id)), ";");
 				emitted = true;
 				emitted = true;
 			}
 			}
 			else if (c.specialization)
 			else if (c.specialization)
@@ -3080,7 +3082,7 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
 	     get_variable_data_type(*var).basetype == SPIRType::Struct))
 	     get_variable_data_type(*var).basetype == SPIRType::Struct))
 	{
 	{
 		AccessChainMeta meta;
 		AccessChainMeta meta;
-		std::vector<uint32_t> indices;
+		SmallVector<uint32_t> indices;
 		uint32_t next_id = ir.increase_bound_by(2);
 		uint32_t next_id = ir.increase_bound_by(2);
 
 
 		indices.reserve(length - 3 + 1);
 		indices.reserve(length - 3 + 1);
@@ -3961,6 +3963,29 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
 		is_constant = true;
 		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.
+	if (type.array.size() > 1)
+	{
+		if (type.array.size() > SPVFuncImplArrayCopyMultidimMax)
+			SPIRV_CROSS_THROW("Cannot support this many dimensions for arrays of arrays.");
+		auto func = static_cast<SPVFuncImpl>(SPVFuncImplArrayCopyMultidimBase + type.array.size());
+		if (spv_function_implementations.count(func) == 0)
+		{
+			spv_function_implementations.insert(func);
+			suppress_missing_prototypes = true;
+			force_recompile();
+		}
+	}
+	else if (spv_function_implementations.count(SPVFuncImplArrayCopy) == 0)
+	{
+		spv_function_implementations.insert(SPVFuncImplArrayCopy);
+		suppress_missing_prototypes = true;
+		force_recompile();
+	}
+
 	const char *tag = is_constant ? "FromConstant" : "FromStack";
 	const char *tag = is_constant ? "FromConstant" : "FromStack";
 	statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
 	statement("spvArrayCopy", tag, type.array.size(), "(", lhs, ", ", to_expression(rhs_id), ");");
 }
 }
@@ -4943,7 +4968,7 @@ void CompilerMSL::add_convert_row_major_matrix_function(uint32_t cols, uint32_t
 	auto rslt = spv_function_implementations.insert(spv_func);
 	auto rslt = spv_function_implementations.insert(spv_func);
 	if (rslt.second)
 	if (rslt.second)
 	{
 	{
-		add_pragma_line("#pragma clang diagnostic ignored \"-Wmissing-prototypes\"");
+		suppress_missing_prototypes = true;
 		force_recompile();
 		force_recompile();
 	}
 	}
 }
 }
@@ -5667,7 +5692,7 @@ void CompilerMSL::entry_point_args_discrete_descriptors(string &ep_args)
 		uint32_t index;
 		uint32_t index;
 	};
 	};
 
 
-	vector<Resource> resources;
+	SmallVector<Resource> resources;
 
 
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
 		if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||
 		if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||
@@ -7383,7 +7408,7 @@ void CompilerMSL::MemberSorter::sort()
 	// Create a temporary array of consecutive member indices and sort it based on how
 	// 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.
 	// the members should be reordered, based on builtin and sorting aspect meta info.
 	size_t mbr_cnt = type.member_types.size();
 	size_t mbr_cnt = type.member_types.size();
-	vector<uint32_t> mbr_idxs(mbr_cnt);
+	SmallVector<uint32_t> mbr_idxs(mbr_cnt);
 	iota(mbr_idxs.begin(), mbr_idxs.end(), 0); // Fill with consecutive indices
 	iota(mbr_idxs.begin(), mbr_idxs.end(), 0); // Fill with consecutive indices
 	std::sort(mbr_idxs.begin(), mbr_idxs.end(), *this); // Sort member indices based on sorting aspect
 	std::sort(mbr_idxs.begin(), mbr_idxs.end(), *this); // Sort member indices based on sorting aspect
 
 
@@ -7577,7 +7602,7 @@ void CompilerMSL::analyze_argument_buffers()
 		SPIRType::BaseType basetype;
 		SPIRType::BaseType basetype;
 		uint32_t index;
 		uint32_t index;
 	};
 	};
-	vector<Resource> resources_in_set[kMaxArgumentBuffers];
+	SmallVector<Resource> resources_in_set[kMaxArgumentBuffers];
 
 
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t self, SPIRVariable &var) {
 	ir.for_each_typed_id<SPIRVariable>([&](uint32_t self, SPIRVariable &var) {
 		if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||
 		if ((var.storage == StorageClassUniform || var.storage == StorageClassUniformConstant ||

+ 6 - 5
3rdparty/spirv-cross/spirv_msl.hpp

@@ -22,7 +22,6 @@
 #include <set>
 #include <set>
 #include <unordered_map>
 #include <unordered_map>
 #include <unordered_set>
 #include <unordered_set>
-#include <vector>
 
 
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
@@ -495,9 +494,9 @@ protected:
 	std::unordered_map<MSLStructMemberKey, uint32_t> struct_member_padding;
 	std::unordered_map<MSLStructMemberKey, uint32_t> struct_member_padding;
 	std::set<std::string> pragma_lines;
 	std::set<std::string> pragma_lines;
 	std::set<std::string> typedef_lines;
 	std::set<std::string> typedef_lines;
-	std::vector<uint32_t> vars_needing_early_declaration;
+	SmallVector<uint32_t> vars_needing_early_declaration;
 
 
-	std::vector<std::pair<MSLResourceBinding, bool>> resource_bindings;
+	SmallVector<std::pair<MSLResourceBinding, bool>> resource_bindings;
 	uint32_t next_metal_resource_index_buffer = 0;
 	uint32_t next_metal_resource_index_buffer = 0;
 	uint32_t next_metal_resource_index_texture = 0;
 	uint32_t next_metal_resource_index_texture = 0;
 	uint32_t next_metal_resource_index_sampler = 0;
 	uint32_t next_metal_resource_index_sampler = 0;
@@ -530,7 +529,7 @@ protected:
 	spv::Op previous_instruction_opcode = spv::OpNop;
 	spv::Op previous_instruction_opcode = spv::OpNop;
 
 
 	std::unordered_map<uint32_t, MSLConstexprSampler> constexpr_samplers;
 	std::unordered_map<uint32_t, MSLConstexprSampler> constexpr_samplers;
-	std::vector<uint32_t> buffer_arrays;
+	SmallVector<uint32_t> buffer_arrays;
 
 
 	uint32_t argument_buffer_ids[kMaxArgumentBuffers];
 	uint32_t argument_buffer_ids[kMaxArgumentBuffers];
 	uint32_t argument_buffer_discrete_mask = 0;
 	uint32_t argument_buffer_discrete_mask = 0;
@@ -540,6 +539,8 @@ protected:
 	uint32_t get_target_components_for_fragment_location(uint32_t location) const;
 	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);
 	uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components);
 
 
+	bool suppress_missing_prototypes = false;
+
 	// OpcodeHandler that handles several MSL preprocessing operations.
 	// OpcodeHandler that handles several MSL preprocessing operations.
 	struct OpCodePreprocessor : OpcodeHandler
 	struct OpCodePreprocessor : OpcodeHandler
 	{
 	{
@@ -595,6 +596,6 @@ protected:
 		SortAspect sort_aspect;
 		SortAspect sort_aspect;
 	};
 	};
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 7 - 5
3rdparty/spirv-cross/spirv_parser.cpp

@@ -22,7 +22,7 @@ using namespace spv;
 
 
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
-Parser::Parser(std::vector<uint32_t> spirv)
+Parser::Parser(vector<uint32_t> spirv)
 {
 {
 	ir.spirv = move(spirv);
 	ir.spirv = move(spirv);
 }
 }
@@ -88,7 +88,7 @@ void Parser::parse()
 
 
 	uint32_t offset = 5;
 	uint32_t offset = 5;
 
 
-	vector<Instruction> instructions;
+	SmallVector<Instruction> instructions;
 	while (offset < len)
 	while (offset < len)
 	{
 	{
 		Instruction instr = {};
 		Instruction instr = {};
@@ -207,6 +207,8 @@ void Parser::parse(const Instruction &instruction)
 		uint32_t result_type = ops[0];
 		uint32_t result_type = ops[0];
 		uint32_t id = ops[1];
 		uint32_t id = ops[1];
 		set<SPIRUndef>(id, result_type);
 		set<SPIRUndef>(id, result_type);
+		if (current_block)
+			current_block->ops.push_back(instruction);
 		break;
 		break;
 	}
 	}
 
 
@@ -1095,7 +1097,7 @@ void Parser::make_constant_null(uint32_t id, uint32_t type)
 		if (!constant_type.array_size_literal.back())
 		if (!constant_type.array_size_literal.back())
 			SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
 			SPIRV_CROSS_THROW("Array size of OpConstantNull must be a literal.");
 
 
-		vector<uint32_t> elements(constant_type.array.back());
+		SmallVector<uint32_t> elements(constant_type.array.back());
 		for (uint32_t i = 0; i < constant_type.array.back(); i++)
 		for (uint32_t i = 0; i < constant_type.array.back(); i++)
 			elements[i] = parent_id;
 			elements[i] = parent_id;
 		set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
 		set<SPIRConstant>(id, type, elements.data(), uint32_t(elements.size()), false);
@@ -1103,7 +1105,7 @@ void Parser::make_constant_null(uint32_t id, uint32_t type)
 	else if (!constant_type.member_types.empty())
 	else if (!constant_type.member_types.empty())
 	{
 	{
 		uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size()));
 		uint32_t member_ids = ir.increase_bound_by(uint32_t(constant_type.member_types.size()));
-		vector<uint32_t> elements(constant_type.member_types.size());
+		SmallVector<uint32_t> elements(constant_type.member_types.size());
 		for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
 		for (uint32_t i = 0; i < constant_type.member_types.size(); i++)
 		{
 		{
 			make_constant_null(member_ids + i, constant_type.member_types[i]);
 			make_constant_null(member_ids + i, constant_type.member_types[i]);
@@ -1118,4 +1120,4 @@ void Parser::make_constant_null(uint32_t id, uint32_t type)
 	}
 	}
 }
 }
 
 
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE

+ 2 - 3
3rdparty/spirv-cross/spirv_parser.hpp

@@ -19,7 +19,6 @@
 
 
 #include "spirv_cross_parsed_ir.hpp"
 #include "spirv_cross_parsed_ir.hpp"
 #include <stdint.h>
 #include <stdint.h>
-#include <vector>
 
 
 namespace SPIRV_CROSS_NAMESPACE
 namespace SPIRV_CROSS_NAMESPACE
 {
 {
@@ -84,12 +83,12 @@ private:
 	}
 	}
 
 
 	// This must be an ordered data structure so we always pick the same type aliases.
 	// This must be an ordered data structure so we always pick the same type aliases.
-	std::vector<uint32_t> global_struct_cache;
+	SmallVector<uint32_t> global_struct_cache;
 
 
 	bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
 	bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
 	bool variable_storage_is_aliased(const SPIRVariable &v) const;
 	bool variable_storage_is_aliased(const SPIRVariable &v) const;
 	void make_constant_null(uint32_t id, uint32_t type);
 	void make_constant_null(uint32_t id, uint32_t type);
 };
 };
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 10 - 4
3rdparty/spirv-cross/spirv_reflect.cpp

@@ -36,10 +36,16 @@ using Stack = std::stack<State>;
 class Stream
 class Stream
 {
 {
 	Stack stack;
 	Stack stack;
-	std::ostringstream buffer;
+	StringStream<> buffer;
 	uint32_t indent{ 0 };
 	uint32_t indent{ 0 };
+	char current_locale_radix_character = '.';
 
 
 public:
 public:
+	void set_current_locale_radix_character(char c)
+	{
+		current_locale_radix_character = c;
+	}
+
 	void begin_json_object();
 	void begin_json_object();
 	void end_json_object();
 	void end_json_object();
 	void emit_json_key(const std::string &key);
 	void emit_json_key(const std::string &key);
@@ -212,7 +218,7 @@ void Stream::emit_json_key_value(const std::string &key, int32_t value)
 void Stream::emit_json_key_value(const std::string &key, float value)
 void Stream::emit_json_key_value(const std::string &key, float value)
 {
 {
 	emit_json_key(key);
 	emit_json_key(key);
-	statement_inner(value);
+	statement_inner(convert_to_string(value, current_locale_radix_character));
 }
 }
 
 
 void Stream::emit_json_key_value(const std::string &key, bool value)
 void Stream::emit_json_key_value(const std::string &key, bool value)
@@ -247,8 +253,8 @@ void CompilerReflection::set_format(const std::string &format)
 
 
 string CompilerReflection::compile()
 string CompilerReflection::compile()
 {
 {
-	// Move constructor for this type is broken on GCC 4.9 ...
 	json_stream = std::make_shared<simple_json::Stream>();
 	json_stream = std::make_shared<simple_json::Stream>();
+	json_stream->set_current_locale_radix_character(current_locale_radix_character);
 	json_stream->begin_json_object();
 	json_stream->begin_json_object();
 	emit_entry_points();
 	emit_entry_points();
 	emit_types();
 	emit_types();
@@ -439,7 +445,7 @@ void CompilerReflection::emit_resources()
 	emit_resources("acceleration_structures", res.acceleration_structures);
 	emit_resources("acceleration_structures", res.acceleration_structures);
 }
 }
 
 
-void CompilerReflection::emit_resources(const char *tag, const vector<Resource> &resources)
+void CompilerReflection::emit_resources(const char *tag, const SmallVector<Resource> &resources)
 {
 {
 	if (resources.empty())
 	if (resources.empty())
 	{
 	{

+ 3 - 4
3rdparty/spirv-cross/spirv_reflect.hpp

@@ -19,7 +19,6 @@
 
 
 #include "spirv_glsl.hpp"
 #include "spirv_glsl.hpp"
 #include <utility>
 #include <utility>
-#include <vector>
 
 
 namespace simple_json
 namespace simple_json
 {
 {
@@ -34,7 +33,7 @@ class CompilerReflection : public CompilerGLSL
 
 
 public:
 public:
 	explicit CompilerReflection(std::vector<uint32_t> spirv_)
 	explicit CompilerReflection(std::vector<uint32_t> spirv_)
-	    : Parent(move(spirv_))
+	    : Parent(std::move(spirv_))
 	{
 	{
 		options.vulkan_semantics = true;
 		options.vulkan_semantics = true;
 	}
 	}
@@ -72,13 +71,13 @@ private:
 	void emit_type_member(const SPIRType &type, uint32_t index);
 	void emit_type_member(const SPIRType &type, uint32_t index);
 	void emit_type_member_qualifiers(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_type_array(const SPIRType &type);
-	void emit_resources(const char *tag, const std::vector<Resource> &resources);
+	void emit_resources(const char *tag, const SmallVector<Resource> &resources);
 
 
 	std::string to_member_name(const SPIRType &type, uint32_t index) const;
 	std::string to_member_name(const SPIRType &type, uint32_t index) const;
 
 
 	std::shared_ptr<simple_json::Stream> json_stream;
 	std::shared_ptr<simple_json::Stream> json_stream;
 };
 };
 
 
-} // namespace spirv_cross
+} // namespace SPIRV_CROSS_NAMESPACE
 
 
 #endif
 #endif

+ 49 - 41
3rdparty/spirv-cross/test_shaders.py

@@ -17,7 +17,8 @@ import errno
 from functools import partial
 from functools import partial
 
 
 class Paths():
 class Paths():
-    def __init__(self, glslang, spirv_as, spirv_val, spirv_opt):
+    def __init__(self, spirv_cross, glslang, spirv_as, spirv_val, spirv_opt):
+        self.spirv_cross = spirv_cross
         self.glslang = glslang
         self.glslang = glslang
         self.spirv_as = spirv_as
         self.spirv_as = spirv_as
         self.spirv_val = spirv_val
         self.spirv_val = spirv_val
@@ -138,7 +139,7 @@ def validate_shader_msl(shader, opt):
         print('Error compiling Metal shader: ' + msl_path)
         print('Error compiling Metal shader: ' + msl_path)
         raise RuntimeError('Failed to compile Metal shader')
         raise RuntimeError('Failed to compile Metal shader')
 
 
-def cross_compile_msl(shader, spirv, opt, paths):
+def cross_compile_msl(shader, spirv, opt, iterations, paths):
     spirv_path = create_temporary()
     spirv_path = create_temporary()
     msl_path = create_temporary(os.path.basename(shader))
     msl_path = create_temporary(os.path.basename(shader))
 
 
@@ -154,9 +155,9 @@ def cross_compile_msl(shader, spirv, opt, paths):
     if opt:
     if opt:
         subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
         subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
 
 
-    spirv_cross_path = './spirv-cross'
+    spirv_cross_path = paths.spirv_cross
 
 
-    msl_args = [spirv_cross_path, '--entry', 'main', '--output', msl_path, spirv_path, '--msl']
+    msl_args = [spirv_cross_path, '--entry', 'main', '--output', msl_path, spirv_path, '--msl', '--iterations', str(iterations)]
     msl_args.append('--msl-version')
     msl_args.append('--msl-version')
     msl_args.append(path_to_msl_standard_cli(shader))
     msl_args.append(path_to_msl_standard_cli(shader))
     if '.swizzle.' in shader:
     if '.swizzle.' in shader:
@@ -246,7 +247,7 @@ def shader_to_sm(shader):
     else:
     else:
         return '50'
         return '50'
 
 
-def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, paths):
+def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, iterations, paths):
     spirv_path = create_temporary()
     spirv_path = create_temporary()
     hlsl_path = create_temporary(os.path.basename(shader))
     hlsl_path = create_temporary(os.path.basename(shader))
 
 
@@ -262,10 +263,10 @@ def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, paths):
     if opt:
     if opt:
         subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
         subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
 
 
-    spirv_cross_path = './spirv-cross'
+    spirv_cross_path = paths.spirv_cross
 
 
     sm = shader_to_sm(shader)
     sm = shader_to_sm(shader)
-    subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl-enable-compat', '--hlsl', '--shader-model', sm])
+    subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', hlsl_path, spirv_path, '--hlsl-enable-compat', '--hlsl', '--shader-model', sm, '--iterations', str(iterations)])
 
 
     if not shader_is_invalid_spirv(hlsl_path):
     if not shader_is_invalid_spirv(hlsl_path):
         subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path])
         subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path])
@@ -274,7 +275,7 @@ def cross_compile_hlsl(shader, spirv, opt, force_no_external_validation, paths):
     
     
     return (spirv_path, hlsl_path)
     return (spirv_path, hlsl_path)
 
 
-def cross_compile_reflect(shader, spirv, opt, paths):
+def cross_compile_reflect(shader, spirv, opt, iterations, paths):
     spirv_path = create_temporary()
     spirv_path = create_temporary()
     reflect_path = create_temporary(os.path.basename(shader))
     reflect_path = create_temporary(os.path.basename(shader))
 
 
@@ -290,10 +291,10 @@ def cross_compile_reflect(shader, spirv, opt, paths):
     if opt:
     if opt:
         subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
         subprocess.check_call([paths.spirv_opt, '--skip-validation', '-O', '-o', spirv_path, spirv_path])
 
 
-    spirv_cross_path = './spirv-cross'
+    spirv_cross_path = paths.spirv_cross
 
 
     sm = shader_to_sm(shader)
     sm = shader_to_sm(shader)
-    subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', reflect_path, spirv_path, '--reflect'])
+    subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', reflect_path, spirv_path, '--reflect', '--iterations', str(iterations)])
     return (spirv_path, reflect_path)
     return (spirv_path, reflect_path)
 
 
 def validate_shader(shader, vulkan, paths):
 def validate_shader(shader, vulkan, paths):
@@ -302,7 +303,7 @@ def validate_shader(shader, vulkan, paths):
     else:
     else:
         subprocess.check_call([paths.glslang, shader])
         subprocess.check_call([paths.glslang, shader])
 
 
-def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt, push_ubo, paths):
+def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt, push_ubo, iterations, paths):
     spirv_path = create_temporary()
     spirv_path = create_temporary()
     glsl_path = create_temporary(os.path.basename(shader))
     glsl_path = create_temporary(os.path.basename(shader))
 
 
@@ -324,7 +325,7 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl
     if not invalid_spirv:
     if not invalid_spirv:
         subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path])
         subprocess.check_call([paths.spirv_val, '--target-env', 'vulkan1.1', spirv_path])
 
 
-    extra_args = []
+    extra_args = ['--iterations', str(iterations)]
     if eliminate:
     if eliminate:
         extra_args += ['--remove-unused-variables']
         extra_args += ['--remove-unused-variables']
     if is_legacy:
     if is_legacy:
@@ -338,7 +339,7 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl
     if push_ubo:
     if push_ubo:
         extra_args += ['--glsl-emit-push-constant-as-ubo']
         extra_args += ['--glsl-emit-push-constant-as-ubo']
 
 
-    spirv_cross_path = './spirv-cross'
+    spirv_cross_path = paths.spirv_cross
 
 
     # A shader might not be possible to make valid GLSL from, skip validation for this case.
     # A shader might not be possible to make valid GLSL from, skip validation for this case.
     if not ('nocompat' in glsl_path):
     if not ('nocompat' in glsl_path):
@@ -391,8 +392,8 @@ def json_ordered(obj):
 def json_compare(json_a, json_b):
 def json_compare(json_a, json_b):
     return json_ordered(json_a) == json_ordered(json_b)
     return json_ordered(json_a) == json_ordered(json_b)
 
 
-def regression_check_reflect(shader, json_file, update, keep, opt):
-    reference = reference_path(shader[0], shader[1], opt) + '.json'
+def regression_check_reflect(shader, json_file, args):
+    reference = reference_path(shader[0], shader[1], args.opt) + '.json'
     joined_path = os.path.join(shader[0], shader[1])
     joined_path = os.path.join(shader[0], shader[1])
     print('Reference shader reflection path:', reference)
     print('Reference shader reflection path:', reference)
     if os.path.exists(reference):
     if os.path.exists(reference):
@@ -404,7 +405,7 @@ def regression_check_reflect(shader, json_file, update, keep, opt):
         with open(reference) as f:
         with open(reference) as f:
             expected = json.load(f)
             expected = json.load(f)
         if (json_compare(actual, expected) != True):
         if (json_compare(actual, expected) != True):
-            if update:
+            if args.update:
                 print('Generated reflection json has changed for {}!'.format(reference))
                 print('Generated reflection json has changed for {}!'.format(reference))
                 # If we expect changes, update the reference file.
                 # If we expect changes, update the reference file.
                 if os.path.exists(reference):
                 if os.path.exists(reference):
@@ -422,7 +423,7 @@ def regression_check_reflect(shader, json_file, update, keep, opt):
                     print('')
                     print('')
 
 
                 # Otherwise, fail the test. Keep the shader file around so we can inspect.
                 # Otherwise, fail the test. Keep the shader file around so we can inspect.
-                if not keep:
+                if not args.keep:
                     remove_file(json_file)
                     remove_file(json_file)
 
 
                 raise RuntimeError('Does not match reference')
                 raise RuntimeError('Does not match reference')
@@ -433,14 +434,14 @@ def regression_check_reflect(shader, json_file, update, keep, opt):
         make_reference_dir(reference)
         make_reference_dir(reference)
         shutil.move(json_file, reference)
         shutil.move(json_file, reference)
     
     
-def regression_check(shader, glsl, update, keep, opt):
-    reference = reference_path(shader[0], shader[1], opt)
+def regression_check(shader, glsl, args):
+    reference = reference_path(shader[0], shader[1], args.opt)
     joined_path = os.path.join(shader[0], shader[1])
     joined_path = os.path.join(shader[0], shader[1])
     print('Reference shader path:', reference)
     print('Reference shader path:', reference)
 
 
     if os.path.exists(reference):
     if os.path.exists(reference):
         if md5_for_file(glsl) != md5_for_file(reference):
         if md5_for_file(glsl) != md5_for_file(reference):
-            if update:
+            if args.update:
                 print('Generated source code has changed for {}!'.format(reference))
                 print('Generated source code has changed for {}!'.format(reference))
                 # If we expect changes, update the reference file.
                 # If we expect changes, update the reference file.
                 if os.path.exists(reference):
                 if os.path.exists(reference):
@@ -458,7 +459,7 @@ def regression_check(shader, glsl, update, keep, opt):
                     print('')
                     print('')
 
 
                 # Otherwise, fail the test. Keep the shader file around so we can inspect.
                 # Otherwise, fail the test. Keep the shader file around so we can inspect.
-                if not keep:
+                if not args.keep:
                     remove_file(glsl)
                     remove_file(glsl)
                 raise RuntimeError('Does not match reference')
                 raise RuntimeError('Does not match reference')
         else:
         else:
@@ -501,7 +502,7 @@ def shader_is_noopt(shader):
 def shader_is_push_ubo(shader):
 def shader_is_push_ubo(shader):
     return '.push-ubo.' in shader
     return '.push-ubo.' in shader
 
 
-def test_shader(stats, shader, update, keep, opt, paths):
+def test_shader(stats, shader, args, paths):
     joined_path = os.path.join(shader[0], shader[1])
     joined_path = os.path.join(shader[0], shader[1])
     vulkan = shader_is_vulkan(shader[1])
     vulkan = shader_is_vulkan(shader[1])
     desktop = shader_is_desktop(shader[1])
     desktop = shader_is_desktop(shader[1])
@@ -516,16 +517,16 @@ def test_shader(stats, shader, update, keep, opt, paths):
     push_ubo = shader_is_push_ubo(shader[1])
     push_ubo = shader_is_push_ubo(shader[1])
 
 
     print('Testing shader:', joined_path)
     print('Testing shader:', joined_path)
-    spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, opt and (not noopt), push_ubo, paths)
+    spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim, args.opt and (not noopt), push_ubo, args.iterations, paths)
 
 
     # Only test GLSL stats if we have a shader following GL semantics.
     # Only test GLSL stats if we have a shader following GL semantics.
     if stats and (not vulkan) and (not is_spirv) and (not desktop):
     if stats and (not vulkan) and (not is_spirv) and (not desktop):
         cross_stats = get_shader_stats(glsl)
         cross_stats = get_shader_stats(glsl)
 
 
     if glsl:
     if glsl:
-        regression_check(shader, glsl, update, keep, opt)
+        regression_check(shader, glsl, args)
     if vulkan_glsl:
     if vulkan_glsl:
-        regression_check((shader[0], shader[1] + '.vk'), vulkan_glsl, update, keep, opt)
+        regression_check((shader[0], shader[1] + '.vk'), vulkan_glsl, args)
 
 
     remove_file(spirv)
     remove_file(spirv)
 
 
@@ -540,13 +541,13 @@ def test_shader(stats, shader, update, keep, opt, paths):
             a.append(str(i))
             a.append(str(i))
         print(','.join(a), file = stats)
         print(','.join(a), file = stats)
 
 
-def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validation, paths):
+def test_shader_msl(stats, shader, args, paths):
     joined_path = os.path.join(shader[0], shader[1])
     joined_path = os.path.join(shader[0], shader[1])
     print('\nTesting MSL shader:', joined_path)
     print('\nTesting MSL shader:', joined_path)
     is_spirv = shader_is_spirv(shader[1])
     is_spirv = shader_is_spirv(shader[1])
     noopt = shader_is_noopt(shader[1])
     noopt = shader_is_noopt(shader[1])
-    spirv, msl = cross_compile_msl(joined_path, is_spirv, opt and (not noopt), paths)
-    regression_check(shader, msl, update, keep, opt)
+    spirv, msl = cross_compile_msl(joined_path, is_spirv, args.opt and (not noopt), args.iterations, paths)
+    regression_check(shader, msl, args)
 
 
     # Uncomment the following line to print the temp SPIR-V file path.
     # Uncomment the following line to print the temp SPIR-V file path.
     # This temp SPIR-V file is not deleted until after the Metal validation step below.
     # This temp SPIR-V file is not deleted until after the Metal validation step below.
@@ -556,40 +557,40 @@ def test_shader_msl(stats, shader, update, keep, opt, force_no_external_validati
     # executable from Xcode using args: `--msl --entry main --output msl_path spirv_path`.
     # executable from Xcode using args: `--msl --entry main --output msl_path spirv_path`.
 #    print('SPRIV shader: ' + spirv)
 #    print('SPRIV shader: ' + spirv)
 
 
-    if not force_no_external_validation:
-        validate_shader_msl(shader, opt)
+    if not args.force_no_external_validation:
+        validate_shader_msl(shader, args.opt)
 
 
     remove_file(spirv)
     remove_file(spirv)
 
 
-def test_shader_hlsl(stats, shader, update, keep, opt, force_no_external_validation, paths):
+def test_shader_hlsl(stats, shader, args, paths):
     joined_path = os.path.join(shader[0], shader[1])
     joined_path = os.path.join(shader[0], shader[1])
     print('Testing HLSL shader:', joined_path)
     print('Testing HLSL shader:', joined_path)
     is_spirv = shader_is_spirv(shader[1])
     is_spirv = shader_is_spirv(shader[1])
     noopt = shader_is_noopt(shader[1])
     noopt = shader_is_noopt(shader[1])
-    spirv, hlsl = cross_compile_hlsl(joined_path, is_spirv, opt and (not noopt), force_no_external_validation, paths)
-    regression_check(shader, hlsl, update, keep, opt)
+    spirv, hlsl = cross_compile_hlsl(joined_path, is_spirv, args.opt and (not noopt), args.force_no_external_validation, args.iterations, paths)
+    regression_check(shader, hlsl, args)
     remove_file(spirv)
     remove_file(spirv)
 
 
-def test_shader_reflect(stats, shader, update, keep, opt, paths):
+def test_shader_reflect(stats, shader, args, paths):
     joined_path = os.path.join(shader[0], shader[1])
     joined_path = os.path.join(shader[0], shader[1])
     print('Testing shader reflection:', joined_path)
     print('Testing shader reflection:', joined_path)
     is_spirv = shader_is_spirv(shader[1])
     is_spirv = shader_is_spirv(shader[1])
     noopt = shader_is_noopt(shader[1])
     noopt = shader_is_noopt(shader[1])
-    spirv, reflect = cross_compile_reflect(joined_path, is_spirv, opt and (not noopt), paths)
-    regression_check_reflect(shader, reflect, update, keep, opt)
+    spirv, reflect = cross_compile_reflect(joined_path, is_spirv, args.opt and (not noopt), args.iterations, paths)
+    regression_check_reflect(shader, reflect, args)
     remove_file(spirv)
     remove_file(spirv)
 
 
 def test_shader_file(relpath, stats, args, backend):
 def test_shader_file(relpath, stats, args, backend):
-    paths = Paths(args.glslang, args.spirv_as, args.spirv_val, args.spirv_opt)
+    paths = Paths(args.spirv_cross, args.glslang, args.spirv_as, args.spirv_val, args.spirv_opt)
     try:
     try:
         if backend == 'msl':
         if backend == 'msl':
-            test_shader_msl(stats, (args.folder, relpath), args.update, args.keep, args.opt, args.force_no_external_validation, paths)
+            test_shader_msl(stats, (args.folder, relpath), args, paths)
         elif backend == 'hlsl':
         elif backend == 'hlsl':
-            test_shader_hlsl(stats, (args.folder, relpath), args.update, args.keep, args.opt, args.force_no_external_validation, paths)
+            test_shader_hlsl(stats, (args.folder, relpath), args, paths)
         elif backend == 'reflect':
         elif backend == 'reflect':
-            test_shader_reflect(stats, (args.folder, relpath), args.update, args.keep, args.opt, paths)
+            test_shader_reflect(stats, (args.folder, relpath), args, paths)
         else:
         else:
-            test_shader(stats, (args.folder, relpath), args.update, args.keep, args.opt, paths)
+            test_shader(stats, (args.folder, relpath), args, paths)
         return None
         return None
     except Exception as e:
     except Exception as e:
         return e
         return e
@@ -669,6 +670,9 @@ def main():
     parser.add_argument('--parallel',
     parser.add_argument('--parallel',
             action = 'store_true',
             action = 'store_true',
             help = 'Execute tests in parallel.  Useful for doing regression quickly, but bad for debugging and stat output.')
             help = 'Execute tests in parallel.  Useful for doing regression quickly, but bad for debugging and stat output.')
+    parser.add_argument('--spirv-cross',
+            default = './spirv-cross',
+            help = 'Explicit path to spirv-cross')
     parser.add_argument('--glslang',
     parser.add_argument('--glslang',
             default = 'glslangValidator',
             default = 'glslangValidator',
             help = 'Explicit path to glslangValidator')
             help = 'Explicit path to glslangValidator')
@@ -681,6 +685,10 @@ def main():
     parser.add_argument('--spirv-opt',
     parser.add_argument('--spirv-opt',
             default = 'spirv-opt',
             default = 'spirv-opt',
             help = 'Explicit path to spirv-opt')
             help = 'Explicit path to spirv-opt')
+    parser.add_argument('--iterations',
+            default = 1,
+            type = int,
+            help = 'Number of iterations to run SPIRV-Cross (benchmarking)')
     
     
     args = parser.parse_args()
     args = parser.parse_args()
     if not args.folder:
     if not args.folder:

+ 16 - 12
3rdparty/spirv-cross/test_shaders.sh

@@ -1,20 +1,24 @@
 #!/bin/bash
 #!/bin/bash
 
 
-echo "Building spirv-cross"
-make -j$(nproc)
+if [ -z "$SPIRV_CROSS_PATH" ]; then
+	echo "Building spirv-cross"
+	make -j$(nproc)
+	SPIRV_CROSS_PATH="./spirv-cross"
+fi
 
 
 export PATH="./external/glslang-build/output/bin:./external/spirv-tools-build/output/bin:.:$PATH"
 export PATH="./external/glslang-build/output/bin:./external/spirv-tools-build/output/bin:.:$PATH"
 echo "Using glslangValidation in: $(which glslangValidator)."
 echo "Using glslangValidation in: $(which glslangValidator)."
 echo "Using spirv-opt in: $(which spirv-opt)."
 echo "Using spirv-opt in: $(which spirv-opt)."
+echo "Using SPIRV-Cross in: \"$SPIRV_CROSS_PATH\"."
 
 
-./test_shaders.py shaders || exit 1
-./test_shaders.py shaders --opt || exit 1
-./test_shaders.py shaders-no-opt || exit 1
-./test_shaders.py shaders-msl --msl || exit 1
-./test_shaders.py shaders-msl --msl --opt || exit 1
-./test_shaders.py shaders-msl-no-opt --msl || exit 1
-./test_shaders.py shaders-hlsl --hlsl || exit 1
-./test_shaders.py shaders-hlsl --hlsl --opt || exit 1
-./test_shaders.py shaders-hlsl-no-opt --hlsl || exit 1
-./test_shaders.py shaders-reflection --reflect || exit 1
+./test_shaders.py shaders --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-no-opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-msl --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-msl --msl --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-msl-no-opt --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-hlsl --hlsl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-hlsl --hlsl --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-hlsl-no-opt --hlsl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-reflection --reflect --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
 
 

+ 226 - 0
3rdparty/spirv-cross/tests-other/small_vector.cpp

@@ -0,0 +1,226 @@
+/*
+ * Copyright 2019 Hans-Kristian Arntzen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "spirv_cross.hpp"
+#include <memory>
+
+using namespace spirv_cross;
+
+// Test the tricky bits of the implementation.
+// Running the entire test suite on this implementation should find all other potential issues.
+
+static int allocations = 0;
+static int deallocations = 0;
+
+#define SPVC_ASSERT(x) do { \
+	if (!(x)) SPIRV_CROSS_THROW("Assert: " #x " failed!"); \
+} while(0)
+
+struct RAIIInt
+{
+	RAIIInt(int v_) : v(v_) { allocations++; }
+	~RAIIInt() { deallocations++; }
+	RAIIInt() { allocations++; }
+	RAIIInt(const RAIIInt &other) { v = other.v; allocations++; }
+	RAIIInt(RAIIInt &&other) SPIRV_CROSS_NOEXCEPT { v = other.v; allocations++; }
+	RAIIInt &operator=(RAIIInt &&) = default;
+	RAIIInt &operator=(const RAIIInt &) = default;
+
+	int v = 0;
+};
+
+static void propagate_stack_to_heap()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+	auto *old_data = ints.data();
+	SPVC_ASSERT(ints[0].v == 1);
+	SPVC_ASSERT(ints[1].v == 2);
+	ints.emplace_back(3);
+	SPVC_ASSERT(old_data != ints.data());
+	SPVC_ASSERT(ints[0].v == 1);
+	SPVC_ASSERT(ints[1].v == 2);
+	SPVC_ASSERT(ints[2].v == 3);
+	SPVC_ASSERT(ints.size() == 3);
+}
+
+static void insert_end()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+
+	const RAIIInt new_ints[3] = { 10, 20, 30 };
+	ints.insert(ints.end(), new_ints, new_ints + 3);
+	SPVC_ASSERT(ints.size() == 5);
+
+	SPVC_ASSERT(ints[0].v == 1);
+	SPVC_ASSERT(ints[1].v == 2);
+	SPVC_ASSERT(ints[2].v == 10);
+	SPVC_ASSERT(ints[3].v == 20);
+	SPVC_ASSERT(ints[4].v == 30);
+}
+
+static void insert_begin_realloc()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+
+	const RAIIInt new_ints[3] = { 10, 20, 30 };
+	ints.insert(ints.begin(), new_ints, new_ints + 3);
+	SPVC_ASSERT(ints.size() == 5);
+
+	SPVC_ASSERT(ints[0].v == 10);
+	SPVC_ASSERT(ints[1].v == 20);
+	SPVC_ASSERT(ints[2].v == 30);
+	SPVC_ASSERT(ints[3].v == 1);
+	SPVC_ASSERT(ints[4].v == 2);
+}
+
+static void insert_middle_realloc()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+
+	const RAIIInt new_ints[3] = { 10, 20, 30 };
+	ints.insert(ints.begin() + 1, new_ints, new_ints + 3);
+	SPVC_ASSERT(ints.size() == 5);
+
+	SPVC_ASSERT(ints[0].v == 1);
+	SPVC_ASSERT(ints[1].v == 10);
+	SPVC_ASSERT(ints[2].v == 20);
+	SPVC_ASSERT(ints[3].v == 30);
+	SPVC_ASSERT(ints[4].v == 2);
+}
+
+static void insert_begin_no_realloc()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.reserve(10);
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+
+	const RAIIInt new_ints[3] = { 10, 20, 30 };
+	ints.insert(ints.begin(), new_ints, new_ints + 3);
+	SPVC_ASSERT(ints.size() == 5);
+
+	SPVC_ASSERT(ints[0].v == 10);
+	SPVC_ASSERT(ints[1].v == 20);
+	SPVC_ASSERT(ints[2].v == 30);
+	SPVC_ASSERT(ints[3].v == 1);
+	SPVC_ASSERT(ints[4].v == 2);
+}
+
+static void insert_middle_no_realloc()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.reserve(10);
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+
+	const RAIIInt new_ints[3] = { 10, 20, 30 };
+	ints.insert(ints.begin() + 1, new_ints, new_ints + 3);
+	SPVC_ASSERT(ints.size() == 5);
+
+	SPVC_ASSERT(ints[0].v == 1);
+	SPVC_ASSERT(ints[1].v == 10);
+	SPVC_ASSERT(ints[2].v == 20);
+	SPVC_ASSERT(ints[3].v == 30);
+	SPVC_ASSERT(ints[4].v == 2);
+}
+
+static void erase_end()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+	ints.emplace_back(3);
+	ints.emplace_back(4);
+	ints.erase(ints.begin() + 1, ints.end());
+
+	SPVC_ASSERT(ints.size() == 1);
+	SPVC_ASSERT(ints[0].v == 1);
+}
+
+static void erase_middle()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+	ints.emplace_back(3);
+	ints.emplace_back(4);
+	ints.erase(ints.begin() + 1, ints.end() - 1);
+
+	SPVC_ASSERT(ints.size() == 2);
+	SPVC_ASSERT(ints[0].v == 1);
+	SPVC_ASSERT(ints[1].v == 4);
+}
+
+static void erase_start()
+{
+	SmallVector<RAIIInt, 2> ints;
+	ints.emplace_back(1);
+	ints.emplace_back(2);
+	ints.emplace_back(3);
+	ints.emplace_back(4);
+	ints.erase(ints.begin(), ints.end() - 2);
+
+	SPVC_ASSERT(ints.size() == 2);
+	SPVC_ASSERT(ints[0].v == 3);
+	SPVC_ASSERT(ints[1].v == 4);
+}
+
+static void convert_to_std_vector()
+{
+	SmallVector<RAIIInt, 4> foo;
+	foo.push_back(1);
+	foo.push_back(2);
+	std::vector<RAIIInt> ints(foo);
+	SPVC_ASSERT(ints.size() == 2);
+	SPVC_ASSERT(foo.size() == 2);
+	SPVC_ASSERT(ints[0].v == 1);
+	SPVC_ASSERT(ints[1].v == 2);
+
+	// This doesn't work on MSVC 2013. Ignore it.
+#if !(defined(_MSC_VER) && _MSC_VER < 1900)
+	SmallVector<std::unique_ptr<RAIIInt>> move_only_buffer;
+	move_only_buffer.emplace_back(new RAIIInt(40));
+	std::vector<std::unique_ptr<RAIIInt>> move_only_vector(std::move(move_only_buffer));
+	SPVC_ASSERT(move_only_vector.size() == 1);
+	SPVC_ASSERT(move_only_vector[0]->v == 40);
+#endif
+}
+
+int main()
+{
+	propagate_stack_to_heap();
+	insert_end();
+	insert_begin_realloc();
+	insert_begin_no_realloc();
+	insert_middle_realloc();
+	insert_middle_no_realloc();
+	erase_end();
+	erase_middle();
+	erase_start();
+
+	convert_to_std_vector();
+
+	SPVC_ASSERT(allocations > 0 && deallocations > 0 && deallocations == allocations);
+}
+

+ 16 - 12
3rdparty/spirv-cross/update_test_shaders.sh

@@ -1,21 +1,25 @@
 #!/bin/bash
 #!/bin/bash
 
 
-echo "Building spirv-cross"
-make -j$(nproc)
+if [ -z "$SPIRV_CROSS_PATH" ]; then
+	echo "Building spirv-cross"
+	make -j$(nproc)
+	SPIRV_CROSS_PATH="./spirv-cross"
+fi
 
 
 export PATH="./external/glslang-build/output/bin:./external/spirv-tools-build/output/bin:.:$PATH"
 export PATH="./external/glslang-build/output/bin:./external/spirv-tools-build/output/bin:.:$PATH"
 echo "Using glslangValidation in: $(which glslangValidator)."
 echo "Using glslangValidation in: $(which glslangValidator)."
 echo "Using spirv-opt in: $(which spirv-opt)."
 echo "Using spirv-opt in: $(which spirv-opt)."
+echo "Using SPIRV-Cross in: \"$SPIRV_CROSS_PATH\"."
 
 
-./test_shaders.py shaders --update || exit 1
-./test_shaders.py shaders --update --opt || exit 1
-./test_shaders.py shaders-no-opt --update || exit 1
-./test_shaders.py shaders-msl --update --msl || exit 1
-./test_shaders.py shaders-msl --update --msl --opt || exit 1
-./test_shaders.py shaders-msl-no-opt --update --msl || exit 1
-./test_shaders.py shaders-hlsl --update --hlsl || exit 1
-./test_shaders.py shaders-hlsl --update --hlsl --opt || exit 1
-./test_shaders.py shaders-hlsl-no-opt --update --hlsl || exit 1
-./test_shaders.py shaders-reflection --reflect --update || exit 1
+./test_shaders.py shaders --update --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders --update --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-no-opt --update --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-msl --update --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-msl --update --msl --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-msl-no-opt --update --msl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-hlsl --update --hlsl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-hlsl --update --hlsl --opt --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-hlsl-no-opt --update --hlsl --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
+./test_shaders.py shaders-reflection --reflect --update --spirv-cross "$SPIRV_CROSS_PATH" || exit 1
 
 
 
 

+ 1 - 1
tools/shaderc/shaderc_spirv.cpp

@@ -874,7 +874,7 @@ namespace bgfx { namespace spirv
 
 
 						spirv_cross::ShaderResources resources = msl.get_shader_resources();
 						spirv_cross::ShaderResources resources = msl.get_shader_resources();
 
 
-						std::vector<spirv_cross::EntryPoint> entryPoints = msl.get_entry_points_and_stages();
+						spirv_cross::SmallVector<spirv_cross::EntryPoint> entryPoints = msl.get_entry_points_and_stages();
 						if (!entryPoints.empty())
 						if (!entryPoints.empty())
 							msl.rename_entry_point(entryPoints[0].name, "xlatMtlMain", entryPoints[0].execution_model);
 							msl.rename_entry_point(entryPoints[0].name, "xlatMtlMain", entryPoints[0].execution_model);