Merge branch 'master' of https://github.com/billhollings/MoltenVK into fastmath
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 7ee10cc..48cf717 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -20,7 +20,8 @@
 
 - Advertise support for `shaderInt64` feature.
 - Support fast math on MSL compiler via `MVKConfiguration::fastMathEnabled` configuration 
-  setting and `MVK_CONFIG_FAST_MATH_ENABLED` environment variable (both disabled by default).
+  setting and `MVK_CONFIG_FAST_MATH_ENABLED` environment variable (enabled by default).
+- Support compiling MSL with position invariance if indicated in SPIRV shader.
 - `vkGetMoltenVKConfigurationMVK()` and `vkSetMoltenVKConfigurationMVK()` functions
   can now be used with a `VkInstance` from another Vulkan layer, or with a `VK_NULL_HANDLE VkInstance`.
 - `MVKConfiguration` extended to cover all MoltenVK environment variables.
diff --git a/ExternalRevisions/SPIRV-Cross_repo_revision b/ExternalRevisions/SPIRV-Cross_repo_revision
index e1ad29a..0e4d400 100644
--- a/ExternalRevisions/SPIRV-Cross_repo_revision
+++ b/ExternalRevisions/SPIRV-Cross_repo_revision
@@ -1 +1 @@
-9acb9ec31f5a8ef80ea6b994bb77be787b08d3d1
+4741bbaa641d118ca1864094decd1ba977316161
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 6e69f10..1dc0503 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -470,7 +470,7 @@
 	 * The initial value or this parameter is set by the
 	 * MVK_CONFIG_FAST_MATH_ENABLED
 	 * runtime environment variable or MoltenVK compile-time build setting.
-	 * If neither is set, the value of this parameter defaults to false.
+	 * If neither is set, the value of this parameter defaults to true.
 	 */
 	VkBool32 fastMathEnabled;
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index e45d810..dab1c46 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -648,8 +648,14 @@
 	/** Returns the underlying Metal device. */
 	inline id<MTLDevice> getMTLDevice() { return _physicalDevice->getMTLDevice(); }
 
-	/** Returns standard compilation options to be used when compiling MSL shaders. */
-	inline MTLCompileOptions* getMTLCompileOptions() { return _mtlCompileOptions; }
+	/**
+	 * Returns an autoreleased options object to be used when compiling MSL shaders.
+	 * The useFastMath parameter is and-combined with MVKConfiguration::fastMathEnabled
+	 * to determine whether to enable fast math optimizations in the compiled shader.
+	 * The preserveInvariance parameter indicates that the shader requires the position
+	 * output invariance across invocations (typically for the position output).
+	 */
+	MTLCompileOptions* getMTLCompileOptions(bool useFastMath = true, bool preserveInvariance = false);
 
 	/** Returns the Metal vertex buffer index to use for the specified vertex attribute binding number.  */
 	uint32_t getMetalBufferIndexForVertexAttributeBinding(uint32_t binding);
@@ -762,7 +768,6 @@
 	void initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo);
 	void initQueues(const VkDeviceCreateInfo* pCreateInfo);
 	void reservePrivateData(const VkDeviceCreateInfo* pCreateInfo);
-	void initMTLCompileOptions();
 	void enableFeatures(const VkDeviceCreateInfo* pCreateInfo);
 	void enableFeatures(const VkBool32* pEnable, const VkBool32* pRequested, const VkBool32* pAvailable, uint32_t count);
 	void enableExtensions(const VkDeviceCreateInfo* pCreateInfo);
@@ -775,7 +780,6 @@
 
 	MVKPhysicalDevice* _physicalDevice;
     MVKCommandResourceFactory* _commandResourceFactory;
-	MTLCompileOptions* _mtlCompileOptions;
 	MVKSmallVector<MVKSmallVector<MVKQueue*, kMVKQueueCountPerQueueFamily>, kMVKQueueFamilyCount> _queuesByQueueFamilyIndex;
 	MVKSmallVector<MVKResource*, 256> _resources;
 	MVKSmallVector<MVKPrivateDataSlot*> _privateDataSlots;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 001dee6..4d49f92 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -3651,6 +3651,16 @@
 	return _defaultMTLSamplerState;
 }
 
