Browse Source

Update spirv-cross

Alex Szpakowski 3 years ago
parent
commit
ca9b9ba91f
32 changed files with 2005 additions and 773 deletions
  1. 6 23
      src/libraries/spirv_cross/GLSL.std.450.h
  2. 1 0
      src/libraries/spirv_cross/include/spirv_cross/barrier.hpp
  3. 1 0
      src/libraries/spirv_cross/include/spirv_cross/external_interface.h
  4. 1 0
      src/libraries/spirv_cross/include/spirv_cross/image.hpp
  5. 1 0
      src/libraries/spirv_cross/include/spirv_cross/internal_interface.hpp
  6. 1 0
      src/libraries/spirv_cross/include/spirv_cross/sampler.hpp
  7. 1 0
      src/libraries/spirv_cross/include/spirv_cross/thread_group.hpp
  8. 9 4
      src/libraries/spirv_cross/spirv_cfg.cpp
  9. 1 1
      src/libraries/spirv_cross/spirv_cfg.hpp
  10. 61 9
      src/libraries/spirv_cross/spirv_common.hpp
  11. 1 1
      src/libraries/spirv_cross/spirv_cpp.cpp
  12. 1 1
      src/libraries/spirv_cross/spirv_cpp.hpp
  13. 403 54
      src/libraries/spirv_cross/spirv_cross.cpp
  14. 55 5
      src/libraries/spirv_cross/spirv_cross.hpp
  15. 116 1
      src/libraries/spirv_cross/spirv_cross_c.cpp
  16. 30 2
      src/libraries/spirv_cross/spirv_cross_c.h
  17. 7 7
      src/libraries/spirv_cross/spirv_cross_containers.hpp
  18. 1 1
      src/libraries/spirv_cross/spirv_cross_error_handling.hpp
  19. 4 1
      src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp
  20. 8 1
      src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp
  21. 1 1
      src/libraries/spirv_cross/spirv_cross_util.cpp
  22. 1 1
      src/libraries/spirv_cross/spirv_cross_util.hpp
  23. 377 167
      src/libraries/spirv_cross/spirv_glsl.cpp
  24. 49 15
      src/libraries/spirv_cross/spirv_glsl.hpp
  25. 279 227
      src/libraries/spirv_cross/spirv_hlsl.cpp
  26. 7 3
      src/libraries/spirv_cross/spirv_hlsl.hpp
  27. 437 223
      src/libraries/spirv_cross/spirv_msl.cpp
  28. 75 19
      src/libraries/spirv_cross/spirv_msl.hpp
  29. 67 3
      src/libraries/spirv_cross/spirv_parser.cpp
  30. 1 1
      src/libraries/spirv_cross/spirv_parser.hpp
  31. 1 1
      src/libraries/spirv_cross/spirv_reflect.cpp
  32. 1 1
      src/libraries/spirv_cross/spirv_reflect.hpp

+ 6 - 23
src/libraries/spirv_cross/GLSL.std.450.h

@@ -1,27 +1,10 @@
 /*
 /*
-** Copyright (c) 2014-2016 The Khronos Group Inc.
-**
-** Permission is hereby granted, free of charge, to any person obtaining a copy
-** of this software and/or associated documentation files (the "Materials"),
-** to deal in the Materials without restriction, including without limitation
-** the rights to use, copy, modify, merge, publish, distribute, sublicense,
-** and/or sell copies of the Materials, and to permit persons to whom the
-** Materials are furnished to do so, subject to the following conditions:
-**
-** The above copyright notice and this permission notice shall be included in
-** all copies or substantial portions of the Materials.
-**
-** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
-** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
-** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/ 
-**
-** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
-** IN THE MATERIALS.
+ * Copyright 2014-2016,2021 The Khronos Group, Inc.
+ * SPDX-License-Identifier: MIT
+ *
+ * MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
+ * STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
+ * HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
 */
 */
 
 
 #ifndef GLSLstd450_H
 #ifndef GLSLstd450_H

+ 1 - 0
src/libraries/spirv_cross/include/spirv_cross/barrier.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2017 ARM Limited
  * Copyright 2015-2017 ARM Limited
+ * SPDX-License-Identifier: Apache-2.0
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.

+ 1 - 0
src/libraries/spirv_cross/include/spirv_cross/external_interface.h

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2017 ARM Limited
  * Copyright 2015-2017 ARM Limited
+ * SPDX-License-Identifier: Apache-2.0
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.

+ 1 - 0
src/libraries/spirv_cross/include/spirv_cross/image.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2017 ARM Limited
  * Copyright 2015-2017 ARM Limited
+ * SPDX-License-Identifier: Apache-2.0
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.

+ 1 - 0
src/libraries/spirv_cross/include/spirv_cross/internal_interface.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2017 ARM Limited
  * Copyright 2015-2017 ARM Limited
+ * SPDX-License-Identifier: Apache-2.0
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.

+ 1 - 0
src/libraries/spirv_cross/include/spirv_cross/sampler.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2017 ARM Limited
  * Copyright 2015-2017 ARM Limited
+ * SPDX-License-Identifier: Apache-2.0
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.

+ 1 - 0
src/libraries/spirv_cross/include/spirv_cross/thread_group.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2017 ARM Limited
  * Copyright 2015-2017 ARM Limited
+ * SPDX-License-Identifier: Apache-2.0
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.

+ 9 - 4
src/libraries/spirv_cross/spirv_cfg.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2016-2021 Arm Limited
  * Copyright 2016-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_cfg.hpp"
 #include "spirv_cfg.hpp"
@@ -135,7 +135,9 @@ bool CFG::post_order_visit(uint32_t block_id)
 		break;
 		break;
 
 
 	case SPIRBlock::MultiSelect:
 	case SPIRBlock::MultiSelect:
-		for (auto &target : block.cases)
+	{
+		const auto &cases = compiler.get_case_list(block);
+		for (const auto &target : cases)
 		{
 		{
 			if (post_order_visit(target.block))
 			if (post_order_visit(target.block))
 				add_branch(block_id, target.block);
 				add_branch(block_id, target.block);
@@ -143,7 +145,7 @@ bool CFG::post_order_visit(uint32_t block_id)
 		if (block.default_block && post_order_visit(block.default_block))
 		if (block.default_block && post_order_visit(block.default_block))
 			add_branch(block_id, block.default_block);
 			add_branch(block_id, block.default_block);
 		break;
 		break;
-
+	}
 	default:
 	default:
 		break;
 		break;
 	}
 	}
@@ -385,7 +387,9 @@ void DominatorBuilder::lift_continue_block_dominator()
 		break;
 		break;
 
 
 	case SPIRBlock::MultiSelect:
 	case SPIRBlock::MultiSelect:
-		for (auto &target : block.cases)
+	{
+		auto &cases = cfg.get_compiler().get_case_list(block);
+		for (auto &target : cases)
 		{
 		{
 			if (cfg.get_visit_order(target.block) > post_order)
 			if (cfg.get_visit_order(target.block) > post_order)
 				back_edge_dominator = true;
 				back_edge_dominator = true;
@@ -393,6 +397,7 @@ void DominatorBuilder::lift_continue_block_dominator()
 		if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
 		if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
 			back_edge_dominator = true;
 			back_edge_dominator = true;
 		break;
 		break;
+	}
 
 
 	default:
 	default:
 		break;
 		break;

+ 1 - 1
src/libraries/spirv_cross/spirv_cfg.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2016-2021 Arm Limited
  * Copyright 2016-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_CFG_HPP
 #ifndef SPIRV_CROSS_CFG_HPP

+ 61 - 9
src/libraries/spirv_cross/spirv_common.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_COMMON_HPP
 #ifndef SPIRV_CROSS_COMMON_HPP
@@ -211,6 +211,28 @@ inline std::string convert_to_string(const T &t)
 	return std::to_string(t);
 	return std::to_string(t);
 }
 }
 
 
+static inline std::string convert_to_string(int32_t value)
+{
+	// INT_MIN is ... special on some backends. If we use a decimal literal, and negate it, we
+	// could accidentally promote the literal to long first, then negate.
+	// To workaround it, emit int(0x80000000) instead.
+	if (value == std::numeric_limits<int32_t>::min())
+		return "int(0x80000000)";
+	else
+		return std::to_string(value);
+}
+
+static inline std::string convert_to_string(int64_t value, const std::string &int64_type, bool long_long_literal_suffix)
+{
+	// INT64_MIN is ... special on some backends.
+	// If we use a decimal literal, and negate it, we might overflow the representable numbers.
+	// To workaround it, emit int(0x80000000) instead.
+	if (value == std::numeric_limits<int64_t>::min())
+		return join(int64_type, "(0x8000000000000000u", (long_long_literal_suffix ? "ll" : "l"), ")");
+	else
+		return std::to_string(value) + (long_long_literal_suffix ? "ll" : "l");
+}
+
 // Allow implementations to set a convenient standard precision
 // Allow implementations to set a convenient standard precision
 #ifndef SPIRV_CROSS_FLT_FMT
 #ifndef SPIRV_CROSS_FLT_FMT
 #define SPIRV_CROSS_FLT_FMT "%.32g"
 #define SPIRV_CROSS_FLT_FMT "%.32g"
@@ -417,6 +439,11 @@ struct IVariant
 	virtual ~IVariant() = default;
 	virtual ~IVariant() = default;
 	virtual IVariant *clone(ObjectPoolBase *pool) = 0;
 	virtual IVariant *clone(ObjectPoolBase *pool) = 0;
 	ID self = 0;
 	ID self = 0;
+
+protected:
+	IVariant() = default;
+	IVariant(const IVariant&) = default;
+	IVariant &operator=(const IVariant&) = default;
 };
 };
 
 
 #define SPIRV_CROSS_DECLARE_CLONE(T)                                \
 #define SPIRV_CROSS_DECLARE_CLONE(T)                                \
@@ -827,10 +854,11 @@ struct SPIRBlock : IVariant
 
 
 	struct Case
 	struct Case
 	{
 	{
-		uint32_t value;
+		uint64_t value;
 		BlockID block;
 		BlockID block;
 	};
 	};
-	SmallVector<Case> cases;
+	SmallVector<Case> cases_32bit;
+	SmallVector<Case> cases_64bit;
 
 
 	// 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.
@@ -1372,7 +1400,7 @@ public:
 	~Variant()
 	~Variant()
 	{
 	{
 		if (holder)
 		if (holder)
-			group->pools[type]->free_opaque(holder);
+			group->pools[type]->deallocate_opaque(holder);
 	}
 	}
 
 
 	// Marking custom move constructor as noexcept is important.
 	// Marking custom move constructor as noexcept is important.
@@ -1391,7 +1419,7 @@ public:
 		if (this != &other)
 		if (this != &other)
 		{
 		{
 			if (holder)
 			if (holder)
-				group->pools[type]->free_opaque(holder);
+				group->pools[type]->deallocate_opaque(holder);
 			holder = other.holder;
 			holder = other.holder;
 			group = other.group;
 			group = other.group;
 			type = other.type;
 			type = other.type;
@@ -1415,7 +1443,7 @@ public:
 		if (this != &other)
 		if (this != &other)
 		{
 		{
 			if (holder)
 			if (holder)
-				group->pools[type]->free_opaque(holder);
+				group->pools[type]->deallocate_opaque(holder);
 
 
 			if (other.holder)
 			if (other.holder)
 				holder = other.holder->clone(group->pools[other.type].get());
 				holder = other.holder->clone(group->pools[other.type].get());
@@ -1431,13 +1459,13 @@ public:
 	void set(IVariant *val, Types new_type)
 	void set(IVariant *val, Types new_type)
 	{
 	{
 		if (holder)
 		if (holder)
-			group->pools[type]->free_opaque(holder);
+			group->pools[type]->deallocate_opaque(holder);
 		holder = nullptr;
 		holder = nullptr;
 
 
 		if (!allow_type_rewrite && type != TypeNone && type != new_type)
 		if (!allow_type_rewrite && type != TypeNone && type != new_type)
 		{
 		{
 			if (val)
 			if (val)
-				group->pools[new_type]->free_opaque(val);
+				group->pools[new_type]->deallocate_opaque(val);
 			SPIRV_CROSS_THROW("Overwriting a variant with new type.");
 			SPIRV_CROSS_THROW("Overwriting a variant with new type.");
 		}
 		}
 
 
@@ -1492,7 +1520,7 @@ public:
 	void reset()
 	void reset()
 	{
 	{
 		if (holder)
 		if (holder)
-			group->pools[type]->free_opaque(holder);
+			group->pools[type]->deallocate_opaque(holder);
 		holder = nullptr;
 		holder = nullptr;
 		type = TypeNone;
 		type = TypeNone;
 	}
 	}
@@ -1763,6 +1791,22 @@ struct SetBindingPair
 	}
 	}
 };
 };
 
 
+struct LocationComponentPair
+{
+	uint32_t location;
+	uint32_t component;
+
+	inline bool operator==(const LocationComponentPair &other) const
+	{
+		return location == other.location && component == other.component;
+	}
+
+	inline bool operator<(const LocationComponentPair &other) const
+	{
+		return location < other.location || (location == other.location && component < other.component);
+	}
+};
+
 struct StageSetBinding
 struct StageSetBinding
 {
 {
 	spv::ExecutionModel model;
 	spv::ExecutionModel model;
@@ -1785,6 +1829,14 @@ struct InternalHasher
 		return (hash_set * 0x10001b31) ^ hash_binding;
 		return (hash_set * 0x10001b31) ^ hash_binding;
 	}
 	}
 
 
+	inline size_t operator()(const LocationComponentPair &value) const
+	{
+		// Quality of hash doesn't really matter here.
+		auto hash_set = std::hash<uint32_t>()(value.location);
+		auto hash_binding = std::hash<uint32_t>()(value.component);
+		return (hash_set * 0x10001b31) ^ hash_binding;
+	}
+
 	inline size_t operator()(const StageSetBinding &value) const
 	inline size_t operator()(const StageSetBinding &value) const
 	{
 	{
 		// Quality of hash doesn't really matter here.
 		// Quality of hash doesn't really matter here.

+ 1 - 1
src/libraries/spirv_cross/spirv_cpp.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_cpp.hpp"
 #include "spirv_cpp.hpp"

+ 1 - 1
src/libraries/spirv_cross/spirv_cpp.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_CPP_HPP
 #ifndef SPIRV_CROSS_CPP_HPP

+ 403 - 54
src/libraries/spirv_cross/spirv_cross.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_cross.hpp"
 #include "spirv_cross.hpp"
@@ -167,6 +167,12 @@ bool Compiler::block_is_pure(const SPIRBlock &block)
 		case OpTraceRayKHR:
 		case OpTraceRayKHR:
 		case OpExecuteCallableNV:
 		case OpExecuteCallableNV:
 		case OpExecuteCallableKHR:
 		case OpExecuteCallableKHR:
+		case OpRayQueryInitializeKHR:
+		case OpRayQueryTerminateKHR:
+		case OpRayQueryGenerateIntersectionKHR:
+		case OpRayQueryConfirmIntersectionKHR:
+		case OpRayQueryProceedKHR:
+			// There are various getters in ray query, but they are considered pure.
 			return false;
 			return false;
 
 
 			// OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure.
 			// OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure.
@@ -175,6 +181,30 @@ bool Compiler::block_is_pure(const SPIRBlock &block)
 			// This is a global side effect of the function.
 			// This is a global side effect of the function.
 			return false;
 			return false;
 
 
+		case OpExtInst:
+		{
+			uint32_t extension_set = ops[2];
+			if (get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
+			{
+				auto op_450 = static_cast<GLSLstd450>(ops[3]);
+				switch (op_450)
+				{
+				case GLSLstd450Modf:
+				case GLSLstd450Frexp:
+				{
+					auto &type = expression_type(ops[5]);
+					if (type.storage != StorageClassFunction)
+						return false;
+					break;
+				}
+
+				default:
+					break;
+				}
+			}
+			break;
+		}
+
 		default:
 		default:
 			break;
 			break;
 		}
 		}
@@ -284,31 +314,6 @@ SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
 	return var;
 	return var;
 }
 }
 
 
-StorageClass Compiler::get_expression_effective_storage_class(uint32_t ptr)
-{
-	auto *var = maybe_get_backing_variable(ptr);
-
-	// If the expression has been lowered to a temporary, we need to use the Generic storage class.
-	// We're looking for the effective storage class of a given expression.
-	// An access chain or forwarded OpLoads from such access chains
-	// will generally have the storage class of the underlying variable, but if the load was not forwarded
-	// we have lost any address space qualifiers.
-	bool forced_temporary = ir.ids[ptr].get_type() == TypeExpression && !get<SPIRExpression>(ptr).access_chain &&
-	                        (forced_temporaries.count(ptr) != 0 || forwarded_temporaries.count(ptr) == 0);
-
-	if (var && !forced_temporary)
-	{
-		// Normalize SSBOs to StorageBuffer here.
-		if (var->storage == StorageClassUniform &&
-		    has_decoration(get<SPIRType>(var->basetype).self, DecorationBufferBlock))
-			return StorageClassStorageBuffer;
-		else
-			return var->storage;
-	}
-	else
-		return expression_type(ptr).storage;
-}
-
 void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
 void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
 {
 {
 	auto &e = get<SPIRExpression>(expr);
 	auto &e = get<SPIRExpression>(expr);
@@ -735,6 +740,15 @@ bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t
 				break;
 				break;
 			}
 			}
 
 
+			case GLSLstd450Modf:
+			case GLSLstd450Fract:
+			{
+				auto *var = compiler.maybe_get<SPIRVariable>(args[5]);
+				if (var && storage_class_is_interface(var->storage))
+					variables.insert(args[5]);
+				break;
+			}
+
 			default:
 			default:
 				break;
 				break;
 			}
 			}
@@ -853,19 +867,79 @@ ShaderResources Compiler::get_shader_resources(const unordered_set<VariableID> *
 
 
 		// It is possible for uniform storage classes to be passed as function parameters, so detect
 		// It is possible for uniform storage classes to be passed as function parameters, so detect
 		// that. To detect function parameters, check of StorageClass of variable is function scope.
 		// that. To detect function parameters, check of StorageClass of variable is function scope.
-		if (var.storage == StorageClassFunction || !type.pointer || is_builtin_variable(var))
+		if (var.storage == StorageClassFunction || !type.pointer)
 			return;
 			return;
 
 
 		if (active_variables && active_variables->find(var.self) == end(*active_variables))
 		if (active_variables && active_variables->find(var.self) == end(*active_variables))
 			return;
 			return;
 
 
+		// In SPIR-V 1.4 and up, every global must be present in the entry point interface list,
+		// not just IO variables.
+		bool active_in_entry_point = true;
+		if (ir.get_spirv_version() < 0x10400)
+		{
+			if (var.storage == StorageClassInput || var.storage == StorageClassOutput)
+				active_in_entry_point = interface_variable_exists_in_entry_point(var.self);
+		}
+		else
+			active_in_entry_point = interface_variable_exists_in_entry_point(var.self);
+
+		if (!active_in_entry_point)
+			return;
+
+		bool is_builtin = is_builtin_variable(var);
+
+		if (is_builtin)
+		{
+			if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
+				return;
+
+			auto &list = var.storage == StorageClassInput ? res.builtin_inputs : res.builtin_outputs;
+			BuiltInResource resource;
+
+			if (has_decoration(type.self, DecorationBlock))
+			{
+				resource.resource = { var.self, var.basetype, type.self,
+				                      get_remapped_declared_block_name(var.self, false) };
+
+				for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+				{
+					resource.value_type_id = type.member_types[i];
+					resource.builtin = BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn));
+					list.push_back(resource);
+				}
+			}
+			else
+			{
+				bool strip_array =
+						!has_decoration(var.self, DecorationPatch) && (
+								get_execution_model() == ExecutionModelTessellationControl ||
+								(get_execution_model() == ExecutionModelTessellationEvaluation &&
+								 var.storage == StorageClassInput));
+
+				resource.resource = { var.self, var.basetype, type.self, get_name(var.self) };
+
+				if (strip_array && !type.array.empty())
+					resource.value_type_id = get_variable_data_type(var).parent_type;
+				else
+					resource.value_type_id = get_variable_data_type_id(var);
+
+				assert(resource.value_type_id);
+
+				resource.builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
+				list.push_back(std::move(resource));
+			}
+			return;
+		}
+
 		// Input
 		// Input
-		if (var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self))
+		if (var.storage == StorageClassInput)
 		{
 		{
 			if (has_decoration(type.self, DecorationBlock))
 			if (has_decoration(type.self, DecorationBlock))
 			{
 			{
 				res.stage_inputs.push_back(
 				res.stage_inputs.push_back(
-				    { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
+						{ var.self, var.basetype, type.self,
+						  get_remapped_declared_block_name(var.self, false) });
 			}
 			}
 			else
 			else
 				res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
 				res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
@@ -876,12 +950,12 @@ ShaderResources Compiler::get_shader_resources(const unordered_set<VariableID> *
 			res.subpass_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
 			res.subpass_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
 		}
 		}
 		// Outputs
 		// Outputs
-		else if (var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
+		else if (var.storage == StorageClassOutput)
 		{
 		{
 			if (has_decoration(type.self, DecorationBlock))
 			if (has_decoration(type.self, DecorationBlock))
 			{
 			{
 				res.stage_outputs.push_back(
 				res.stage_outputs.push_back(
-				    { var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
+						{ var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
 			}
 			}
 			else
 			else
 				res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
 				res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
@@ -1585,6 +1659,39 @@ SPIRBlock::ContinueBlockType Compiler::continue_block_type(const SPIRBlock &bloc
 	}
 	}
 }
 }
 
 
+const SmallVector<SPIRBlock::Case> &Compiler::get_case_list(const SPIRBlock &block) const
+{
+	uint32_t width = 0;
+
+	// First we check if we can get the type directly from the block.condition
+	// since it can be a SPIRConstant or a SPIRVariable.
+	if (const auto *constant = maybe_get<SPIRConstant>(block.condition))
+	{
+		const auto &type = get<SPIRType>(constant->constant_type);
+		width = type.width;
+	}
+	else if (const auto *var = maybe_get<SPIRVariable>(block.condition))
+	{
+		const auto &type = get<SPIRType>(var->basetype);
+		width = type.width;
+	}
+	else
+	{
+		auto search = ir.load_type_width.find(block.condition);
+		if (search == ir.load_type_width.end())
+		{
+			SPIRV_CROSS_THROW("Use of undeclared variable on a switch statement.");
+		}
+
+		width = search->second;
+	}
+
+	if (width > 32)
+		return block.cases_64bit;
+
+	return block.cases_32bit;
+}
+
 bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const
 bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const
 {
 {
 	handler.set_current_block(block);
 	handler.set_current_block(block);
@@ -1618,6 +1725,9 @@ bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHand
 		}
 		}
 	}
 	}
 
 
+	if (!handler.handle_terminator(block))
+		return false;
+
 	return true;
 	return true;
 }
 }
 
 
@@ -1685,10 +1795,22 @@ size_t Compiler::get_declared_struct_size(const SPIRType &type) const
 	if (type.member_types.empty())
 	if (type.member_types.empty())
 		SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
 		SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
 
 
-	uint32_t last = uint32_t(type.member_types.size() - 1);
-	size_t offset = type_struct_member_offset(type, last);
-	size_t size = get_declared_struct_member_size(type, last);
-	return offset + size;
+	// Offsets can be declared out of order, so we need to deduce the actual size
+	// based on last member instead.
+	uint32_t member_index = 0;
+	size_t highest_offset = 0;
+	for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+	{
+		size_t offset = type_struct_member_offset(type, i);
+		if (offset > highest_offset)
+		{
+			highest_offset = offset;
+			member_index = i;
+		}
+	}
+
+	size_t size = get_declared_struct_member_size(type, member_index);
+	return highest_offset + size;
 }
 }
 
 
 size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const
 size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const
@@ -2968,12 +3090,15 @@ void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBl
 		break;
 		break;
 
 
 	case SPIRBlock::MultiSelect:
 	case SPIRBlock::MultiSelect:
+	{
 		notify_variable_access(block.condition, block.self);
 		notify_variable_access(block.condition, block.self);
-		for (auto &target : block.cases)
+		auto &cases = compiler.get_case_list(block);
+		for (auto &target : cases)
 			test_phi(target.block);
 			test_phi(target.block);
 		if (block.default_block)
 		if (block.default_block)
 			test_phi(block.default_block);
 			test_phi(block.default_block);
 		break;
 		break;
+	}
 
 
 	default:
 	default:
 		break;
 		break;
@@ -3014,6 +3139,27 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_potential_temporary(uint
 	return compiler.ir.ids[id].empty() || (compiler.ir.ids[id].get_type() == TypeExpression);
 	return compiler.ir.ids[id].empty() || (compiler.ir.ids[id].get_type() == TypeExpression);
 }
 }
 
 
+bool Compiler::AnalyzeVariableScopeAccessHandler::handle_terminator(const SPIRBlock &block)
+{
+	switch (block.terminator)
+	{
+	case SPIRBlock::Return:
+		if (block.return_value)
+			notify_variable_access(block.return_value, block.self);
+		break;
+
+	case SPIRBlock::Select:
+	case SPIRBlock::MultiSelect:
+		notify_variable_access(block.condition, block.self);
+		break;
+
+	default:
+		break;
+	}
+
+	return true;
+}
+
 bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length)
 bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length)
 {
 {
 	// Keep track of the types of temporaries, so we can hoist them out as necessary.
 	// Keep track of the types of temporaries, so we can hoist them out as necessary.
@@ -3185,15 +3331,69 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
 		break;
 		break;
 	}
 	}
 
 
+	case OpSelect:
+	{
+		// In case of variable pointers, we might access a variable here.
+		// We cannot prove anything about these accesses however.
+		for (uint32_t i = 1; i < length; i++)
+		{
+			if (i >= 3)
+			{
+				auto *var = compiler.maybe_get_backing_variable(args[i]);
+				if (var)
+				{
+					accessed_variables_to_block[var->self].insert(current_block->self);
+					// Assume we can get partial writes to this variable.
+					partial_write_variables_to_block[var->self].insert(current_block->self);
+				}
+			}
+
+			// Might try to copy a Phi variable here.
+			notify_variable_access(args[i], current_block->self);
+		}
+		break;
+	}
+
 	case OpExtInst:
 	case OpExtInst:
 	{
 	{
 		for (uint32_t i = 4; i < length; i++)
 		for (uint32_t i = 4; i < length; i++)
 			notify_variable_access(args[i], current_block->self);
 			notify_variable_access(args[i], current_block->self);
 		notify_variable_access(args[1], current_block->self);
 		notify_variable_access(args[1], current_block->self);
+
+		uint32_t extension_set = args[2];
+		if (compiler.get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
+		{
+			auto op_450 = static_cast<GLSLstd450>(args[3]);
+			switch (op_450)
+			{
+			case GLSLstd450Modf:
+			case GLSLstd450Frexp:
+			{
+				uint32_t ptr = args[5];
+				auto *var = compiler.maybe_get_backing_variable(ptr);
+				if (var)
+				{
+					accessed_variables_to_block[var->self].insert(current_block->self);
+					if (var->self == ptr)
+						complete_write_variables_to_block[var->self].insert(current_block->self);
+					else
+						partial_write_variables_to_block[var->self].insert(current_block->self);
+				}
+				break;
+			}
+
+			default:
+				break;
+			}
+		}
 		break;
 		break;
 	}
 	}
 
 
 	case OpArrayLength:
 	case OpArrayLength:
+		// Only result is a temporary.
+		notify_variable_access(args[1], current_block->self);
+		break;
+
 	case OpLine:
 	case OpLine:
 	case OpNoLine:
 	case OpNoLine:
 		// Uses literals, but cannot be a phi variable or temporary, so ignore.
 		// Uses literals, but cannot be a phi variable or temporary, so ignore.
@@ -4071,7 +4271,7 @@ void Compiler::update_active_builtins()
 }
 }
 
 
 // Returns whether this shader uses a builtin of the storage class
 // Returns whether this shader uses a builtin of the storage class
-bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage)
+bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage) const
 {
 {
 	const Bitset *flags;
 	const Bitset *flags;
 	switch (storage)
 	switch (storage)
@@ -4284,16 +4484,13 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_
 		if (length < 4)
 		if (length < 4)
 			return false;
 			return false;
 
 
-		uint32_t result_type = args[0];
-		uint32_t result_id = args[1];
-		auto &type = compiler.get<SPIRType>(result_type);
-
 		// If the underlying resource has been used for comparison then duplicate loads of that resource must be too.
 		// If the underlying resource has been used for comparison then duplicate loads of that resource must be too.
 		// This image must be a depth image.
 		// This image must be a depth image.
+		uint32_t result_id = args[1];
 		uint32_t image = args[2];
 		uint32_t image = args[2];
 		uint32_t sampler = args[3];
 		uint32_t sampler = args[3];
 
 
-		if (type.image.depth || dref_combined_samplers.count(result_id) != 0)
+		if (dref_combined_samplers.count(result_id) != 0)
 		{
 		{
 			add_hierarchy_to_comparison_ids(image);
 			add_hierarchy_to_comparison_ids(image);
 
 
@@ -4553,9 +4750,11 @@ bool Compiler::is_desktop_only_format(spv::ImageFormat format)
 	return false;
 	return false;
 }
 }
 
 
-bool Compiler::image_is_comparison(const SPIRType &type, uint32_t id) const
+// An image is determined to be a depth image if it is marked as a depth image and is not also
+// explicitly marked with a color format, or if there are any sample/gather compare operations on it.
+bool Compiler::is_depth_image(const SPIRType &type, uint32_t id) const
 {
 {
-	return type.image.depth || (comparison_ids.count(id) != 0);
+	return (type.image.depth && type.image.format == ImageFormatUnknown) || comparison_ids.count(id);
 }
 }
 
 
 bool Compiler::type_is_opaque_value(const SPIRType &type) const
 bool Compiler::type_is_opaque_value(const SPIRType &type) const
@@ -4585,31 +4784,181 @@ Compiler::PhysicalStorageBufferPointerHandler::PhysicalStorageBufferPointerHandl
 {
 {
 }
 }
 
 
-bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t)
+Compiler::PhysicalBlockMeta *Compiler::PhysicalStorageBufferPointerHandler::find_block_meta(uint32_t id) const
 {
 {
-	if (op == OpConvertUToPtr || op == OpBitcast)
+	auto chain_itr = access_chain_to_physical_block.find(id);
+	if (chain_itr != access_chain_to_physical_block.end())
+		return chain_itr->second;
+	else
+		return nullptr;
+}
+
+void Compiler::PhysicalStorageBufferPointerHandler::mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length)
+{
+	uint32_t mask = *args;
+	args++;
+	length--;
+	if (length && (mask & MemoryAccessVolatileMask) != 0)
 	{
 	{
-		auto &type = compiler.get<SPIRType>(args[0]);
-		if (type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer && type.pointer_depth == 1)
+		args++;
+		length--;
+	}
+
+	if (length && (mask & MemoryAccessAlignedMask) != 0)
+	{
+		uint32_t alignment = *args;
+		auto *meta = find_block_meta(id);
+
+		// This makes the assumption that the application does not rely on insane edge cases like:
+		// Bind buffer with ADDR = 8, use block offset of 8 bytes, load/store with 16 byte alignment.
+		// If we emit the buffer with alignment = 16 here, the first element at offset = 0 should
+		// actually have alignment of 8 bytes, but this is too theoretical and awkward to support.
+		// We could potentially keep track of any offset in the access chain, but it's
+		// practically impossible for high level compilers to emit code like that,
+		// so deducing overall alignment requirement based on maximum observed Alignment value is probably fine.
+		if (meta && alignment > meta->alignment)
+			meta->alignment = alignment;
+	}
+}
+
+bool Compiler::PhysicalStorageBufferPointerHandler::type_is_bda_block_entry(uint32_t type_id) const
+{
+	auto &type = compiler.get<SPIRType>(type_id);
+	return type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer &&
+	       type.pointer_depth == 1 && !compiler.type_is_array_of_pointers(type);
+}
+
+uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_minimum_scalar_alignment(const SPIRType &type) const
+{
+	if (type.storage == spv::StorageClassPhysicalStorageBufferEXT)
+		return 8;
+	else if (type.basetype == SPIRType::Struct)
+	{
+		uint32_t alignment = 0;
+		for (auto &member_type : type.member_types)
 		{
 		{
-			// If we need to cast to a pointer type which is not a block, we might need to synthesize ourselves
-			// a block type which wraps this POD type.
-			if (type.basetype != SPIRType::Struct)
-				types.insert(args[0]);
+			uint32_t member_align = get_minimum_scalar_alignment(compiler.get<SPIRType>(member_type));
+			if (member_align > alignment)
+				alignment = member_align;
 		}
 		}
+		return alignment;
+	}
+	else
+		return type.width / 8;
+}
+
+void Compiler::PhysicalStorageBufferPointerHandler::setup_meta_chain(uint32_t type_id, uint32_t var_id)
+{
+	if (type_is_bda_block_entry(type_id))
+	{
+		auto &meta = physical_block_type_meta[type_id];
+		access_chain_to_physical_block[var_id] = &meta;
+
+		auto &type = compiler.get<SPIRType>(type_id);
+		if (type.basetype != SPIRType::Struct)
+			non_block_types.insert(type_id);
+
+		if (meta.alignment == 0)
+			meta.alignment = get_minimum_scalar_alignment(compiler.get_pointee_type(type));
+	}
+}
+
+bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t length)
+{
+	// When a BDA pointer comes to life, we need to keep a mapping of SSA ID -> type ID for the pointer type.
+	// For every load and store, we'll need to be able to look up the type ID being accessed and mark any alignment
+	// requirements.
+	switch (op)
+	{
+	case OpConvertUToPtr:
+	case OpBitcast:
+	case OpCompositeExtract:
+		// Extract can begin a new chain if we had a struct or array of pointers as input.
+		// We don't begin chains before we have a pure scalar pointer.
+		setup_meta_chain(args[0], args[1]);
+		break;
+
+	case OpAccessChain:
+	case OpInBoundsAccessChain:
+	case OpPtrAccessChain:
+	case OpCopyObject:
+	{
+		auto itr = access_chain_to_physical_block.find(args[2]);
+		if (itr != access_chain_to_physical_block.end())
+			access_chain_to_physical_block[args[1]] = itr->second;
+		break;
+	}
+
+	case OpLoad:
+	{
+		setup_meta_chain(args[0], args[1]);
+		if (length >= 4)
+			mark_aligned_access(args[2], args + 3, length - 3);
+		break;
+	}
+
+	case OpStore:
+	{
+		if (length >= 3)
+			mark_aligned_access(args[0], args + 2, length - 2);
+		break;
+	}
+
+	default:
+		break;
 	}
 	}
 
 
 	return true;
 	return true;
 }
 }
 
 
+uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_base_non_block_type_id(uint32_t type_id) const
+{
+	auto *type = &compiler.get<SPIRType>(type_id);
+	while (type->pointer &&
+	       type->storage == StorageClassPhysicalStorageBufferEXT &&
+	       !type_is_bda_block_entry(type_id))
+	{
+		type_id = type->parent_type;
+		type = &compiler.get<SPIRType>(type_id);
+	}
+
+	assert(type_is_bda_block_entry(type_id));
+	return type_id;
+}
+
+void Compiler::PhysicalStorageBufferPointerHandler::analyze_non_block_types_from_block(const SPIRType &type)
+{
+	for (auto &member : type.member_types)
+	{
+		auto &subtype = compiler.get<SPIRType>(member);
+		if (subtype.basetype != SPIRType::Struct && subtype.pointer &&
+		    subtype.storage == spv::StorageClassPhysicalStorageBufferEXT)
+		{
+			non_block_types.insert(get_base_non_block_type_id(member));
+		}
+		else if (subtype.basetype == SPIRType::Struct && !subtype.pointer)
+			analyze_non_block_types_from_block(subtype);
+	}
+}
+
 void Compiler::analyze_non_block_pointer_types()
 void Compiler::analyze_non_block_pointer_types()
 {
 {
 	PhysicalStorageBufferPointerHandler handler(*this);
 	PhysicalStorageBufferPointerHandler handler(*this);
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
 	traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
-	physical_storage_non_block_pointer_types.reserve(handler.types.size());
-	for (auto type : handler.types)
+
+	// Analyze any block declaration we have to make. It might contain
+	// physical pointers to POD types which we never used, and thus never added to the list.
+	// We'll need to add those pointer types to the set of types we declare.
+	ir.for_each_typed_id<SPIRType>([&](uint32_t, SPIRType &type) {
+		if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))
+			handler.analyze_non_block_types_from_block(type);
+	});
+
+	physical_storage_non_block_pointer_types.reserve(handler.non_block_types.size());
+	for (auto type : handler.non_block_types)
 		physical_storage_non_block_pointer_types.push_back(type);
 		physical_storage_non_block_pointer_types.push_back(type);
 	sort(begin(physical_storage_non_block_pointer_types), end(physical_storage_non_block_pointer_types));
 	sort(begin(physical_storage_non_block_pointer_types), end(physical_storage_non_block_pointer_types));
+	physical_storage_type_to_alignment = move(handler.physical_block_type_meta);
 }
 }
 
 
 bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t)
 bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t)

+ 55 - 5
src/libraries/spirv_cross/spirv_cross.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_HPP
 #ifndef SPIRV_CROSS_HPP
@@ -59,6 +59,27 @@ struct Resource
 	std::string name;
 	std::string name;
 };
 };
 
 
+struct BuiltInResource
+{
+	// This is mostly here to support reflection of builtins such as Position/PointSize/CullDistance/ClipDistance.
+	// This needs to be different from Resource since we can collect builtins from blocks.
+	// A builtin present here does not necessarily mean it's considered an active builtin,
+	// since variable ID "activeness" is only tracked on OpVariable level, not Block members.
+	// For that, update_active_builtins() -> has_active_builtin() can be used to further refine the reflection.
+	spv::BuiltIn builtin;
+
+	// This is the actual value type of the builtin.
+	// Typically float4, float, array<float, N> for the gl_PerVertex builtins.
+	// If the builtin is a control point, the control point array type will be stripped away here as appropriate.
+	TypeID value_type_id;
+
+	// This refers to the base resource which contains the builtin.
+	// If resource is a Block, it can hold multiple builtins, or it might not be a block.
+	// For advanced reflection scenarios, all information in builtin/value_type_id can be deduced,
+	// it's just more convenient this way.
+	Resource resource;
+};
+
 struct ShaderResources
 struct ShaderResources
 {
 {
 	SmallVector<Resource> uniform_buffers;
 	SmallVector<Resource> uniform_buffers;
@@ -79,6 +100,9 @@ struct ShaderResources
 	// these correspond to separate texture2D and samplers respectively.
 	// these correspond to separate texture2D and samplers respectively.
 	SmallVector<Resource> separate_images;
 	SmallVector<Resource> separate_images;
 	SmallVector<Resource> separate_samplers;
 	SmallVector<Resource> separate_samplers;
+
+	SmallVector<BuiltInResource> builtin_inputs;
+	SmallVector<BuiltInResource> builtin_outputs;
 };
 };
 
 
 struct CombinedImageSampler
 struct CombinedImageSampler
@@ -324,7 +348,7 @@ public:
 
 
 	// Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader.
 	// Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader.
 	void update_active_builtins();
 	void update_active_builtins();
-	bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage);
+	bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage) const;
 
 
 	// Query and modify OpExecutionMode.
 	// Query and modify OpExecutionMode.
 	const Bitset &get_execution_mode_bitset() const;
 	const Bitset &get_execution_mode_bitset() const;
@@ -647,7 +671,6 @@ protected:
 	bool expression_is_lvalue(uint32_t id) const;
 	bool expression_is_lvalue(uint32_t id) const;
 	bool variable_storage_is_aliased(const SPIRVariable &var);
 	bool variable_storage_is_aliased(const SPIRVariable &var);
 	SPIRVariable *maybe_get_backing_variable(uint32_t chain);
 	SPIRVariable *maybe_get_backing_variable(uint32_t chain);
-	spv::StorageClass get_expression_effective_storage_class(uint32_t ptr);
 
 
 	void register_read(uint32_t expr, uint32_t chain, bool forwarded);
 	void register_read(uint32_t expr, uint32_t chain, bool forwarded);
 	void register_write(uint32_t chain);
 	void register_write(uint32_t chain);
@@ -742,6 +765,10 @@ protected:
 		// Return true if traversal should continue.
 		// Return true if traversal should continue.
 		// If false, traversal will end immediately.
 		// If false, traversal will end immediately.
 		virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0;
 		virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0;
+		virtual bool handle_terminator(const SPIRBlock &)
+		{
+			return true;
+		}
 
 
 		virtual bool follow_function_call(const SPIRFunction &)
 		virtual bool follow_function_call(const SPIRFunction &)
 		{
 		{
@@ -956,6 +983,7 @@ protected:
 		bool id_is_phi_variable(uint32_t id) const;
 		bool id_is_phi_variable(uint32_t id) const;
 		bool id_is_potential_temporary(uint32_t id) const;
 		bool id_is_potential_temporary(uint32_t id) const;
 		bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
 		bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
+		bool handle_terminator(const SPIRBlock &block) override;
 
 
 		Compiler &compiler;
 		Compiler &compiler;
 		SPIRFunction &entry;
 		SPIRFunction &entry;
@@ -982,15 +1010,32 @@ protected:
 		uint32_t write_count = 0;
 		uint32_t write_count = 0;
 	};
 	};
 
 
+	struct PhysicalBlockMeta
+	{
+		uint32_t alignment = 0;
+	};
+
 	struct PhysicalStorageBufferPointerHandler : OpcodeHandler
 	struct PhysicalStorageBufferPointerHandler : OpcodeHandler
 	{
 	{
 		explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_);
 		explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_);
 		bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
 		bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
 		Compiler &compiler;
 		Compiler &compiler;
-		std::unordered_set<uint32_t> types;
+
+		std::unordered_set<uint32_t> non_block_types;
+		std::unordered_map<uint32_t, PhysicalBlockMeta> physical_block_type_meta;
+		std::unordered_map<uint32_t, PhysicalBlockMeta *> access_chain_to_physical_block;
+
+		void mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length);
+		PhysicalBlockMeta *find_block_meta(uint32_t id) const;
+		bool type_is_bda_block_entry(uint32_t type_id) const;
+		void setup_meta_chain(uint32_t type_id, uint32_t var_id);
+		uint32_t get_minimum_scalar_alignment(const SPIRType &type) const;
+		void analyze_non_block_types_from_block(const SPIRType &type);
+		uint32_t get_base_non_block_type_id(uint32_t type_id) const;
 	};
 	};
 	void analyze_non_block_pointer_types();
 	void analyze_non_block_pointer_types();
 	SmallVector<uint32_t> physical_storage_non_block_pointer_types;
 	SmallVector<uint32_t> physical_storage_non_block_pointer_types;
+	std::unordered_map<uint32_t, PhysicalBlockMeta> physical_storage_type_to_alignment;
 
 
 	void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler);
 	void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler);
 	void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler,
 	void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler,
@@ -1062,7 +1107,7 @@ protected:
 	Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const;
 	Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const;
 	static bool is_desktop_only_format(spv::ImageFormat format);
 	static bool is_desktop_only_format(spv::ImageFormat format);
 
 
-	bool image_is_comparison(const SPIRType &type, uint32_t id) const;
+	bool is_depth_image(const SPIRType &type, uint32_t id) const;
 
 
 	void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0);
 	void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0);
 	uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const;
 	uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const;
@@ -1090,6 +1135,11 @@ protected:
 
 
 	bool is_vertex_like_shader() const;
 	bool is_vertex_like_shader() const;
 
 
+	// Get the correct case list for the OpSwitch, since it can be either a
+	// 32 bit wide condition or a 64 bit, but the type is not embedded in the
+	// instruction itself.
+	const SmallVector<SPIRBlock::Case> &get_case_list(const SPIRBlock &block) const;
+
 private:
 private:
 	// Used only to implement the old deprecated get_entry_point() interface.
 	// Used only to implement the old deprecated get_entry_point() interface.
 	const SPIREntryPoint &get_first_entry_point(const std::string &name) const;
 	const SPIREntryPoint &get_first_entry_point(const std::string &name) const;

+ 116 - 1
src/libraries/spirv_cross/spirv_cross_c.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2019-2021 Hans-Kristian Arntzen
  * Copyright 2019-2021 Hans-Kristian Arntzen
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_cross_c.h"
 #include "spirv_cross_c.h"
@@ -197,8 +197,11 @@ struct spvc_resources_s : ScratchMemoryAllocation
 	SmallVector<spvc_reflected_resource> separate_images;
 	SmallVector<spvc_reflected_resource> separate_images;
 	SmallVector<spvc_reflected_resource> separate_samplers;
 	SmallVector<spvc_reflected_resource> separate_samplers;
 	SmallVector<spvc_reflected_resource> acceleration_structures;
 	SmallVector<spvc_reflected_resource> acceleration_structures;
+	SmallVector<spvc_reflected_builtin_resource> builtin_inputs;
+	SmallVector<spvc_reflected_builtin_resource> builtin_outputs;
 
 
 	bool copy_resources(SmallVector<spvc_reflected_resource> &outputs, const SmallVector<Resource> &inputs);
 	bool copy_resources(SmallVector<spvc_reflected_resource> &outputs, const SmallVector<Resource> &inputs);
+	bool copy_resources(SmallVector<spvc_reflected_builtin_resource> &outputs, const SmallVector<BuiltInResource> &inputs);
 	bool copy_resources(const ShaderResources &resources);
 	bool copy_resources(const ShaderResources &resources);
 };
 };
 
 
@@ -469,6 +472,9 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c
 	case SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS:
 	case SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS:
 		options->glsl.force_flattened_io_blocks = value != 0;
 		options->glsl.force_flattened_io_blocks = value != 0;
 		break;
 		break;
+	case SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT:
+		options->glsl.ovr_multiview_view_count = value;
+		break;
 #endif
 #endif
 
 
 #if SPIRV_CROSS_C_API_HLSL
 #if SPIRV_CROSS_C_API_HLSL
@@ -818,6 +824,44 @@ spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spv
 #endif
 #endif
 }
 }
 
 
+spvc_result spvc_compiler_mask_stage_output_by_location(spvc_compiler compiler,
+                                                        unsigned location, unsigned component)
+{
+#if SPIRV_CROSS_C_API_GLSL
+	if (compiler->backend == SPVC_BACKEND_NONE)
+	{
+		compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+		return SPVC_ERROR_INVALID_ARGUMENT;
+	}
+
+	static_cast<CompilerGLSL *>(compiler->compiler.get())->mask_stage_output_by_location(location, component);
+	return SPVC_SUCCESS;
+#else
+	(void)location;
+	(void)component;
+	compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+	return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
+spvc_result spvc_compiler_mask_stage_output_by_builtin(spvc_compiler compiler, SpvBuiltIn builtin)
+{
+#if SPIRV_CROSS_C_API_GLSL
+	if (compiler->backend == SPVC_BACKEND_NONE)
+	{
+		compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+		return SPVC_ERROR_INVALID_ARGUMENT;
+	}
+
+	static_cast<CompilerGLSL *>(compiler->compiler.get())->mask_stage_output_by_builtin(spv::BuiltIn(builtin));
+	return SPVC_SUCCESS;
+#else
+	(void)builtin;
+	compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
+	return SPVC_ERROR_INVALID_ARGUMENT;
+#endif
+}
+
 spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler,
 spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler,
                                                          const spvc_hlsl_root_constants *constant_info,
                                                          const spvc_hlsl_root_constants *constant_info,
                                                          size_t count)
                                                          size_t count)
@@ -1551,6 +1595,30 @@ bool spvc_resources_s::copy_resources(SmallVector<spvc_reflected_resource> &outp
 	return true;
 	return true;
 }
 }
 
 
+bool spvc_resources_s::copy_resources(SmallVector<spvc_reflected_builtin_resource> &outputs,
+                                      const SmallVector<BuiltInResource> &inputs)
+{
+	for (auto &i : inputs)
+	{
+		spvc_reflected_builtin_resource br;
+
+		br.value_type_id = i.value_type_id;
+		br.builtin = SpvBuiltIn(i.builtin);
+
+		auto &r = br.resource;
+		r.base_type_id = i.resource.base_type_id;
+		r.type_id = i.resource.type_id;
+		r.id = i.resource.id;
+		r.name = context->allocate_name(i.resource.name);
+		if (!r.name)
+			return false;
+
+		outputs.push_back(br);
+	}
+
+	return true;
+}
+
 bool spvc_resources_s::copy_resources(const ShaderResources &resources)
 bool spvc_resources_s::copy_resources(const ShaderResources &resources)
 {
 {
 	if (!copy_resources(uniform_buffers, resources.uniform_buffers))
 	if (!copy_resources(uniform_buffers, resources.uniform_buffers))
@@ -1577,6 +1645,10 @@ bool spvc_resources_s::copy_resources(const ShaderResources &resources)
 		return false;
 		return false;
 	if (!copy_resources(acceleration_structures, resources.acceleration_structures))
 	if (!copy_resources(acceleration_structures, resources.acceleration_structures))
 		return false;
 		return false;
+	if (!copy_resources(builtin_inputs, resources.builtin_inputs))
+		return false;
+	if (!copy_resources(builtin_outputs, resources.builtin_outputs))
+		return false;
 
 
 	return true;
 	return true;
 }
 }
@@ -1735,6 +1807,37 @@ spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources,
 	return SPVC_SUCCESS;
 	return SPVC_SUCCESS;
 }
 }
 
 
+spvc_result spvc_resources_get_builtin_resource_list_for_type(
+		spvc_resources resources, spvc_builtin_resource_type type,
+		const spvc_reflected_builtin_resource **resource_list,
+		size_t *resource_size)
+{
+	const SmallVector<spvc_reflected_builtin_resource> *list = nullptr;
+	switch (type)
+	{
+	case SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT:
+		list = &resources->builtin_inputs;
+		break;
+
+	case SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT:
+		list = &resources->builtin_outputs;
+		break;
+
+	default:
+		break;
+	}
+
+	if (!list)
+	{
+		resources->context->report_error("Invalid argument.");
+		return SPVC_ERROR_INVALID_ARGUMENT;
+	}
+
+	*resource_size = list->size();
+	*resource_list = list->data();
+	return SPVC_SUCCESS;
+}
+
 void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, unsigned argument)
 void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, unsigned argument)
 {
 {
 	compiler->compiler->set_decoration(id, static_cast<spv::Decoration>(decoration), argument);
 	compiler->compiler->set_decoration(id, static_cast<spv::Decoration>(decoration), argument);
@@ -1937,6 +2040,18 @@ SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler)
 	return static_cast<SpvExecutionModel>(compiler->compiler->get_execution_model());
 	return static_cast<SpvExecutionModel>(compiler->compiler->get_execution_model());
 }
 }
 
 