+MTLCompileOptions* MVKDevice::getMTLCompileOptions(bool useFastMath, bool preserveInvariance) {
+	MTLCompileOptions* mtlCompOpt = [MTLCompileOptions new];
+	mtlCompOpt.languageVersion = _pMetalFeatures->mslVersionEnum;
+	mtlCompOpt.fastMathEnabled = useFastMath && mvkGetMVKConfiguration()->fastMathEnabled;
+	if ([mtlCompOpt respondsToSelector: @selector(setPreserveInvariance:)]) {
+		[mtlCompOpt setPreserveInvariance: preserveInvariance];
+	}
+	return [mtlCompOpt autorelease];
+}
+
 // Can't use prefilled Metal command buffers if any of the resource descriptors can be updated after binding.
 bool MVKDevice::shouldPrefillMTLCommandBuffers() {
 	return (mvkGetMVKConfiguration()->prefillMetalCommandBuffers &&
@@ -3703,8 +3713,6 @@
 
 	_defaultMTLSamplerState = nil;
 
-	initMTLCompileOptions();	// Before command resource factory
-
 	_commandResourceFactory = new MVKCommandResourceFactory(this);
 
 // This code will be refactored in an upcoming release, but for now,
@@ -4085,19 +4093,12 @@
 	}
 }
 
-void MVKDevice::initMTLCompileOptions() {
-	_mtlCompileOptions = [MTLCompileOptions new];	// retained
-	_mtlCompileOptions.languageVersion = _pMetalFeatures->mslVersionEnum;
-	_mtlCompileOptions.fastMathEnabled = mvkGetMVKConfiguration()->fastMathEnabled;
-}
-
 MVKDevice::~MVKDevice() {
 	for (auto& queues : _queuesByQueueFamilyIndex) {
 		mvkDestroyContainerContents(queues);
 	}
 	_commandResourceFactory->destroy();
 
-	[_mtlCompileOptions release];
     [_globalVisibilityResultMTLBuffer release];
 	[_defaultMTLSamplerState release];
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 027803c..932b26b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -2074,7 +2074,8 @@
 		archive(ep.mtlFunctionName,
 				ep.workgroupSize.width,
 				ep.workgroupSize.height,
-				ep.workgroupSize.depth);
+				ep.workgroupSize.depth,
+				ep.supportsFastMath);
 	}
 
 	template<class Archive>
@@ -2113,6 +2114,7 @@
 	void serialize(Archive & archive, SPIRVToMSLConversionResults& scr) {
 		archive(scr.entryPoint,
 				scr.isRasterizationDisabled,
+				scr.isPositionInvariant,
 				scr.needsSwizzleBuffer,
 				scr.needsOutputBuffer,
 				scr.needsPatchOutputBuffer,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
index 49b0ce2..7b45d55 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
@@ -254,7 +254,8 @@
 	 * If the Metal library compiler does not return within MVKConfiguration::metalCompileTimeout
 	 * nanoseconds, an error will be generated and logged, and nil will be returned.
 	 */
-	id<MTLLibrary> newMTLLibrary(NSString* mslSourceCode);
+	id<MTLLibrary> newMTLLibrary(NSString* mslSourceCode,
+								 const SPIRVToMSLConversionResults& shaderConversionResults);
 
 
 #pragma mark Construction
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index e88bfdd..b30d924 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -135,8 +135,8 @@
 								   const SPIRVToMSLConversionResults& shaderConversionResults) : _owner(owner) {
 	MVKShaderLibraryCompiler* slc = new MVKShaderLibraryCompiler(_owner);
 
-	NSString* nsSrc = [[NSString alloc] initWithUTF8String: mslSourceCode.c_str()];	// temp retained
-	_mtlLibrary = slc->newMTLLibrary(nsSrc);	// retained
+	NSString* nsSrc = [[NSString alloc] initWithUTF8String: mslSourceCode.c_str()];					// temp retained
+	_mtlLibrary = slc->newMTLLibrary(nsSrc, shaderConversionResults);	// retained
 	[nsSrc release];	// release temp string
 
 	slc->destroy();
@@ -415,12 +415,14 @@
 #pragma mark -
 #pragma mark MVKShaderLibraryCompiler
 
-id<MTLLibrary> MVKShaderLibraryCompiler::newMTLLibrary(NSString* mslSourceCode) {
+id<MTLLibrary> MVKShaderLibraryCompiler::newMTLLibrary(NSString* mslSourceCode,
+													   const SPIRVToMSLConversionResults& shaderConversionResults) {
 	unique_lock<mutex> lock(_completionLock);
 
 	compile(lock, ^{
 		[_owner->getMTLDevice() newLibraryWithSource: mslSourceCode
-											 options: _owner->getDevice()->getMTLCompileOptions()
+											 options: _owner->getDevice()->getMTLCompileOptions(shaderConversionResults.entryPoint.supportsFastMath,
+																								shaderConversionResults.isPositionInvariant)
 								   completionHandler: ^(id<MTLLibrary> mtlLib, NSError* error) {
 									   bool isLate = compileComplete(mtlLib, error);
 									   if (isLate) { destroy(); }
diff --git a/MoltenVK/MoltenVK/Utility/MVKEnvironment.h b/MoltenVK/MoltenVK/Utility/MVKEnvironment.h
index 5eb6efe..1818903 100644
--- a/MoltenVK/MoltenVK/Utility/MVKEnvironment.h
+++ b/MoltenVK/MoltenVK/Utility/MVKEnvironment.h
@@ -173,9 +173,9 @@
 #   define MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE    0
 #endif
 
-/** Set the fastMathEnabled Metal Compiler option. Disabled by default. */
+/** Set the fastMathEnabled Metal Compiler option. Enabled by default. */
 #ifndef MVK_CONFIG_FAST_MATH_ENABLED
-#   define MVK_CONFIG_FAST_MATH_ENABLED 0
+#   define MVK_CONFIG_FAST_MATH_ENABLED 1
 #endif
 
 /**
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.cpp
index f57d623..996374a 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.cpp
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.cpp
@@ -25,6 +25,7 @@
 
 using namespace mvk;
 using namespace std;
+using namespace spv;
 using namespace SPIRV_CROSS_NAMESPACE;
 
 
@@ -144,9 +145,9 @@
 }
 
 MVK_PUBLIC_SYMBOL bool SPIRVToMSLConversionConfiguration::stageSupportsVertexAttributes() const {
-	return (options.entryPointStage == spv::ExecutionModelVertex ||
-			options.entryPointStage == spv::ExecutionModelTessellationControl ||
-			options.entryPointStage == spv::ExecutionModelTessellationEvaluation);
+	return (options.entryPointStage == ExecutionModelVertex ||
+			options.entryPointStage == ExecutionModelTessellationControl ||
+			options.entryPointStage == ExecutionModelTessellationEvaluation);
 }
 
 // Check them all in case inactive VA's duplicate locations used by active VA's.
@@ -232,25 +233,25 @@
 
 	if (shouldLogSPIRV) { logSPIRV("Converting"); }
 
-	SPIRV_CROSS_NAMESPACE::CompilerMSL* pMSLCompiler = nullptr;
+	CompilerMSL* pMSLCompiler = nullptr;
 
 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
 	try {
 #endif
-		pMSLCompiler = new SPIRV_CROSS_NAMESPACE::CompilerMSL(_spirv);
+		pMSLCompiler = new CompilerMSL(_spirv);
 
 		if (context.options.hasEntryPoint()) {
 			pMSLCompiler->set_entry_point(context.options.entryPointName, context.options.entryPointStage);
 		}
 
 		// Set up tessellation parameters if needed.
-		if (context.options.entryPointStage == spv::ExecutionModelTessellationControl ||
-			context.options.entryPointStage == spv::ExecutionModelTessellationEvaluation) {
-			if (context.options.tessPatchKind != spv::ExecutionModeMax) {
+		if (context.options.entryPointStage == ExecutionModelTessellationControl ||
+			context.options.entryPointStage == ExecutionModelTessellationEvaluation) {
+			if (context.options.tessPatchKind != ExecutionModeMax) {
 				pMSLCompiler->set_execution_mode(context.options.tessPatchKind);
 			}
 			if (context.options.numTessControlPoints != 0) {
-				pMSLCompiler->set_execution_mode(spv::ExecutionModeOutputVertices, context.options.numTessControlPoints);
+				pMSLCompiler->set_execution_mode(ExecutionModeOutputVertices, context.options.numTessControlPoints);
 			}
 		}
 
@@ -282,7 +283,7 @@
         if (shouldLogMSL) { logSource(_msl, "MSL", "Converted"); }
 
 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
-	} catch (SPIRV_CROSS_NAMESPACE::CompilerError& ex) {
+	} catch (CompilerError& ex) {
 		string errMsg("MSL conversion error: ");
 		errMsg += ex.what();
 		logError(errMsg.data());
@@ -297,6 +298,7 @@
 	// and mark which vertex attributes and resource bindings are used by the shader
 	populateEntryPoint(pMSLCompiler, context.options);
 	_shaderConversionResults.isRasterizationDisabled = pMSLCompiler && pMSLCompiler->get_is_rasterization_disabled();
+	_shaderConversionResults.isPositionInvariant = pMSLCompiler && pMSLCompiler->is_position_invariant();
 	_shaderConversionResults.needsSwizzleBuffer = pMSLCompiler && pMSLCompiler->needs_swizzle_buffer();
 	_shaderConversionResults.needsOutputBuffer = pMSLCompiler && pMSLCompiler->needs_output_buffer();
 	_shaderConversionResults.needsPatchOutputBuffer = pMSLCompiler && pMSLCompiler->needs_patch_output_buffer();
@@ -318,12 +320,12 @@
 
     // To check GLSL conversion
     if (shouldLogGLSL) {
-		SPIRV_CROSS_NAMESPACE::CompilerGLSL* pGLSLCompiler = nullptr;
+		CompilerGLSL* pGLSLCompiler = nullptr;
 
 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
 		try {
 #endif
-			pGLSLCompiler = new SPIRV_CROSS_NAMESPACE::CompilerGLSL(_spirv);
+			pGLSLCompiler = new CompilerGLSL(_spirv);
 			auto options = pGLSLCompiler->get_common_options();
 			options.vulkan_semantics = true;
 			options.separate_shader_objects = true;
@@ -331,7 +333,7 @@
 			string glsl = pGLSLCompiler->compile();
             logSource(glsl, "GLSL", "Estimated original");
 #ifndef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
-        } catch (SPIRV_CROSS_NAMESPACE::CompilerError& ex) {
+        } catch (CompilerError& ex) {
             string errMsg("Original GLSL extraction error: ");
             errMsg += ex.what();
             logMsg(errMsg.data());
@@ -397,7 +399,7 @@
 // Validates that the SPIR-V code will disassemble during logging.
 bool SPIRVToMSLConverter::validateSPIRV() {
 	if (_spirv.size() < 5) { return false; }
-	if (_spirv[0] != spv::MagicNumber) { return false; }
+	if (_spirv[0] != MagicNumber) { return false; }
 	if (_spirv[4] != 0) { return false; }
 	return true;
 }
@@ -416,19 +418,19 @@
 
 void SPIRVToMSLConverter::populateWorkgroupDimension(SPIRVWorkgroupSizeDimension& wgDim,
 													 uint32_t size,
-													 SPIRV_CROSS_NAMESPACE::SpecializationConstant& spvSpecConst) {
+													 SpecializationConstant& spvSpecConst) {
 	wgDim.size = max(size, 1u);
 	wgDim.isSpecialized = (uint32_t(spvSpecConst.id) != 0);
 	wgDim.specializationID = spvSpecConst.constant_id;
 }
 
 // Populates the entry point with info extracted from the SPRI-V compiler.
-void SPIRVToMSLConverter::populateEntryPoint(SPIRV_CROSS_NAMESPACE::Compiler* pCompiler,
+void SPIRVToMSLConverter::populateEntryPoint(Compiler* pCompiler,
 											 SPIRVToMSLConversionOptions& options) {
 
 	if ( !pCompiler ) { return; }
 
-	SPIRV_CROSS_NAMESPACE::SPIREntryPoint spvEP;
+	SPIREntryPoint spvEP;
 	if (options.hasEntryPoint()) {
 		spvEP = pCompiler->get_entry_point(options.entryPointName, options.entryPointStage);
 	} else {
@@ -441,8 +443,9 @@
 
 	auto& ep = _shaderConversionResults.entryPoint;
 	ep.mtlFunctionName = spvEP.name;
+	ep.supportsFastMath = !spvEP.flags.get(ExecutionModeSignedZeroInfNanPreserve);
 
-	SPIRV_CROSS_NAMESPACE::SpecializationConstant widthSC, heightSC, depthSC;
+	SpecializationConstant widthSC, heightSC, depthSC;
 	pCompiler->get_work_group_size_specialization_constants(widthSC, heightSC, depthSC);
 
 	auto& wgSize = ep.workgroupSize;
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.h b/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.h
index c35face..d1d127b 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.h
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter/SPIRVToMSLConverter.h
@@ -192,6 +192,7 @@
 			SPIRVWorkgroupSizeDimension height;
 			SPIRVWorkgroupSizeDimension depth;
 		} workgroupSize;
+		bool supportsFastMath = true;
 	} SPIRVEntryPoint;
 
 	/**
@@ -203,6 +204,7 @@
 	typedef struct SPIRVToMSLConversionResults {
 		SPIRVEntryPoint entryPoint;
 		bool isRasterizationDisabled = false;
+		bool isPositionInvariant = false;
 		bool needsSwizzleBuffer = false;
 		bool needsOutputBuffer = false;
 		bool needsPatchOutputBuffer = false;