+void spvc_compiler_update_active_builtins(spvc_compiler compiler)
+{
+       compiler->compiler->update_active_builtins();
+}
+
+spvc_bool spvc_compiler_has_active_builtin(spvc_compiler compiler, SpvBuiltIn builtin, SpvStorageClass storage)
+{
+	return compiler->compiler->has_active_builtin(static_cast<spv::BuiltIn>(builtin), static_cast<spv::StorageClass>(storage)) ?
+		SPVC_TRUE :
+		SPVC_FALSE;
+}
+
 spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id)
 spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id)
 {
 {
 	// Should only throw if an intentionally garbage ID is passed, but the IDs are not type-safe.
 	// Should only throw if an intentionally garbage ID is passed, but the IDs are not type-safe.

+ 30 - 2
src/libraries/spirv_cross/spirv_cross_c.h

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2019-2021 Hans-Kristian Arntzen
  * Copyright 2019-2021 Hans-Kristian Arntzen
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_C_API_H
 #ifndef SPIRV_CROSS_C_API_H
@@ -40,7 +40,7 @@ extern "C" {
 /* Bumped if ABI or API breaks backwards compatibility. */
 /* Bumped if ABI or API breaks backwards compatibility. */
 #define SPVC_C_API_VERSION_MAJOR 0
 #define SPVC_C_API_VERSION_MAJOR 0
 /* Bumped if APIs or enumerations are added in a backwards compatible way. */
 /* Bumped if APIs or enumerations are added in a backwards compatible way. */
-#define SPVC_C_API_VERSION_MINOR 45
+#define SPVC_C_API_VERSION_MINOR 48
 /* Bumped if internal implementation details change. */
 /* Bumped if internal implementation details change. */
 #define SPVC_C_API_VERSION_PATCH 0
 #define SPVC_C_API_VERSION_PATCH 0
 
 
@@ -99,6 +99,13 @@ typedef struct spvc_reflected_resource
 	const char *name;
 	const char *name;
 } spvc_reflected_resource;
 } spvc_reflected_resource;
 
 
+typedef struct spvc_reflected_builtin_resource
+{
+	SpvBuiltIn builtin;
+	spvc_type_id value_type_id;
+	spvc_reflected_resource resource;
+} spvc_reflected_builtin_resource;
+
 /* See C++ API. */
 /* See C++ API. */
 typedef struct spvc_entry_point
 typedef struct spvc_entry_point
 {
 {
@@ -221,6 +228,14 @@ typedef enum spvc_resource_type
 	SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff
 	SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff
 } spvc_resource_type;
 } spvc_resource_type;
 
 
+typedef enum spvc_builtin_resource_type
+{
+	SPVC_BUILTIN_RESOURCE_TYPE_UNKNOWN = 0,
+	SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT = 1,
+	SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT = 2,
+	SPVC_BUILTIN_RESOURCE_TYPE_INT_MAX = 0x7fffffff
+} spvc_builtin_resource_type;
+
 /* Maps to spirv_cross::SPIRType::BaseType. */
 /* Maps to spirv_cross::SPIRType::BaseType. */
 typedef enum spvc_basetype
 typedef enum spvc_basetype
 {
 {
@@ -660,6 +675,8 @@ typedef enum spvc_compiler_option
 	SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING = 75 | SPVC_COMPILER_OPTION_MSL_BIT,
 	SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING = 75 | SPVC_COMPILER_OPTION_MSL_BIT,
 	SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE = 76 | SPVC_COMPILER_OPTION_MSL_BIT,
 	SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE = 76 | SPVC_COMPILER_OPTION_MSL_BIT,
 
 
+	SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT = 77 | SPVC_COMPILER_OPTION_GLSL_BIT,
+
 	SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
 	SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
 } spvc_compiler_option;
 } spvc_compiler_option;
 
 
@@ -722,6 +739,10 @@ SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler com
 
 
 SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id);
 SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id);
 
 
+SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_location(spvc_compiler compiler,
+                                                                        unsigned location, unsigned component);
+SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_builtin(spvc_compiler compiler, SpvBuiltIn builtin);
+
 /*
 /*
  * HLSL specifics.
  * HLSL specifics.
  * Maps to C++ API.
  * Maps to C++ API.
@@ -805,6 +826,11 @@ SPVC_PUBLIC_API spvc_result spvc_resources_get_resource_list_for_type(spvc_resou
                                                                       const spvc_reflected_resource **resource_list,
                                                                       const spvc_reflected_resource **resource_list,
                                                                       size_t *resource_size);
                                                                       size_t *resource_size);
 
 
+SPVC_PUBLIC_API spvc_result spvc_resources_get_builtin_resource_list_for_type(
+		spvc_resources resources, spvc_builtin_resource_type type,
+		const spvc_reflected_builtin_resource **resource_list,
+		size_t *resource_size);
+
 /*
 /*
  * Decorations.
  * Decorations.
  * Maps to C++ API.
  * Maps to C++ API.
@@ -861,6 +887,8 @@ SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler
 SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler,
 SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler,
                                                                             SpvExecutionMode mode, unsigned index);
                                                                             SpvExecutionMode mode, unsigned index);
 SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler);
 SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler);
+SPVC_PUBLIC_API void spvc_compiler_update_active_builtins(spvc_compiler compiler);
+SPVC_PUBLIC_API spvc_bool spvc_compiler_has_active_builtin(spvc_compiler compiler, SpvBuiltIn builtin, SpvStorageClass storage);
 
 
 /*
 /*
  * Type query interface.
  * Type query interface.

+ 7 - 7
src/libraries/spirv_cross/spirv_cross_containers.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2019-2021 Hans-Kristian Arntzen
  * Copyright 2019-2021 Hans-Kristian Arntzen
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_CONTAINERS_HPP
 #ifndef SPIRV_CROSS_CONTAINERS_HPP
@@ -327,8 +327,8 @@ public:
 
 
 	void reserve(size_t count) SPIRV_CROSS_NOEXCEPT
 	void reserve(size_t count) SPIRV_CROSS_NOEXCEPT
 	{
 	{
-		if ((count > std::numeric_limits<size_t>::max() / sizeof(T)) ||
-		    (count > std::numeric_limits<size_t>::max() / 2))
+		if ((count > (std::numeric_limits<size_t>::max)() / sizeof(T)) ||
+		    (count > (std::numeric_limits<size_t>::max)() / 2))
 		{
 		{
 			// Only way this should ever happen is with garbage input, terminate.
 			// Only way this should ever happen is with garbage input, terminate.
 			std::terminate();
 			std::terminate();
@@ -546,7 +546,7 @@ class ObjectPoolBase
 {
 {
 public:
 public:
 	virtual ~ObjectPoolBase() = default;
 	virtual ~ObjectPoolBase() = default;
-	virtual void free_opaque(void *ptr) = 0;
+	virtual void deallocate_opaque(void *ptr) = 0;
 };
 };
 
 
 template <typename T>
 template <typename T>
@@ -580,15 +580,15 @@ public:
 		return ptr;
 		return ptr;
 	}
 	}
 
 
-	void free(T *ptr)
+	void deallocate(T *ptr)
 	{
 	{
 		ptr->~T();
 		ptr->~T();
 		vacants.push_back(ptr);
 		vacants.push_back(ptr);
 	}
 	}
 
 
-	void free_opaque(void *ptr) override
+	void deallocate_opaque(void *ptr) override
 	{
 	{
-		free(static_cast<T *>(ptr));
+		deallocate(static_cast<T *>(ptr));
 	}
 	}
 
 
 	void clear()
 	void clear()

+ 1 - 1
src/libraries/spirv_cross/spirv_cross_error_handling.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_ERROR_HANDLING
 #ifndef SPIRV_CROSS_ERROR_HANDLING

+ 4 - 1
src/libraries/spirv_cross/spirv_cross_parsed_ir.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2018-2021 Arm Limited
  * Copyright 2018-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_cross_parsed_ir.hpp"
 #include "spirv_cross_parsed_ir.hpp"
@@ -83,6 +83,7 @@ ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
 		loop_iteration_depth_soft = other.loop_iteration_depth_soft;
 		loop_iteration_depth_soft = other.loop_iteration_depth_soft;
 
 
 		meta_needing_name_fixup = std::move(other.meta_needing_name_fixup);
 		meta_needing_name_fixup = std::move(other.meta_needing_name_fixup);
+		load_type_width = std::move(other.load_type_width);
 	}
 	}
 	return *this;
 	return *this;
 }
 }
@@ -115,7 +116,9 @@ ParsedIR &ParsedIR::operator=(const ParsedIR &other)
 		addressing_model = other.addressing_model;
 		addressing_model = other.addressing_model;
 		memory_model = other.memory_model;
 		memory_model = other.memory_model;
 
 
+
 		meta_needing_name_fixup = other.meta_needing_name_fixup;
 		meta_needing_name_fixup = other.meta_needing_name_fixup;
+		load_type_width = other.load_type_width;
 
 
 		// Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor.
 		// 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.
 		// Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group.

+ 8 - 1
src/libraries/spirv_cross/spirv_cross_parsed_ir.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2018-2021 Arm Limited
  * Copyright 2018-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_PARSED_IR_HPP
 #ifndef SPIRV_CROSS_PARSED_IR_HPP
@@ -78,6 +78,13 @@ public:
 	SmallVector<ID> ids_for_constant_or_type;
 	SmallVector<ID> ids_for_constant_or_type;
 	SmallVector<ID> ids_for_constant_or_variable;
 	SmallVector<ID> ids_for_constant_or_variable;
 
 
+	// We need to keep track of the width the Ops that contains a type for the
+	// OpSwitch instruction, since this one doesn't contains the type in the
+	// instruction itself. And in some case we need to cast the condition to
+	// wider types. We only need the width to do the branch fixup since the
+	// type check itself can be done at runtime
+	std::unordered_map<ID, uint32_t> load_type_width;
+
 	// 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.
 	SmallVector<spv::Capability> declared_capabilities;
 	SmallVector<spv::Capability> declared_capabilities;

+ 1 - 1
src/libraries/spirv_cross/spirv_cross_util.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_cross_util.hpp"
 #include "spirv_cross_util.hpp"

+ 1 - 1
src/libraries/spirv_cross/spirv_cross_util.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_UTIL_HPP
 #ifndef SPIRV_CROSS_UTIL_HPP

File diff suppressed because it is too large
+ 377 - 167
src/libraries/spirv_cross/spirv_glsl.cpp


+ 49 - 15
src/libraries/spirv_cross/spirv_glsl.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2015-2021 Arm Limited
  * Copyright 2015-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_GLSL_HPP
 #ifndef SPIRV_CROSS_GLSL_HPP
@@ -65,7 +65,8 @@ enum AccessChainFlagBits
 	ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2,
 	ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2,
 	ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3,
 	ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3,
 	ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4,
 	ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4,
-	ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5
+	ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5,
+	ACCESS_CHAIN_FORCE_COMPOSITE_BIT = 1 << 6
 };
 };
 typedef uint32_t AccessChainFlags;
 typedef uint32_t AccessChainFlags;
 
 
@@ -132,6 +133,9 @@ public:
 		// what happens on legacy GLSL targets for blocks and structs.
 		// what happens on legacy GLSL targets for blocks and structs.
 		bool force_flattened_io_blocks = false;
 		bool force_flattened_io_blocks = false;
 
 
+		// If non-zero, controls layout(num_views = N) in; in GL_OVR_multiview2.
+		uint32_t ovr_multiview_view_count = 0;
+
 		enum Precision
 		enum Precision
 		{
 		{
 			DontCare,
 			DontCare,
@@ -177,7 +181,8 @@ public:
 
 
 	// Redirect a subpassInput reading from input_attachment_index to instead load its value from
 	// Redirect a subpassInput reading from input_attachment_index to instead load its value from
 	// the color attachment at location = color_location. Requires ESSL.
 	// the color attachment at location = color_location. Requires ESSL.
-	void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location);
+	// If coherent, uses GL_EXT_shader_framebuffer_fetch, if not, uses noncoherent variant.
+	void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location, bool coherent);
 
 
 	explicit CompilerGLSL(std::vector<uint32_t> spirv_)
 	explicit CompilerGLSL(std::vector<uint32_t> spirv_)
 	    : Compiler(std::move(spirv_))
 	    : Compiler(std::move(spirv_))
@@ -250,6 +255,16 @@ public:
 	// - Images which are statically used at least once with Dref opcodes.
 	// - Images which are statically used at least once with Dref opcodes.
 	bool variable_is_depth_or_compare(VariableID id) const;
 	bool variable_is_depth_or_compare(VariableID id) const;
 
 
+	// If a shader output is active in this stage, but inactive in a subsequent stage,
+	// this can be signalled here. This can be used to work around certain cross-stage matching problems
+	// which plagues MSL and HLSL in certain scenarios.
+	// An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private
+	// variable.
+	// This option is only meaningful for MSL and HLSL, since GLSL matches by location directly.
+	// Masking builtins only takes effect if the builtin in question is part of the stage output interface.
+	void mask_stage_output_by_location(uint32_t location, uint32_t component);
+	void mask_stage_output_by_builtin(spv::BuiltIn builtin);
+
 protected:
 protected:
 	struct ShaderSubgroupSupportHelper
 	struct ShaderSubgroupSupportHelper
 	{
 	{
@@ -371,10 +386,11 @@ protected:
 	virtual void emit_struct_padding_target(const SPIRType &type);
 	virtual void emit_struct_padding_target(const SPIRType &type);
 	virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0);
 	virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0);
 	std::string constant_expression(const SPIRConstant &c);
 	std::string constant_expression(const SPIRConstant &c);
-	std::string constant_op_expression(const SPIRConstantOp &cop);
+	virtual std::string constant_op_expression(const SPIRConstantOp &cop);
 	virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
 	virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
 	virtual void emit_fixup();
 	virtual void emit_fixup();
 	virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0);
 	virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0);
+	virtual bool variable_decl_is_remapped_storage(const SPIRVariable &var, spv::StorageClass storage) const;
 	virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id);
 	virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id);
 
 
 	struct TextureFunctionBaseArguments
 	struct TextureFunctionBaseArguments
@@ -569,13 +585,15 @@ protected:
 		bool use_array_constructor = false;
 		bool use_array_constructor = false;
 		bool needs_row_major_load_workaround = false;
 		bool needs_row_major_load_workaround = false;
 		bool support_pointer_to_pointer = false;
 		bool support_pointer_to_pointer = false;
+		bool support_precise_qualifier = false;
+		bool support_64bit_switch = false;
 	} backend;
 	} backend;
 
 
 	void emit_struct(SPIRType &type);
 	void emit_struct(SPIRType &type);
 	void emit_resources();
 	void emit_resources();
 	void emit_extension_workarounds(spv::ExecutionModel model);
 	void emit_extension_workarounds(spv::ExecutionModel model);
 	void emit_buffer_block_native(const SPIRVariable &var);
 	void emit_buffer_block_native(const SPIRVariable &var);
-	void emit_buffer_reference_block(SPIRType &type, bool forward_declaration);
+	void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration);
 	void emit_buffer_block_legacy(const SPIRVariable &var);
 	void emit_buffer_block_legacy(const SPIRVariable &var);
 	void emit_buffer_block_flattened(const SPIRVariable &type);
 	void emit_buffer_block_flattened(const SPIRVariable &type);
 	void fixup_implicit_builtin_block_names();
 	void fixup_implicit_builtin_block_names();
@@ -616,6 +634,8 @@ protected:
 	void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
 	void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
 	                          const char *op);
 	                          const char *op);
 	void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
 	void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
+	void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
+	void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op);
 
 
 	void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op,
 	void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op,
 	                             SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type);
 	                             SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type);
@@ -661,6 +681,9 @@ protected:
 	std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags,
 	std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags,
 	                                  AccessChainMeta *meta);
 	                                  AccessChainMeta *meta);
 
 
+	spv::StorageClass get_expression_effective_storage_class(uint32_t ptr);
+	virtual bool access_chain_needs_stage_io_builtin_translation(uint32_t base);
+
 	virtual void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type,
 	virtual void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type,
 	                                                    spv::StorageClass storage, bool &is_packed);
 	                                                    spv::StorageClass storage, bool &is_packed);
 
 
@@ -691,6 +714,7 @@ protected:
 	void emit_uninitialized_temporary(uint32_t type, uint32_t id);
 	void emit_uninitialized_temporary(uint32_t type, uint32_t id);
 	SPIRExpression &emit_uninitialized_temporary_expression(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);
 	void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector<std::string> &arglist);
+	std::string to_non_uniform_aware_expression(uint32_t id);
 	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_composite_constructor_expression(uint32_t id, bool uses_buffer_offset);
 	std::string to_composite_constructor_expression(uint32_t id, bool uses_buffer_offset);
 	std::string to_rerolled_array_expression(const std::string &expr, const SPIRType &type);
 	std::string to_rerolled_array_expression(const std::string &expr, const SPIRType &type);
@@ -716,17 +740,17 @@ protected:
 	virtual std::string to_qualifiers_glsl(uint32_t id);
 	virtual std::string to_qualifiers_glsl(uint32_t id);
 	void fixup_io_block_patch_qualifiers(const SPIRVariable &var);
 	void fixup_io_block_patch_qualifiers(const SPIRVariable &var);
 	void emit_output_variable_initializer(const SPIRVariable &var);
 	void emit_output_variable_initializer(const SPIRVariable &var);
-	const char *to_precision_qualifiers_glsl(uint32_t id);
+	std::string to_precision_qualifiers_glsl(uint32_t id);
 	virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var);
 	virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var);
-	const char *flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags);
+	std::string flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags);
 	const char *format_to_glsl(spv::ImageFormat format);
 	const char *format_to_glsl(spv::ImageFormat format);
 	virtual std::string layout_for_member(const SPIRType &type, uint32_t index);
 	virtual std::string layout_for_member(const SPIRType &type, uint32_t index);
 	virtual std::string to_interpolation_qualifiers(const Bitset &flags);
 	virtual std::string to_interpolation_qualifiers(const Bitset &flags);
 	std::string layout_for_variable(const SPIRVariable &variable);
 	std::string layout_for_variable(const SPIRVariable &variable);
 	std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id);
 	std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id);
 	virtual bool skip_argument(uint32_t id) const;
 	virtual bool skip_argument(uint32_t id) const;
-	virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id, spv::StorageClass lhs_storage,
-	                             spv::StorageClass rhs_storage);
+	virtual void emit_array_copy(const std::string &lhs, uint32_t lhs_id, uint32_t rhs_id,
+	                             spv::StorageClass lhs_storage, spv::StorageClass rhs_storage);
 	virtual void emit_block_hints(const SPIRBlock &block);
 	virtual void emit_block_hints(const SPIRBlock &block);
 	virtual std::string to_initializer_expression(const SPIRVariable &var);
 	virtual std::string to_initializer_expression(const SPIRVariable &var);
 	virtual std::string to_zero_initialized_expression(uint32_t type_id);
 	virtual std::string to_zero_initialized_expression(uint32_t type_id);
@@ -741,6 +765,7 @@ protected:
 	uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
 	uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
 	uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
 	uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
 	uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
 	uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
+	uint32_t type_to_location_count(const SPIRType &type) const;
 
 
 	std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg);
 	std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg);
 	virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type);
 	virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type);
@@ -838,7 +863,9 @@ protected:
 
 
 	// GL_EXT_shader_framebuffer_fetch support.
 	// GL_EXT_shader_framebuffer_fetch support.
 	std::vector<std::pair<uint32_t, uint32_t>> subpass_to_framebuffer_fetch_attachment;
 	std::vector<std::pair<uint32_t, uint32_t>> subpass_to_framebuffer_fetch_attachment;
-	std::unordered_set<uint32_t> inout_color_attachments;
+	std::vector<std::pair<uint32_t, bool>> inout_color_attachments;
+	bool location_is_framebuffer_fetch(uint32_t location) const;
+	bool location_is_non_coherent_framebuffer_fetch(uint32_t location) const;
 	bool subpass_input_is_framebuffer_fetch(uint32_t id) const;
 	bool subpass_input_is_framebuffer_fetch(uint32_t id) const;
 	void emit_inout_fragment_outputs_copy_to_subpass_inputs();
 	void emit_inout_fragment_outputs_copy_to_subpass_inputs();
 	const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const;
 	const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const;
@@ -877,11 +904,11 @@ protected:
 	// Builtins in GLSL are always specific signedness, but the SPIR-V can declare them
 	// Builtins in GLSL are always specific signedness, but the SPIR-V can declare them
 	// as either unsigned or signed.
 	// as either unsigned or signed.
 	// Sometimes we will need to automatically perform casts on load and store to make this work.
 	// Sometimes we will need to automatically perform casts on load and store to make this work.
-	virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
-	virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
+	virtual void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
+	virtual void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
 	void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
 	void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
 	bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id);
 	bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id);
-	void convert_non_uniform_expression(const SPIRType &type, std::string &expr);
+	void convert_non_uniform_expression(std::string &expr, uint32_t ptr_id);
 
 
 	void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);
 	void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);
 	void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
 	void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
@@ -900,10 +927,17 @@ protected:
 	void fixup_type_alias();
 	void fixup_type_alias();
 	void reorder_type_alias();
 	void reorder_type_alias();
 
 
-	void propagate_nonuniform_qualifier(uint32_t id);
-
 	static const char *vector_swizzle(int vecsize, int index);
 	static const char *vector_swizzle(int vecsize, int index);
 
 
+	bool is_stage_output_location_masked(uint32_t location, uint32_t component) const;
+	bool is_stage_output_builtin_masked(spv::BuiltIn builtin) const;
+	bool is_stage_output_variable_masked(const SPIRVariable &var) const;
+	bool is_stage_output_block_member_masked(const SPIRVariable &var, uint32_t index, bool strip_array) const;
+	uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const;
+	uint32_t get_declared_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const;
+	std::unordered_set<LocationComponentPair, InternalHasher> masked_output_locations;
+	std::unordered_set<uint32_t> masked_output_builtins;
+
 private:
 private:
 	void init();
 	void init();
 };
 };

+ 279 - 227
src/libraries/spirv_cross/spirv_hlsl.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2016-2021 Robert Konrad
  * Copyright 2016-2021 Robert Konrad
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -19,7 +20,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_hlsl.hpp"
 #include "spirv_hlsl.hpp"
@@ -570,7 +570,7 @@ void CompilerHLSL::emit_builtin_outputs_in_struct()
 		switch (builtin)
 		switch (builtin)
 		{
 		{
 		case BuiltInPosition:
 		case BuiltInPosition:
-			type = "float4";
+			type = is_position_invariant() && backend.support_precise_qualifier ? "precise float4" : "float4";
 			semantic = legacy ? "POSITION" : "SV_Position";
 			semantic = legacy ? "POSITION" : "SV_Position";
 			break;
 			break;
 
 
@@ -641,7 +641,6 @@ void CompilerHLSL::emit_builtin_outputs_in_struct()
 
 
 		default:
 		default:
 			SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
 			SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
-			break;
 		}
 		}
 
 
 		if (type && semantic)
 		if (type && semantic)
@@ -770,7 +769,6 @@ void CompilerHLSL::emit_builtin_inputs_in_struct()
 
 
 		default:
 		default:
 			SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
 			SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
-			break;
 		}
 		}
 
 
 		if (type && semantic)
 		if (type && semantic)
@@ -818,8 +816,8 @@ string CompilerHLSL::to_interpolation_qualifiers(const Bitset &flags)
 		res += "patch "; // Seems to be different in actual HLSL.
 		res += "patch "; // Seems to be different in actual HLSL.
 	if (flags.get(DecorationSample))
 	if (flags.get(DecorationSample))
 		res += "sample ";
 		res += "sample ";
-	if (flags.get(DecorationInvariant))
-		res += "invariant "; // Not supported?
+	if (flags.get(DecorationInvariant) && backend.support_precise_qualifier)
+		res += "precise "; // Not supported?
 
 
 	return res;
 	return res;
 }
 }
@@ -854,48 +852,25 @@ std::string CompilerHLSL::to_initializer_expression(const SPIRVariable &var)
 		return CompilerGLSL::to_initializer_expression(var);
 		return CompilerGLSL::to_initializer_expression(var);
 }
 }
 
 
-void CompilerHLSL::emit_io_block(const SPIRVariable &var)
+void CompilerHLSL::emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index,
+                                                         uint32_t location,
+                                                         std::unordered_set<uint32_t> &active_locations)
 {
 {
 	auto &execution = get_entry_point();
 	auto &execution = get_entry_point();
-
-	auto &type = get<SPIRType>(var.basetype);
-	add_resource_name(type.self);
-
-	statement("struct ", to_name(type.self));
-	begin_scope();
-	type.member_name_cache.clear();
-
-	uint32_t base_location = get_decoration(var.self, DecorationLocation);
-
-	for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
-	{
-		string semantic;
-		if (has_member_decoration(type.self, i, DecorationLocation))
-		{
-			uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
-			semantic = join(" : ", to_semantic(location, execution.model, var.storage));
-		}
-		else
-		{
-			// If the block itself has a location, but not its members, use the implicit location.
-			// 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.
-			uint32_t location = base_location + i;
-			semantic = join(" : ", to_semantic(location, execution.model, var.storage));
-		}
-
-		add_member_name(type, i);
-
-		auto &membertype = get<SPIRType>(type.member_types[i]);
-		statement(to_interpolation_qualifiers(get_member_decoration_bitset(type.self, i)),
-		          variable_decl(membertype, to_member_name(type, i)), semantic, ";");
-	}
-
-	end_scope_decl();
-	statement("");
-
-	statement("static ", variable_decl(var), ";");
-	statement("");
+	auto type = get<SPIRType>(var.basetype);
+	auto semantic = to_semantic(location, execution.model, var.storage);
+	auto mbr_name = join(to_name(type.self), "_", to_member_name(type, member_index));
+	auto &mbr_type = get<SPIRType>(type.member_types[member_index]);
+
+	statement(to_interpolation_qualifiers(get_member_decoration_bitset(type.self, member_index)),
+	          type_to_glsl(mbr_type),
+	          " ", mbr_name, type_to_array_glsl(mbr_type),
+	          " : ", semantic, ";");
+
+	// Structs and arrays should consume more locations.
+	uint32_t consumed_locations = type_to_consumed_locations(mbr_type);
+	for (uint32_t i = 0; i < consumed_locations; i++)
+		active_locations.insert(location + i);
 }
 }
 
 
 void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set<uint32_t> &active_locations)
 void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set<uint32_t> &active_locations)
@@ -930,7 +905,6 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
 
 
 	bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
 	bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
 
 
-	auto &m = ir.meta[var.self].decoration;
 	auto name = to_name(var.self);
 	auto name = to_name(var.self);
 	if (use_location_number)
 	if (use_location_number)
 	{
 	{
@@ -938,8 +912,8 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
 
 
 		// If an explicit location exists, use it with TEXCOORD[N] semantic.
 		// If an explicit location exists, use it with TEXCOORD[N] semantic.
 		// Otherwise, pick a vacant location.
 		// Otherwise, pick a vacant location.
-		if (m.decoration_flags.get(DecorationLocation))
-			location_number = m.location;
+		if (has_decoration(var.self, DecorationLocation))
+			location_number = get_decoration(var.self, DecorationLocation);
 		else
 		else
 			location_number = get_vacant_location();
 			location_number = get_vacant_location();
 
 
@@ -1188,10 +1162,10 @@ void CompilerHLSL::emit_composite_constants()
 
 
 		auto &type = this->get<SPIRType>(c.constant_type);
 		auto &type = this->get<SPIRType>(c.constant_type);
 
 
-		// Cannot declare block type constants here.
-		// We do not have the struct type yet.
-		bool is_block = has_decoration(type.self, DecorationBlock);
-		if (!is_block && (type.basetype == SPIRType::Struct || !type.array.empty()))
+		if (type.basetype == SPIRType::Struct && is_builtin_type(type))
+			return;
+
+		if (type.basetype == SPIRType::Struct || !type.array.empty())
 		{
 		{
 			auto name = to_name(c.self);
 			auto name = to_name(c.self);
 			statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";");
 			statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";");
@@ -1209,6 +1183,18 @@ void CompilerHLSL::emit_specialization_constants_and_structs()
 	SpecializationConstant wg_x, wg_y, wg_z;
 	SpecializationConstant wg_x, wg_y, wg_z;
 	ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
 	ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
 
 
+	std::unordered_set<TypeID> io_block_types;
+	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
+		auto &type = this->get<SPIRType>(var.basetype);
+		if ((var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
+		    !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
+		    interface_variable_exists_in_entry_point(var.self) &&
+		    has_decoration(type.self, DecorationBlock))
+		{
+			io_block_types.insert(type.self);
+		}
+	});
+
 	auto loop_lock = ir.create_loop_hard_lock();
 	auto loop_lock = ir.create_loop_hard_lock();
 	for (auto &id_ : ir.ids_for_constant_or_type)
 	for (auto &id_ : ir.ids_for_constant_or_type)
 	{
 	{
@@ -1251,9 +1237,11 @@ void CompilerHLSL::emit_specialization_constants_and_structs()
 		else if (id.get_type() == TypeType)
 		else if (id.get_type() == TypeType)
 		{
 		{
 			auto &type = id.get<SPIRType>();
 			auto &type = id.get<SPIRType>();
-			if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
-			    (!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) &&
-			     !ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
+			bool is_non_io_block = has_decoration(type.self, DecorationBlock) &&
+			                       io_block_types.count(type.self) == 0;
+			bool is_buffer_block = has_decoration(type.self, DecorationBufferBlock);
+			if (type.basetype == SPIRType::Struct && type.array.empty() &&
+			    !type.pointer && !is_non_io_block && !is_buffer_block)
 			{
 			{
 				if (emitted)
 				if (emitted)
 					statement("");
 					statement("");
@@ -1379,16 +1367,12 @@ void CompilerHLSL::emit_resources()
 
 
 	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);
 
 
-		// Do not emit I/O blocks here.
-		// I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders
-		// and tessellation down the line.
-		if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
+		if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
 		    (var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) &&
 		    (var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) &&
 		    interface_variable_exists_in_entry_point(var.self))
 		    interface_variable_exists_in_entry_point(var.self))
 		{
 		{
-			// Only emit non-builtins which are not blocks here. Builtin variables are handled separately.
+			// Builtin variables are handled separately.
 			emit_interface_block_globally(var);
 			emit_interface_block_globally(var);
 			emitted = true;
 			emitted = true;
 		}
 		}
@@ -1402,69 +1386,72 @@ 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;
-	SmallVector<SPIRVariable *> input_variables;
-	SmallVector<SPIRVariable *> output_variables;
+
+	struct IOVariable
+	{
+		const SPIRVariable *var;
+		uint32_t location;
+		uint32_t block_member_index;
+		bool block;
+	};
+
+	SmallVector<IOVariable> input_variables;
+	SmallVector<IOVariable> 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 = has_decoration(type.self, DecorationBlock);
 
 
 		if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
 		if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
 			return;
 			return;
 
 
-		// Do not emit I/O blocks here.
-		// I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders
-		// and tessellation down the line.
-		if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
+		if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
 		    interface_variable_exists_in_entry_point(var.self))
 		    interface_variable_exists_in_entry_point(var.self))
 		{
 		{
-			if (var.storage == StorageClassInput)
-				input_variables.push_back(&var);
-			else
-				output_variables.push_back(&var);
-		}
-
-		// Reserve input and output locations for block variables as necessary.
-		if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
-		{
-			auto &active = var.storage == StorageClassInput ? active_inputs : active_outputs;
-			for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
+			if (block)
 			{
 			{
-				if (has_member_decoration(type.self, i, DecorationLocation))
+				for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
 				{
 				{
-					uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
-					active.insert(location);
+					uint32_t location = get_declared_member_location(var, i, false);
+					if (var.storage == StorageClassInput)
+						input_variables.push_back({ &var, location, i, true });
+					else
+						output_variables.push_back({ &var, location, i, true });
 				}
 				}
 			}
 			}
-
-			// Emit the block struct and a global variable here.
-			emit_io_block(var);
+			else
+			{
+				uint32_t location = get_decoration(var.self, DecorationLocation);
+				if (var.storage == StorageClassInput)
+					input_variables.push_back({ &var, location, 0, false });
+				else
+					output_variables.push_back({ &var, location, 0, false });
+			}
 		}
 		}
 	});
 	});
 
 
-	const auto variable_compare = [&](const SPIRVariable *a, const SPIRVariable *b) -> bool {
+	const auto variable_compare = [&](const IOVariable &a, const IOVariable &b) -> bool {
 		// Sort input and output variables based on, from more robust to less robust:
 		// Sort input and output variables based on, from more robust to less robust:
 		// - Location
 		// - Location
 		// - Variable has a location
 		// - Variable has a location
 		// - Name comparison
 		// - Name comparison
 		// - Variable has a name
 		// - Variable has a name
 		// - Fallback: ID
 		// - Fallback: ID
-		bool has_location_a = has_decoration(a->self, DecorationLocation);
-		bool has_location_b = has_decoration(b->self, DecorationLocation);
+		bool has_location_a = a.block || has_decoration(a.var->self, DecorationLocation);
+		bool has_location_b = b.block || has_decoration(b.var->self, DecorationLocation);
 
 
 		if (has_location_a && has_location_b)
 		if (has_location_a && has_location_b)
-		{
-			return get_decoration(a->self, DecorationLocation) < get_decoration(b->self, DecorationLocation);
-		}
+			return a.location < b.location;
 		else if (has_location_a && !has_location_b)
 		else if (has_location_a && !has_location_b)
 			return true;
 			return true;
 		else if (!has_location_a && has_location_b)
 		else if (!has_location_a && has_location_b)
 			return false;
 			return false;
 
 
-		const auto &name1 = to_name(a->self);
-		const auto &name2 = to_name(b->self);
+		const auto &name1 = to_name(a.var->self);
+		const auto &name2 = to_name(b.var->self);
 
 
 		if (name1.empty() && name2.empty())
 		if (name1.empty() && name2.empty())
-			return a->self < b->self;
+			return a.var->self < b.var->self;
 		else if (name1.empty())
 		else if (name1.empty())
 			return true;
 			return true;
 		else if (name2.empty())
 		else if (name2.empty())
@@ -1491,8 +1478,13 @@ void CompilerHLSL::emit_resources()
 
 
 		begin_scope();
 		begin_scope();
 		sort(input_variables.begin(), input_variables.end(), variable_compare);
 		sort(input_variables.begin(), input_variables.end(), variable_compare);
-		for (auto var : input_variables)
-			emit_interface_block_in_struct(*var, active_inputs);
+		for (auto &var : input_variables)
+		{
+			if (var.block)
+				emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_inputs);
+			else
+				emit_interface_block_in_struct(*var.var, active_inputs);
+		}
 		emit_builtin_inputs_in_struct();
 		emit_builtin_inputs_in_struct();
 		end_scope_decl();
 		end_scope_decl();
 		statement("");
 		statement("");
@@ -1504,10 +1496,14 @@ void CompilerHLSL::emit_resources()
 		statement("struct SPIRV_Cross_Output");
 		statement("struct SPIRV_Cross_Output");
 
 
 		begin_scope();
 		begin_scope();
-		// FIXME: Use locations properly if they exist.
 		sort(output_variables.begin(), output_variables.end(), variable_compare);
 		sort(output_variables.begin(), output_variables.end(), variable_compare);
-		for (auto var : output_variables)
-			emit_interface_block_in_struct(*var, active_outputs);
+		for (auto &var : output_variables)
+		{
+			if (var.block)
+				emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_outputs);
+			else
+				emit_interface_block_in_struct(*var.var, active_outputs);
+		}
 		emit_builtin_outputs_in_struct();
 		emit_builtin_outputs_in_struct();
 		end_scope_decl();
 		end_scope_decl();
 		statement("");
 		statement("");
@@ -1944,6 +1940,28 @@ void CompilerHLSL::emit_resources()
 		end_scope();
 		end_scope();
 		statement("");
 		statement("");
 	}
 	}
+
+	for (TypeID type_id : composite_selection_workaround_types)
+	{
+		// Need out variable since HLSL does not support returning arrays.
+		auto &type = get<SPIRType>(type_id);
+		auto type_str = type_to_glsl(type);
+		auto type_arr_str = type_to_array_glsl(type);
+		statement("void spvSelectComposite(out ", type_str, " out_value", type_arr_str, ", bool cond, ",
+		          type_str, " true_val", type_arr_str, ", ",
+		          type_str, " false_val", type_arr_str, ")");
+		begin_scope();
+		statement("if (cond)");
+		begin_scope();
+		statement("out_value = true_val;");
+		end_scope();
+		statement("else");
+		begin_scope();
+		statement("out_value = false_val;");
+		end_scope();
+		end_scope();
+		statement("");
+	}
 }
 }
 
 
 void CompilerHLSL::emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav,
 void CompilerHLSL::emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav,
@@ -2051,13 +2069,6 @@ void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type
 	if (index < memb.size())
 	if (index < memb.size())
 		memberflags = memb[index].decoration_flags;
 		memberflags = memb[index].decoration_flags;
 
 
-	string qualifiers;
-	bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
-	                ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
-
-	if (is_block)
-		qualifiers = to_interpolation_qualifiers(memberflags);
-
 	string packing_offset;
 	string packing_offset;
 	bool is_push_constant = type.storage == StorageClassPushConstant;
 	bool is_push_constant = type.storage == StorageClassPushConstant;
 
 
@@ -2072,7 +2083,7 @@ void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type
 		packing_offset = join(" : packoffset(c", offset / 16, packing_swizzle[(offset & 15) >> 2], ")");
 		packing_offset = join(" : packoffset(c", offset / 16, packing_swizzle[(offset & 15) >> 2], ")");
 	}
 	}
 
 
-	statement(layout_for_member(type, index), qualifiers, qualifier,
+	statement(layout_for_member(type, index), qualifier,
 	          variable_decl(membertype, to_member_name(type, index)), packing_offset, ";");
 	          variable_decl(membertype, to_member_name(type, index)), packing_offset, ";");
 }
 }
 
 
@@ -2249,7 +2260,7 @@ void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
 
 
 string CompilerHLSL::to_sampler_expression(uint32_t id)
 string CompilerHLSL::to_sampler_expression(uint32_t id)
 {
 {
-	auto expr = join("_", to_expression(id));
+	auto expr = join("_", to_non_uniform_aware_expression(id));
 	auto index = expr.find_first_of('[');
 	auto index = expr.find_first_of('[');
 	if (index == string::npos)
 	if (index == string::npos)
 	{
 	{
@@ -2369,7 +2380,7 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret
 		    arg_type.image.dim != DimBuffer)
 		    arg_type.image.dim != DimBuffer)
 		{
 		{
 			// Manufacture automatic sampler arg for SampledImage texture
 			// Manufacture automatic sampler arg for SampledImage texture
-			arglist.push_back(join(image_is_comparison(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ",
+			arglist.push_back(join(is_depth_image(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ",
 			                       to_sampler_expression(arg.id), type_to_array_glsl(arg_type)));
 			                       to_sampler_expression(arg.id), type_to_array_glsl(arg_type)));
 		}
 		}
 
 
@@ -2407,27 +2418,6 @@ void CompilerHLSL::emit_hlsl_entry_point()
 	if (require_input)
 	if (require_input)
 		arguments.push_back("SPIRV_Cross_Input stage_input");
 		arguments.push_back("SPIRV_Cross_Input stage_input");
 
 
-	// Add I/O blocks as separate arguments with appropriate storage qualifier.
-	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
-		auto &type = this->get<SPIRType>(var.basetype);
-		bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
-
-		if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
-			return;
-
-		if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
-		{
-			if (var.storage == StorageClassInput)
-			{
-				arguments.push_back(join("in ", variable_decl(type, join("stage_input", to_name(var.self)))));
-			}
-			else if (var.storage == StorageClassOutput)
-			{
-				arguments.push_back(join("out ", variable_decl(type, join("stage_output", to_name(var.self)))));
-			}
-		}
-	});
-
 	auto &execution = get_entry_point();
 	auto &execution = get_entry_point();
 
 
 	switch (execution.model)
 	switch (execution.model)
@@ -2588,36 +2578,43 @@ void CompilerHLSL::emit_hlsl_entry_point()
 	// Copy from stage input struct to globals.
 	// Copy from stage input struct to globals.
 	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 = has_decoration(type.self, DecorationBlock);
 
 
 		if (var.storage != StorageClassInput)
 		if (var.storage != StorageClassInput)
 			return;
 			return;
 
 
 		bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
 		bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
 
 
-		if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
+		if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
 		    interface_variable_exists_in_entry_point(var.self))
 		    interface_variable_exists_in_entry_point(var.self))
 		{
 		{
-			auto name = to_name(var.self);
-			auto &mtype = this->get<SPIRType>(var.basetype);
-			if (need_matrix_unroll && mtype.columns > 1)
+			if (block)
 			{
 			{
-				// Unroll matrices.
-				for (uint32_t col = 0; col < mtype.columns; col++)
-					statement(name, "[", col, "] = stage_input.", name, "_", col, ";");
+				auto type_name = to_name(type.self);
+				auto var_name = to_name(var.self);
+				for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++)
+				{
+					auto mbr_name = to_member_name(type, mbr_idx);
+					auto flat_name = join(type_name, "_", mbr_name);
+					statement(var_name, ".", mbr_name, " = stage_input.", flat_name, ";");
+				}
 			}
 			}
 			else
 			else
 			{
 			{
-				statement(name, " = stage_input.", name, ";");
+				auto name = to_name(var.self);
+				auto &mtype = this->get<SPIRType>(var.basetype);
+				if (need_matrix_unroll && mtype.columns > 1)
+				{
+					// Unroll matrices.
+					for (uint32_t col = 0; col < mtype.columns; col++)
+						statement(name, "[", col, "] = stage_input.", name, "_", col, ";");
+				}
+				else
+				{
+					statement(name, " = stage_input.", name, ";");
+				}
 			}
 			}
 		}
 		}
-
-		// I/O blocks don't use the common stage input/output struct, but separate outputs.
-		if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
-		{
-			auto name = to_name(var.self);
-			statement(name, " = stage_input", name, ";");
-		}
 	});
 	});
 
 
 	// Run the shader.
 	// Run the shader.
@@ -2630,22 +2627,6 @@ void CompilerHLSL::emit_hlsl_entry_point()
 	else
 	else
 		SPIRV_CROSS_THROW("Unsupported shader stage.");
 		SPIRV_CROSS_THROW("Unsupported shader stage.");
 
 
-	// Copy block outputs.
-	ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
-		auto &type = this->get<SPIRType>(var.basetype);
-		bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
-
-		if (var.storage != StorageClassOutput)
-			return;
-
-		// I/O blocks don't use the common stage input/output struct, but separate outputs.
-		if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
-		{
-			auto name = to_name(var.self);
-			statement("stage_output", name, " = ", name, ";");
-		}
-	});
-
 	// Copy stage outputs.
 	// Copy stage outputs.
 	if (require_output)
 	if (require_output)
 	{
 	{
@@ -2682,27 +2663,43 @@ void CompilerHLSL::emit_hlsl_entry_point()
 
 
 		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 = has_decoration(type.self, DecorationBlock);
 
 
 			if (var.storage != StorageClassOutput)
 			if (var.storage != StorageClassOutput)
 				return;
 				return;
 
 
-			if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
-			    !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
+			if (!var.remapped_variable && type.pointer &&
+			    !is_builtin_variable(var) &&
+			    interface_variable_exists_in_entry_point(var.self))
 			{
 			{
-				auto name = to_name(var.self);
-
-				if (legacy && execution.model == ExecutionModelFragment)
+				if (block)
 				{
 				{
-					string output_filler;
-					for (uint32_t size = type.vecsize; size < 4; ++size)
-						output_filler += ", 0.0";
-
-					statement("stage_output.", name, " = float4(", name, output_filler, ");");
+					// I/O blocks need to flatten output.
+					auto type_name = to_name(type.self);
+					auto var_name = to_name(var.self);
+					for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++)
+					{
+						auto mbr_name = to_member_name(type, mbr_idx);
+						auto flat_name = join(type_name, "_", mbr_name);
+						statement("stage_output.", flat_name, " = ", var_name, ".", mbr_name, ";");
+					}
 				}
 				}
 				else
 				else
 				{
 				{
-					statement("stage_output.", name, " = ", name, ";");
+					auto name = to_name(var.self);
+
+					if (legacy && execution.model == ExecutionModelFragment)
+					{
+						string output_filler;
+						for (uint32_t size = type.vecsize; size < 4; ++size)
+							output_filler += ", 0.0";
+
+						statement("stage_output.", name, " = float4(", name, output_filler, ");");
+					}
+					else
+					{
+						statement("stage_output.", name, " = ", name, ";");
+					}
 				}
 				}
 			}
 			}
 		});
 		});
@@ -2754,13 +2751,16 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
 	bool proj = false;
 	bool proj = false;
 	const uint32_t *opt = nullptr;
 	const uint32_t *opt = nullptr;
 	auto *combined_image = maybe_get<SPIRCombinedImageSampler>(img);
 	auto *combined_image = maybe_get<SPIRCombinedImageSampler>(img);
-	auto img_expr = to_expression(combined_image ? combined_image->image : img);
 
 
-	inherited_expressions.push_back(coord);
+	if (combined_image && has_decoration(img, DecorationNonUniform))
+	{
+		set_decoration(combined_image->image, DecorationNonUniform);
+		set_decoration(combined_image->sampler, DecorationNonUniform);
+	}
+
+	auto img_expr = to_non_uniform_aware_expression(combined_image ? combined_image->image : img);
 
 
-	// Make sure non-uniform decoration is back-propagated to where it needs to be.
-	if (has_decoration(img, DecorationNonUniformEXT))
-		propagate_nonuniform_qualifier(img);
+	inherited_expressions.push_back(coord);
 
 
 	switch (op)
 	switch (op)
 	{
 	{
@@ -2910,7 +2910,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
 		{
 		{
 			texop += img_expr;
 			texop += img_expr;
 
 
-			if (image_is_comparison(imgtype, img))
+			if (is_depth_image(imgtype, img))
 			{
 			{
 				if (gather)
 				if (gather)
 				{
 				{
@@ -3016,7 +3016,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
 	{
 	{
 		string sampler_expr;
 		string sampler_expr;
 		if (combined_image)
 		if (combined_image)
-			sampler_expr = to_expression(combined_image->sampler);
+			sampler_expr = to_non_uniform_aware_expression(combined_image->sampler);
 		else
 		else
 			sampler_expr = to_sampler_expression(img);
 			sampler_expr = to_sampler_expression(img);
 		expr += sampler_expr;
 		expr += sampler_expr;
@@ -3386,7 +3386,7 @@ void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var)
 		if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
 		if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
 		{
 		{
 			// For combined image samplers, also emit a combined image sampler.
 			// For combined image samplers, also emit a combined image sampler.
-			if (image_is_comparison(type, var.self))
+			if (is_depth_image(type, var.self))
 				statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type),
 				statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type),
 				          to_resource_binding_sampler(var), ";");
 				          to_resource_binding_sampler(var), ";");
 			else
 			else
@@ -3812,6 +3812,10 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
 		SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported, unless SM 6.2 and "
 		SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported, unless SM 6.2 and "
 		                  "native 16-bit types are enabled.");
 		                  "native 16-bit types are enabled.");
 
 
+	string base = chain.base;
+	if (has_decoration(chain.self, DecorationNonUniform))
+		convert_non_uniform_expression(base, chain.self);
+
 	bool templated_load = hlsl_options.shader_model >= 62;
 	bool templated_load = hlsl_options.shader_model >= 62;
 	string load_expr;
 	string load_expr;
 
 
@@ -3844,7 +3848,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
 		if (templated_load)
 		if (templated_load)
 			load_op = "Load";
 			load_op = "Load";
 
 
-		load_expr = join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")");
+		load_expr = join(base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")");
 	}
 	}
 	else if (type.columns == 1)
 	else if (type.columns == 1)
 	{
 	{
@@ -3866,7 +3870,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
 
 
 		for (uint32_t r = 0; r < type.vecsize; r++)
 		for (uint32_t r = 0; r < type.vecsize; r++)
 		{
 		{
-			load_expr += join(chain.base, ".Load", template_expr, "(", chain.dynamic_index,
+			load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index,
 			                  chain.static_index + r * chain.matrix_stride, ")");
 			                  chain.static_index + r * chain.matrix_stride, ")");
 			if (r + 1 < type.vecsize)
 			if (r + 1 < type.vecsize)
 				load_expr += ", ";
 				load_expr += ", ";
@@ -3915,7 +3919,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
 
 
 		for (uint32_t c = 0; c < type.columns; c++)
 		for (uint32_t c = 0; c < type.columns; c++)
 		{
 		{
-			load_expr += join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index,
+			load_expr += join(base, ".", load_op, template_expr, "(", chain.dynamic_index,
 			                  chain.static_index + c * chain.matrix_stride, ")");
 			                  chain.static_index + c * chain.matrix_stride, ")");
 			if (c + 1 < type.columns)
 			if (c + 1 < type.columns)
 				load_expr += ", ";
 				load_expr += ", ";
@@ -3944,7 +3948,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
 		{
 		{
 			for (uint32_t r = 0; r < type.vecsize; r++)
 			for (uint32_t r = 0; r < type.vecsize; r++)
 			{
 			{
-				load_expr += join(chain.base, ".Load", template_expr, "(", chain.dynamic_index,
+				load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index,
 				                  chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")");
 				                  chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")");
 
 
 				if ((r + 1 < type.vecsize) || (c + 1 < type.columns))
 				if ((r + 1 < type.vecsize) || (c + 1 < type.columns))
@@ -3981,9 +3985,6 @@ void CompilerHLSL::emit_load(const Instruction &instruction)
 		uint32_t id = ops[1];
 		uint32_t id = ops[1];
 		uint32_t ptr = ops[2];
 		uint32_t ptr = ops[2];
 
 
-		if (has_decoration(ptr, DecorationNonUniformEXT))
-			propagate_nonuniform_qualifier(ptr);
-
 		auto &type = get<SPIRType>(result_type);
 		auto &type = get<SPIRType>(result_type);
 		bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct;
 		bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct;
 
 
@@ -4122,9 +4123,6 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
 	// Make sure we trigger a read of the constituents in the access chain.
 	// Make sure we trigger a read of the constituents in the access chain.
 	track_expression_read(chain.self);
 	track_expression_read(chain.self);
 
 
-	if (has_decoration(chain.self, DecorationNonUniformEXT))
-		propagate_nonuniform_qualifier(chain.self);
-
 	SPIRType target_type;
 	SPIRType target_type;
 	target_type.basetype = SPIRType::UInt;
 	target_type.basetype = SPIRType::UInt;
 	target_type.vecsize = type.vecsize;
 	target_type.vecsize = type.vecsize;
@@ -4148,6 +4146,10 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
 
 
 	bool templated_store = hlsl_options.shader_model >= 62;
 	bool templated_store = hlsl_options.shader_model >= 62;
 
 
+	auto base = chain.base;
+	if (has_decoration(chain.self, DecorationNonUniform))
+		convert_non_uniform_expression(base, chain.self);
+
 	string template_expr;
 	string template_expr;
 	if (templated_store)
 	if (templated_store)
 		template_expr = join("<", type_to_glsl(type), ">");
 		template_expr = join("<", type_to_glsl(type), ">");
@@ -4183,7 +4185,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
 		}
 		}
 		else
 		else
 			store_op = "Store";
 			store_op = "Store";
-		statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ",
+		statement(base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ",
 		          store_expr, ");");
 		          store_expr, ");");
 	}
 	}
 	else if (type.columns == 1)
 	else if (type.columns == 1)
@@ -4214,7 +4216,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
 					store_expr = join(bitcast_op, "(", store_expr, ")");
 					store_expr = join(bitcast_op, "(", store_expr, ")");
 			}
 			}
 
 
-			statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index,
+			statement(base, ".Store", template_expr, "(", chain.dynamic_index,
 			          chain.static_index + chain.matrix_stride * r, ", ", store_expr, ");");
 			          chain.static_index + chain.matrix_stride * r, ", ", store_expr, ");");
 		}
 		}
 	}
 	}
@@ -4258,7 +4260,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
 					store_expr = join(bitcast_op, "(", store_expr, ")");
 					store_expr = join(bitcast_op, "(", store_expr, ")");
 			}
 			}
 
 
-			statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index,
+			statement(base, ".", store_op, template_expr, "(", chain.dynamic_index,
 			          chain.static_index + c * chain.matrix_stride, ", ", store_expr, ");");
 			          chain.static_index + c * chain.matrix_stride, ", ", store_expr, ");");
 		}
 		}
 	}
 	}
@@ -4282,7 +4284,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
 				auto bitcast_op = bitcast_glsl_op(target_type, type);
 				auto bitcast_op = bitcast_glsl_op(target_type, type);
 				if (!bitcast_op.empty())
 				if (!bitcast_op.empty())
 					store_expr = join(bitcast_op, "(", store_expr, ")");
 					store_expr = join(bitcast_op, "(", store_expr, ")");
-				statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index,
+				statement(base, ".Store", template_expr, "(", chain.dynamic_index,
 				          chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");");
 				          chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");");
 			}
 			}
 		}
 		}
@@ -4384,9 +4386,6 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
 			inherit_expression_dependencies(ops[1], ops[i]);
 			inherit_expression_dependencies(ops[1], ops[i]);
 			add_implied_read_expression(e, ops[i]);
 			add_implied_read_expression(e, ops[i]);
 		}
 		}
-
-		if (has_decoration(ops[1], DecorationNonUniformEXT))
-			propagate_nonuniform_qualifier(ops[1]);
 	}
 	}
 	else
 	else
 	{
 	{
@@ -4486,13 +4485,16 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
 
 
 		if (data_type.storage == StorageClassImage || !chain)
 		if (data_type.storage == StorageClassImage || !chain)
 		{
 		{
-			statement(atomic_op, "(", to_expression(ops[0]), ", ", to_expression(ops[3]), ", ", to_expression(tmp_id),
-			          ");");
+			statement(atomic_op, "(", to_non_uniform_aware_expression(ops[0]), ", ",
+			          to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
 		}
 		}
 		else
 		else
 		{
 		{
+			string base = chain->base;
+			if (has_decoration(chain->self, DecorationNonUniform))
+				convert_non_uniform_expression(base, chain->self);
 			// RWByteAddress buffer is always uint in its underlying type.
 			// RWByteAddress buffer is always uint in its underlying type.
-			statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ",
+			statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ",
 			          to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
 			          to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
 		}
 		}
 	}
 	}
@@ -4510,14 +4512,17 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
 		SPIRType::BaseType expr_type;
 		SPIRType::BaseType expr_type;
 		if (data_type.storage == StorageClassImage || !chain)
 		if (data_type.storage == StorageClassImage || !chain)
 		{
 		{
-			statement(atomic_op, "(", to_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
+			statement(atomic_op, "(", to_non_uniform_aware_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
 			expr_type = data_type.basetype;
 			expr_type = data_type.basetype;
 		}
 		}
 		else
 		else
 		{
 		{
 			// RWByteAddress buffer is always uint in its underlying type.
 			// RWByteAddress buffer is always uint in its underlying type.
+			string base = chain->base;
+			if (has_decoration(chain->self, DecorationNonUniform))
+				convert_non_uniform_expression(base, chain->self);
 			expr_type = SPIRType::UInt;
 			expr_type = SPIRType::UInt;
-			statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr,
+			statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr,
 			          ", ", to_name(id), ");");
 			          ", ", to_name(id), ");");
 		}
 		}
 
 
@@ -4581,19 +4586,15 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
 
 
 	case OpGroupNonUniformInverseBallot:
 	case OpGroupNonUniformInverseBallot:
 		SPIRV_CROSS_THROW("Cannot trivially implement InverseBallot in HLSL.");
 		SPIRV_CROSS_THROW("Cannot trivially implement InverseBallot in HLSL.");
-		break;
 
 
 	case OpGroupNonUniformBallotBitExtract:
 	case OpGroupNonUniformBallotBitExtract:
 		SPIRV_CROSS_THROW("Cannot trivially implement BallotBitExtract in HLSL.");
 		SPIRV_CROSS_THROW("Cannot trivially implement BallotBitExtract in HLSL.");
-		break;
 
 
 	case OpGroupNonUniformBallotFindLSB:
 	case OpGroupNonUniformBallotFindLSB:
 		SPIRV_CROSS_THROW("Cannot trivially implement BallotFindLSB in HLSL.");
 		SPIRV_CROSS_THROW("Cannot trivially implement BallotFindLSB in HLSL.");
-		break;
 
 
 	case OpGroupNonUniformBallotFindMSB:
 	case OpGroupNonUniformBallotFindMSB:
 		SPIRV_CROSS_THROW("Cannot trivially implement BallotFindMSB in HLSL.");
 		SPIRV_CROSS_THROW("Cannot trivially implement BallotFindMSB in HLSL.");
-		break;
 
 
 	case OpGroupNonUniformBallotBitCount:
 	case OpGroupNonUniformBallotBitCount:
 	{
 	{
@@ -4618,13 +4619,35 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
 	}
 	}
 
 
 	case OpGroupNonUniformShuffle:
 	case OpGroupNonUniformShuffle:
-		SPIRV_CROSS_THROW("Cannot trivially implement Shuffle in HLSL.");
+		emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt");
+		break;
 	case OpGroupNonUniformShuffleXor:
 	case OpGroupNonUniformShuffleXor:
-		SPIRV_CROSS_THROW("Cannot trivially implement ShuffleXor in HLSL.");
+	{
+		bool forward = should_forward(ops[3]);
+		emit_op(ops[0], ops[1],
+		        join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
+		             "WaveGetLaneIndex() ^ ", to_enclosed_expression(ops[4]), ")"), forward);
+		inherit_expression_dependencies(ops[1], ops[3]);
+		break;
+	}
 	case OpGroupNonUniformShuffleUp:
 	case OpGroupNonUniformShuffleUp:
-		SPIRV_CROSS_THROW("Cannot trivially implement ShuffleUp in HLSL.");
+	{
+		bool forward = should_forward(ops[3]);
+		emit_op(ops[0], ops[1],
+		        join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
+		             "WaveGetLaneIndex() - ", to_enclosed_expression(ops[4]), ")"), forward);
+		inherit_expression_dependencies(ops[1], ops[3]);
+		break;
+	}
 	case OpGroupNonUniformShuffleDown:
 	case OpGroupNonUniformShuffleDown:
-		SPIRV_CROSS_THROW("Cannot trivially implement ShuffleDown in HLSL.");
+	{
+		bool forward = should_forward(ops[3]);
+		emit_op(ops[0], ops[1],
+		        join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
+		             "WaveGetLaneIndex() + ", to_enclosed_expression(ops[4]), ")"), forward);
+		inherit_expression_dependencies(ops[1], ops[3]);
+		break;
+	}
 
 
 	case OpGroupNonUniformAll:
 	case OpGroupNonUniformAll:
 		emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllTrue");
 		emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllTrue");
@@ -4771,6 +4794,34 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 		break;
 		break;
 	}
 	}
 
 
+	case OpSelect:
+	{
+		auto &value_type = expression_type(ops[3]);
+		if (value_type.basetype == SPIRType::Struct || is_array(value_type))
+		{
+			// HLSL does not support ternary expressions on composites.
+			// Cannot use branches, since we might be in a continue block
+			// where explicit control flow is prohibited.
+			// Emit a helper function where we can use control flow.
+			TypeID value_type_id = expression_type_id(ops[3]);
+			auto itr = std::find(composite_selection_workaround_types.begin(),
+			                     composite_selection_workaround_types.end(),
+			                     value_type_id);
+			if (itr == composite_selection_workaround_types.end())
+			{
+				composite_selection_workaround_types.push_back(value_type_id);
+				force_recompile();
+			}
+			emit_uninitialized_temporary_expression(ops[0], ops[1]);
+			statement("spvSelectComposite(",
+					  to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
+					  to_expression(ops[3]), ", ", to_expression(ops[4]), ");");
+		}
+		else
+			CompilerGLSL::emit_instruction(instruction);
+		break;
+	}
+
 	case OpStore:
 	case OpStore:
 	{
 	{
 		emit_store(instruction);
 		emit_store(instruction);
@@ -5150,7 +5201,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 		auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
 		auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
 		statement("uint ", dummy_samples_levels, ";");
 		statement("uint ", dummy_samples_levels, ";");
 
 
-		auto expr = join("spvTextureSize(", to_expression(ops[2]), ", ",
+		auto expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", ",
 		                 bitcast_expression(SPIRType::UInt, ops[3]), ", ", dummy_samples_levels, ")");
 		                 bitcast_expression(SPIRType::UInt, ops[3]), ", ", dummy_samples_levels, ")");
 
 
 		auto &restype = get<SPIRType>(ops[0]);
 		auto &restype = get<SPIRType>(ops[0]);
@@ -5176,9 +5227,9 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 
 
 		string expr;
 		string expr;
 		if (uav)
 		if (uav)
-			expr = join("spvImageSize(", to_expression(ops[2]), ", ", dummy_samples_levels, ")");
+			expr = join("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", dummy_samples_levels, ")");
 		else
 		else
-			expr = join("spvTextureSize(", to_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
+			expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
 
 
 		auto &restype = get<SPIRType>(ops[0]);
 		auto &restype = get<SPIRType>(ops[0]);
 		expr = bitcast_expression(restype, SPIRType::UInt, expr);
 		expr = bitcast_expression(restype, SPIRType::UInt, expr);
@@ -5208,9 +5259,9 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 		statement(variable_decl(type, to_name(id)), ";");
 		statement(variable_decl(type, to_name(id)), ";");
 
 
 		if (uav)
 		if (uav)
-			statement("spvImageSize(", to_expression(ops[2]), ", ", to_name(id), ");");
+			statement("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", to_name(id), ");");
 		else
 		else
-			statement("spvTextureSize(", to_expression(ops[2]), ", 0u, ", to_name(id), ");");
+			statement("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", to_name(id), ");");
 
 
 		auto &restype = get<SPIRType>(ops[0]);
 		auto &restype = get<SPIRType>(ops[0]);
 		auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id));
 		auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id));
@@ -5241,16 +5292,16 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 				if (operands != ImageOperandsSampleMask || instruction.length != 6)
 				if (operands != ImageOperandsSampleMask || instruction.length != 6)
 					SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
 					SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
 				uint32_t sample = ops[5];
 				uint32_t sample = ops[5];
-				imgexpr = join(to_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")");
+				imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")");
 			}
 			}
 			else
 			else
-				imgexpr = join(to_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))");
+				imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))");
 
 
 			pure = true;
 			pure = true;
 		}
 		}
 		else
 		else
 		{
 		{
-			imgexpr = join(to_expression(ops[2]), "[", to_expression(ops[3]), "]");
+			imgexpr = join(to_non_uniform_aware_expression(ops[2]), "[", to_expression(ops[3]), "]");
 			// The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
 			// The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
 			// except that the underlying type changes how the data is interpreted.
 			// except that the underlying type changes how the data is interpreted.
 
 
@@ -5299,7 +5350,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 			value_expr = remap_swizzle(narrowed_type, expression_type(ops[2]).vecsize, value_expr);
 			value_expr = remap_swizzle(narrowed_type, expression_type(ops[2]).vecsize, value_expr);
 		}
 		}
 
 
-		statement(to_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";");
+		statement(to_non_uniform_aware_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";");
 		if (var && variable_storage_is_aliased(*var))
 		if (var && variable_storage_is_aliased(*var))
 			flush_all_aliased_variables();
 			flush_all_aliased_variables();
 		break;
 		break;
@@ -5311,10 +5362,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 		uint32_t id = ops[1];
 		uint32_t id = ops[1];
 
 
 		auto expr = to_expression(ops[2]);
 		auto expr = to_expression(ops[2]);
-		if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ops[2], DecorationNonUniformEXT))
-			convert_non_uniform_expression(expression_type(ops[2]), expr);
 		expr += join("[", to_expression(ops[3]), "]");
 		expr += join("[", to_expression(ops[3]), "]");
-
 		auto &e = set<SPIRExpression>(id, expr, result_type, true);
 		auto &e = set<SPIRExpression>(id, expr, result_type, true);
 
 
 		// When using the pointer, we need to know which variable it is actually loaded from.
 		// When using the pointer, we need to know which variable it is actually loaded from.
@@ -5492,7 +5540,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 
 
 	case OpArrayLength:
 	case OpArrayLength:
 	{
 	{
-		auto *var = maybe_get<SPIRVariable>(ops[2]);
+		auto *var = maybe_get_backing_variable(ops[2]);
 		if (!var)
 		if (!var)
 			SPIRV_CROSS_THROW("Array length must point directly to an SSBO block.");
 			SPIRV_CROSS_THROW("Array length must point directly to an SSBO block.");
 
 
@@ -5502,7 +5550,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
 
 
 		// This must be 32-bit uint, so we're good to go.
 		// This must be 32-bit uint, so we're good to go.
 		emit_uninitialized_temporary_expression(ops[0], ops[1]);
 		emit_uninitialized_temporary_expression(ops[0], ops[1]);
-		statement(to_expression(ops[2]), ".GetDimensions(", to_expression(ops[1]), ");");
+		statement(to_non_uniform_aware_expression(ops[2]), ".GetDimensions(", to_expression(ops[1]), ");");
 		uint32_t offset = type_struct_member_offset(type, ops[3]);
 		uint32_t offset = type_struct_member_offset(type, ops[3]);
 		uint32_t stride = type_struct_member_array_stride(type, ops[3]);
 		uint32_t stride = type_struct_member_array_stride(type, ops[3]);
 		statement(to_expression(ops[1]), " = (", to_expression(ops[1]), " - ", offset, ") / ", stride, ";");
 		statement(to_expression(ops[1]), " = (", to_expression(ops[1]), " - ", offset, ") / ", stride, ";");
@@ -5648,6 +5696,7 @@ VariableID CompilerHLSL::remap_num_workgroups_builtin()
 	ir.meta[variable_id].decoration.alias = "SPIRV_Cross_NumWorkgroups";
 	ir.meta[variable_id].decoration.alias = "SPIRV_Cross_NumWorkgroups";
 
 
 	num_workgroups_builtin = variable_id;
 	num_workgroups_builtin = variable_id;
+	get_entry_point().interface_variables.push_back(num_workgroups_builtin);
 	return variable_id;
 	return variable_id;
 }
 }
 
 
@@ -5718,6 +5767,9 @@ string CompilerHLSL::compile()
 	backend.nonuniform_qualifier = "NonUniformResourceIndex";
 	backend.nonuniform_qualifier = "NonUniformResourceIndex";
 	backend.support_case_fallthrough = false;
 	backend.support_case_fallthrough = false;
 
 
+	// SM 4.1 does not support precise for some reason.
+	backend.support_precise_qualifier = hlsl_options.shader_model >= 50 || hlsl_options.shader_model == 40;
+
 	fixup_type_alias();
 	fixup_type_alias();
 	reorder_type_alias();
 	reorder_type_alias();
 	build_function_control_flow_graphs_and_analyze();
 	build_function_control_flow_graphs_and_analyze();

+ 7 - 3
src/libraries/spirv_cross/spirv_hlsl.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2016-2021 Robert Konrad
  * Copyright 2016-2021 Robert Konrad
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_HLSL_HPP
 #ifndef SPIRV_HLSL_HPP
@@ -219,7 +219,10 @@ private:
 	void emit_resources();
 	void emit_resources();
 	void declare_undefined_values() override;
 	void declare_undefined_values() override;
 	void emit_interface_block_globally(const SPIRVariable &type);
 	void emit_interface_block_globally(const SPIRVariable &type);
-	void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set<uint32_t> &active_locations);
+	void emit_interface_block_in_struct(const SPIRVariable &var, std::unordered_set<uint32_t> &active_locations);
+	void emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index,
+	                                           uint32_t location,
+	                                           std::unordered_set<uint32_t> &active_locations);
 	void emit_builtin_inputs_in_struct();
 	void emit_builtin_inputs_in_struct();
 	void emit_builtin_outputs_in_struct();
 	void emit_builtin_outputs_in_struct();
 	void emit_texture_op(const Instruction &i, bool sparse) override;
 	void emit_texture_op(const Instruction &i, bool sparse) override;
@@ -347,7 +350,6 @@ private:
 
 
 	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);
 	std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc);
 	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;
@@ -369,6 +371,8 @@ private:
 
 
 	// Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but SV_Coverage is a scalar in HLSL.
 	// Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but SV_Coverage is a scalar in HLSL.
 	bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override;
 	bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override;
+
+	std::vector<TypeID> composite_selection_workaround_types;
 };
 };
 } // namespace SPIRV_CROSS_NAMESPACE
 } // namespace SPIRV_CROSS_NAMESPACE
 
 

File diff suppressed because it is too large
+ 437 - 223
src/libraries/spirv_cross/spirv_msl.cpp


+ 75 - 19
src/libraries/spirv_cross/spirv_msl.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2016-2021 The Brenwill Workshop Ltd.
  * Copyright 2016-2021 The Brenwill Workshop Ltd.
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_MSL_HPP
 #ifndef SPIRV_CROSS_MSL_HPP
@@ -60,6 +60,7 @@ enum MSLShaderInputFormat
 struct MSLShaderInput
 struct MSLShaderInput
 {
 {
 	uint32_t location = 0;
 	uint32_t location = 0;
+	uint32_t component = 0;
 	MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER;
 	MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER;
 	spv::BuiltIn builtin = spv::BuiltInMax;
 	spv::BuiltIn builtin = spv::BuiltInMax;
 	uint32_t vecsize = 0;
 	uint32_t vecsize = 0;
@@ -71,15 +72,23 @@ struct MSLShaderInput
 // resources consumed by this binding, if the binding represents an array of resources.
 // resources consumed by this binding, if the binding represents an array of resources.
 // If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value
 // If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value
 // will be used to declare the array size in MSL, which does not support run-time-sized arrays.
 // will be used to declare the array size in MSL, which does not support run-time-sized arrays.
-// For resources that are not held in a run-time-sized array, the count field does not need to be populated.
+// If pad_argument_buffer_resources is enabled, the base_type and count values are used to
+// specify the base type and array size of the resource in the argument buffer, if that resource
+// is not defined and used by the shader. With pad_argument_buffer_resources enabled, this
+// information will be used to pad the argument buffer structure, in order to align that
+// structure consistently for all uses, across all shaders, of the descriptor set represented
+// by the arugment buffer. If pad_argument_buffer_resources is disabled, base_type does not
+// need to be populated, and if the resource is also not a run-time sized array, the count
+// field does not need to be populated.
 // If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set,
 // If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set,
 // and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we
 // and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we
 // remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure.
 // remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure.
-// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will become a
-// [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
+// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will
+// become a [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
 struct MSLResourceBinding
 struct MSLResourceBinding
 {
 {
 	spv::ExecutionModel stage = spv::ExecutionModelMax;
 	spv::ExecutionModel stage = spv::ExecutionModelMax;
+	SPIRType::BaseType basetype = SPIRType::Unknown;
 	uint32_t desc_set = 0;
 	uint32_t desc_set = 0;
 	uint32_t binding = 0;
 	uint32_t binding = 0;
 	uint32_t count = 0;
 	uint32_t count = 0;
@@ -346,6 +355,19 @@ public:
 		// and would otherwise declare a different IAB.
 		// and would otherwise declare a different IAB.
 		bool force_active_argument_buffer_resources = false;
 		bool force_active_argument_buffer_resources = false;
 
 
+		// Aligns each resource in an argument buffer to its assigned index value, id(N),
+		// by adding synthetic padding members in the argument buffer struct for any resources
+		// in the argument buffer that are not defined and used by the shader. This allows
+		// the shader to index into the correct argument in a descriptor set argument buffer
+		// that is shared across shaders, where not all resources in the argument buffer are
+		// defined in each shader. For this to work, an MSLResourceBinding must be provided for
+		// all descriptors in any descriptor set held in an argument buffer in the shader, and
+		// that MSLResourceBinding must have the basetype and count members populated correctly.
+		// The implementation here assumes any inline blocks in the argument buffer is provided
+		// in a Metal buffer, and doesn't take into consideration inline blocks that are
+		// optionally embedded directly into the argument buffer via add_inline_uniform_block().
+		bool pad_argument_buffer_resources = false;
+
 		// Forces the use of plain arrays, which works around certain driver bugs on certain versions
 		// Forces the use of plain arrays, which works around certain driver bugs on certain versions
 		// of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210.
 		// of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210.
 		// May reduce performance in scenarios where arrays are copied around as value-types.
 		// May reduce performance in scenarios where arrays are copied around as value-types.
@@ -634,6 +656,8 @@ protected:
 		SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations
 		SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations
 		SPVFuncImplFMul,
 		SPVFuncImplFMul,
 		SPVFuncImplFAdd,
 		SPVFuncImplFAdd,
+		SPVFuncImplFSub,
+		SPVFuncImplQuantizeToF16,
 		SPVFuncImplCubemapTo2DArrayFace,
 		SPVFuncImplCubemapTo2DArrayFace,
 		SPVFuncImplUnsafeArray, // Allow Metal to use the array<T> template to make arrays a value type
 		SPVFuncImplUnsafeArray, // Allow Metal to use the array<T> template to make arrays a value type
 		SPVFuncImplInverse4x4,
 		SPVFuncImplInverse4x4,
@@ -708,13 +732,17 @@ protected:
 	                        const std::string &qualifier = "", uint32_t base_offset = 0) override;
 	                        const std::string &qualifier = "", uint32_t base_offset = 0) override;
 	void emit_struct_padding_target(const SPIRType &type) override;
 	void emit_struct_padding_target(const SPIRType &type) override;
 	std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
 	std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
+	void emit_block_hints(const SPIRBlock &block) override;
 
 
 	// Allow Metal to use the array<T> template to make arrays a value type
 	// Allow Metal to use the array<T> template to make arrays a value type
 	std::string type_to_array_glsl(const SPIRType &type) override;
 	std::string type_to_array_glsl(const SPIRType &type) override;
+	std::string constant_op_expression(const SPIRConstantOp &cop) override;
 
 
 	// Threadgroup arrays can't have a wrapper type
 	// Threadgroup arrays can't have a wrapper type
 	std::string variable_decl(const SPIRVariable &variable) override;
 	std::string variable_decl(const SPIRVariable &variable) override;
 
 
+	bool variable_decl_is_remapped_storage(const SPIRVariable &variable, spv::StorageClass storage) const override;
+
 	// GCC workaround of lambdas calling protected functions (for older GCC versions)
 	// GCC workaround of lambdas calling protected functions (for older GCC versions)
 	std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override;
 	std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override;
 
 
@@ -775,33 +803,44 @@ protected:
 	{
 	{
 		struct LocationMeta
 		struct LocationMeta
 		{
 		{
+			uint32_t base_type_id = 0;
 			uint32_t num_components = 0;
 			uint32_t num_components = 0;
-			uint32_t ib_index = ~0u;
+			bool flat = false;
+			bool noperspective = false;
+			bool centroid = false;
+			bool sample = false;
 		};
 		};
 		std::unordered_map<uint32_t, LocationMeta> location_meta;
 		std::unordered_map<uint32_t, LocationMeta> location_meta;
 		bool strip_array = false;
 		bool strip_array = false;
+		bool allow_local_declaration = false;
 	};
 	};
 
 
+	std::string to_tesc_invocation_id();
+	void emit_local_masked_variable(const SPIRVariable &masked_var, bool strip_array);
 	void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type,
 	void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type,
 	                                     SPIRVariable &var, InterfaceBlockMeta &meta);
 	                                     SPIRVariable &var, InterfaceBlockMeta &meta);
 	void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	                                               SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
 	                                               SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
 	void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	                                           SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
 	                                           SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
+	bool add_component_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
+	                                               SPIRVariable &var, const SPIRType &type,
+	                                               InterfaceBlockMeta &meta);
 	void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	                                                  SPIRType &ib_type, SPIRVariable &var, uint32_t index,
 	                                                  SPIRType &ib_type, SPIRVariable &var, uint32_t index,
 	                                                  InterfaceBlockMeta &meta);
 	                                                  InterfaceBlockMeta &meta);
 	void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
 	                                                      SPIRType &ib_type, SPIRVariable &var, uint32_t index,
 	                                                      SPIRType &ib_type, SPIRVariable &var, uint32_t index,
 	                                                      InterfaceBlockMeta &meta);
 	                                                      InterfaceBlockMeta &meta);
-	uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array);
 	void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var);
 	void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var);
 
 
 	void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id);
 	void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id);
 
 
-	void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, spv::StorageClass storage);
+	void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type,
+	                                     spv::StorageClass storage, bool fallback = false);
 	uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
 	uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
-	uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0);
+	uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t component,
+	                                   uint32_t num_components, bool strip_array);
 
 
 	void emit_custom_templates();
 	void emit_custom_templates();
 	void emit_custom_functions();
 	void emit_custom_functions();
@@ -825,11 +864,13 @@ protected:
 	std::string to_swizzle_expression(uint32_t id);
 	std::string to_swizzle_expression(uint32_t id);
 	std::string to_buffer_size_expression(uint32_t id);
 	std::string to_buffer_size_expression(uint32_t id);
 	bool is_sample_rate() const;
 	bool is_sample_rate() const;
+	bool is_intersection_query() const;
 	bool is_direct_input_builtin(spv::BuiltIn builtin);
 	bool is_direct_input_builtin(spv::BuiltIn builtin);
 	std::string builtin_qualifier(spv::BuiltIn builtin);
 	std::string builtin_qualifier(spv::BuiltIn builtin);
 	std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0);
 	std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0);
 	std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma);
 	std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma);
 	std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
 	std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
+	std::string member_location_attribute_qualifier(const SPIRType &type, uint32_t index);
 	std::string argument_decl(const SPIRFunction::Parameter &arg);
 	std::string argument_decl(const SPIRFunction::Parameter &arg);
 	std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
 	std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
 	uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
 	uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
@@ -886,11 +927,13 @@ protected:
 	void add_pragma_line(const std::string &line);
 	void add_pragma_line(const std::string &line);
 	void add_typedef_line(const std::string &line);
 	void add_typedef_line(const std::string &line);
 	void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
 	void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
-	void emit_array_copy(const std::string &lhs, uint32_t rhs_id, spv::StorageClass lhs_storage,
-	                     spv::StorageClass rhs_storage) override;
+	void emit_array_copy(const std::string &lhs, uint32_t lhs_id, uint32_t rhs_id,
+	                     spv::StorageClass lhs_storage, spv::StorageClass rhs_storage) override;
 	void build_implicit_builtins();
 	void build_implicit_builtins();
 	uint32_t build_constant_uint_array_pointer();
 	uint32_t build_constant_uint_array_pointer();
 	void emit_entry_point_declarations() override;
 	void emit_entry_point_declarations() override;
+	bool uses_explicit_early_fragment_test();
+
 	uint32_t builtin_frag_coord_id = 0;
 	uint32_t builtin_frag_coord_id = 0;
 	uint32_t builtin_sample_id_id = 0;
 	uint32_t builtin_sample_id_id = 0;
 	uint32_t builtin_sample_mask_id = 0;
 	uint32_t builtin_sample_mask_id = 0;
@@ -913,15 +956,19 @@ protected:
 	uint32_t view_mask_buffer_id = 0;
 	uint32_t view_mask_buffer_id = 0;
 	uint32_t dynamic_offsets_buffer_id = 0;
 	uint32_t dynamic_offsets_buffer_id = 0;
 	uint32_t uint_type_id = 0;
 	uint32_t uint_type_id = 0;
+	uint32_t argument_buffer_padding_buffer_type_id = 0;
+	uint32_t argument_buffer_padding_image_type_id = 0;
+	uint32_t argument_buffer_padding_sampler_type_id = 0;
 
 
 	bool does_shader_write_sample_mask = false;
 	bool does_shader_write_sample_mask = false;
 
 
-	void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override;
-	void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override;
+	void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override;
+	void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override;
 	void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override;
 	void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override;
 
 
 	void analyze_sampled_image_usage();
 	void analyze_sampled_image_usage();
 
 
+	bool access_chain_needs_stage_io_builtin_translation(uint32_t base) override;
 	void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, spv::StorageClass storage,
 	void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, spv::StorageClass storage,
 	                                            bool &is_packed) override;
 	                                            bool &is_packed) override;
 	void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length);
 	void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length);
@@ -938,9 +985,10 @@ protected:
 	Options msl_options;
 	Options msl_options;
 	std::set<SPVFuncImpl> spv_function_implementations;
 	std::set<SPVFuncImpl> spv_function_implementations;
 	// Must be ordered to ensure declarations are in a specific order.
 	// Must be ordered to ensure declarations are in a specific order.
-	std::map<uint32_t, MSLShaderInput> inputs_by_location;
+	std::map<LocationComponentPair, MSLShaderInput> inputs_by_location;
 	std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
 	std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
 	std::unordered_set<uint32_t> location_inputs_in_use;
 	std::unordered_set<uint32_t> location_inputs_in_use;
+	std::unordered_set<uint32_t> location_inputs_in_use_fallback;
 	std::unordered_map<uint32_t, uint32_t> fragment_output_components;
 	std::unordered_map<uint32_t, uint32_t> fragment_output_components;
 	std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_input_location;
 	std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_input_location;
 	std::set<std::string> pragma_lines;
 	std::set<std::string> pragma_lines;
@@ -948,7 +996,7 @@ protected:
 	SmallVector<uint32_t> vars_needing_early_declaration;
 	SmallVector<uint32_t> vars_needing_early_declaration;
 
 
 	std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
 	std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
-	uint32_t type_to_location_count(const SPIRType &type) const;
+	std::unordered_map<StageSetBinding, uint32_t, InternalHasher> resource_arg_buff_idx_to_binding_number;
 
 
 	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;
@@ -962,6 +1010,7 @@ protected:
 	VariableID patch_stage_out_var_id = 0;
 	VariableID patch_stage_out_var_id = 0;
 	VariableID stage_in_ptr_var_id = 0;
 	VariableID stage_in_ptr_var_id = 0;
 	VariableID stage_out_ptr_var_id = 0;
 	VariableID stage_out_ptr_var_id = 0;
+	VariableID stage_out_masked_builtin_type_id = 0;
 
 
 	// Handle HLSL-style 0-based vertex/instance index.
 	// Handle HLSL-style 0-based vertex/instance index.
 	enum class TriState
 	enum class TriState
@@ -1027,6 +1076,11 @@ protected:
 
 
 	void analyze_argument_buffers();
 	void analyze_argument_buffers();
 	bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
 	bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
+	MSLResourceBinding &get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx);
+	void add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
+	void add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
+	void add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
+	void add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, uint32_t count);
 
 
 	uint32_t get_target_components_for_fragment_location(uint32_t location) const;
 	uint32_t 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,
@@ -1044,6 +1098,11 @@ protected:
 	bool type_is_pointer_to_pointer(const SPIRType &type) const;
 	bool type_is_pointer_to_pointer(const SPIRType &type) const;
 	bool is_supported_argument_buffer_type(const SPIRType &type) const;
 	bool is_supported_argument_buffer_type(const SPIRType &type) const;
 
 
+	bool variable_storage_requires_stage_io(spv::StorageClass storage) const;
+
+	bool has_additional_fixed_sample_mask() const { return msl_options.additional_fixed_sample_mask != 0xffffffff; }
+	std::string additional_fixed_sample_mask_str() const;
+
 	// OpcodeHandler that handles several MSL preprocessing operations.
 	// OpcodeHandler that handles several MSL preprocessing operations.
 	struct OpCodePreprocessor : OpcodeHandler
 	struct OpCodePreprocessor : OpcodeHandler
 	{
 	{
@@ -1087,11 +1146,8 @@ protected:
 	{
 	{
 		enum SortAspect
 		enum SortAspect
 		{
 		{
-			Location,
-			LocationReverse,
-			Offset,
-			OffsetThenLocationReverse,
-			Alphabetical
+			LocationThenBuiltInType,
+			Offset
 		};
 		};
 
 
 		void sort();
 		void sort();

+ 67 - 3
src/libraries/spirv_cross/spirv_parser.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2018-2021 Arm Limited
  * Copyright 2018-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_parser.hpp"
 #include "spirv_parser.hpp"
@@ -961,6 +961,49 @@ void Parser::parse(const Instruction &instruction)
 		current_block->false_block = ops[2];
 		current_block->false_block = ops[2];
 
 
 		current_block->terminator = SPIRBlock::Select;
 		current_block->terminator = SPIRBlock::Select;
+
+		if (current_block->true_block == current_block->false_block)
+		{
+			// Bogus conditional, translate to a direct branch.
+			// Avoids some ugly edge cases later when analyzing CFGs.
+
+			// There are some super jank cases where the merge block is different from the true/false,
+			// and later branches can "break" out of the selection construct this way.
+			// This is complete nonsense, but CTS hits this case.
+			// In this scenario, we should see the selection construct as more of a Switch with one default case.
+			// The problem here is that this breaks any attempt to break out of outer switch statements,
+			// but it's theoretically solvable if this ever comes up using the ladder breaking system ...
+
+			if (current_block->true_block != current_block->next_block &&
+			    current_block->merge == SPIRBlock::MergeSelection)
+			{
+				uint32_t ids = ir.increase_bound_by(2);
+
+				SPIRType type;
+				type.basetype = SPIRType::Int;
+				type.width = 32;
+				set<SPIRType>(ids, type);
+				auto &c = set<SPIRConstant>(ids + 1, ids);
+
+				current_block->condition = c.self;
+				current_block->default_block = current_block->true_block;
+				current_block->terminator = SPIRBlock::MultiSelect;
+				ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT;
+				ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
+			}
+			else
+			{
+				ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT;
+				current_block->next_block = current_block->true_block;
+				current_block->condition = 0;
+				current_block->true_block = 0;
+				current_block->false_block = 0;
+				current_block->merge_block = 0;
+				current_block->merge = SPIRBlock::MergeNone;
+				current_block->terminator = SPIRBlock::Direct;
+			}
+		}
+
 		current_block = nullptr;
 		current_block = nullptr;
 		break;
 		break;
 	}
 	}
@@ -975,8 +1018,21 @@ void Parser::parse(const Instruction &instruction)
 		current_block->condition = ops[0];
 		current_block->condition = ops[0];
 		current_block->default_block = ops[1];
 		current_block->default_block = ops[1];
 
 
-		for (uint32_t i = 2; i + 2 <= length; i += 2)
-			current_block->cases.push_back({ ops[i], ops[i + 1] });
+		uint32_t remaining_ops = length - 2;
+		if ((remaining_ops % 2) == 0)
+		{
+			for (uint32_t i = 2; i + 2 <= length; i += 2)
+				current_block->cases_32bit.push_back({ ops[i], ops[i + 1] });
+		}
+
+		if ((remaining_ops % 3) == 0)
+		{
+			for (uint32_t i = 2; i + 3 <= length; i += 3)
+			{
+				uint64_t value = (static_cast<uint64_t>(ops[i + 1]) << 32) | ops[i];
+				current_block->cases_64bit.push_back({ value, ops[i + 2] });
+			}
+		}
 
 
 		// If we jump to next block, make it break instead since we're inside a switch case block at that point.
 		// If we jump to next block, make it break instead since we're inside a switch case block at that point.
 		ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
 		ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
@@ -1134,6 +1190,14 @@ void Parser::parse(const Instruction &instruction)
 	// Actual opcodes.
 	// Actual opcodes.
 	default:
 	default:
 	{
 	{
+		if (length >= 2)
+		{
+			const auto *type = maybe_get<SPIRType>(ops[0]);
+			if (type)
+			{
+				ir.load_type_width.insert({ ops[1], type->width });
+			}
+		}
 		if (!current_block)
 		if (!current_block)
 			SPIRV_CROSS_THROW("Currently no block to insert opcode.");
 			SPIRV_CROSS_THROW("Currently no block to insert opcode.");
 
 

+ 1 - 1
src/libraries/spirv_cross/spirv_parser.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2018-2021 Arm Limited
  * Copyright 2018-2021 Arm Limited
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_PARSER_HPP
 #ifndef SPIRV_CROSS_PARSER_HPP

+ 1 - 1
src/libraries/spirv_cross/spirv_reflect.cpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2018-2021 Bradley Austin Davis
  * Copyright 2018-2021 Bradley Austin Davis
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #include "spirv_reflect.hpp"
 #include "spirv_reflect.hpp"

+ 1 - 1
src/libraries/spirv_cross/spirv_reflect.hpp

@@ -1,5 +1,6 @@
 /*
 /*
  * Copyright 2018-2021 Bradley Austin Davis
  * Copyright 2018-2021 Bradley Austin Davis
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * you may not use this file except in compliance with the License.
@@ -18,7 +19,6 @@
  * At your option, you may choose to accept this material under either:
  * At your option, you may choose to accept this material under either:
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
  *  2. The MIT License, found at <http://opensource.org/licenses/MIT>.
- * SPDX-License-Identifier: Apache-2.0 OR MIT.
  */
  */
 
 
 #ifndef SPIRV_CROSS_REFLECT_HPP
 #ifndef SPIRV_CROSS_REFLECT_HPP

Some files were not shown because too many files changed in this diff