Merge pull request #598 from mmaldacker/bugix/texture_coherent

texture don't have coherent memory on mac os
diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj
index 950535e..027b362 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj
+++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj
@@ -9,12 +9,6 @@
 /* Begin PBXBuildFile section */
 		A99789AF1CD3D4E2005E7DAC /* Hologram.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789831CD3D4E2005E7DAC /* Hologram.cpp */; };
 		A99789B01CD3D4E2005E7DAC /* Hologram.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789831CD3D4E2005E7DAC /* Hologram.cpp */; };
-		A99789B11CD3D4E2005E7DAC /* Hologram.frag in Resources */ = {isa = PBXBuildFile; fileRef = A99789841CD3D4E2005E7DAC /* Hologram.frag */; };
-		A99789B21CD3D4E2005E7DAC /* Hologram.frag in Resources */ = {isa = PBXBuildFile; fileRef = A99789841CD3D4E2005E7DAC /* Hologram.frag */; };
-		A99789B31CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */; };
-		A99789B41CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */; };
-		A99789B51CD3D4E2005E7DAC /* Hologram.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789871CD3D4E2005E7DAC /* Hologram.vert */; };
-		A99789B61CD3D4E2005E7DAC /* Hologram.vert in Resources */ = {isa = PBXBuildFile; fileRef = A99789871CD3D4E2005E7DAC /* Hologram.vert */; };
 		A99789B71CD3D4E2005E7DAC /* Main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789881CD3D4E2005E7DAC /* Main.cpp */; };
 		A99789B81CD3D4E2005E7DAC /* Main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789881CD3D4E2005E7DAC /* Main.cpp */; };
 		A99789B91CD3D4E2005E7DAC /* Meshes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A99789891CD3D4E2005E7DAC /* Meshes.cpp */; };
@@ -38,8 +32,6 @@
 		A9B67B901C3AAEA200373FFD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8A1C3AAEA200373FFD /* Main.storyboard */; };
 		A9B67B911C3AAEA200373FFD /* macOS.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */; };
 		A9C2AB99218503A400DDBC03 /* libMoltenVK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A9C2AB97218503A400DDBC03 /* libMoltenVK.a */; };
-		A9C2AB9A218503A400DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9C2AB98218503A400DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework */; };
-		A9C2AB9D218503BA00DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9C2AB9B218503BA00DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework */; };
 		A9C2AB9E218503BA00DDBC03 /* libMoltenVK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A9C2AB9C218503BA00DDBC03 /* libMoltenVK.a */; };
 		A9D516F81CD575E300097D96 /* HelpersDispatchTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D516F61CD575E300097D96 /* HelpersDispatchTable.cpp */; };
 		A9D516F91CD575E300097D96 /* HelpersDispatchTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A9D516F61CD575E300097D96 /* HelpersDispatchTable.cpp */; };
@@ -47,16 +39,16 @@
 
 /* Begin PBXFileReference section */
 		1D6058910D05DD3D006BFB54 /* Hologram.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hologram.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		A93374642284CD260059D36E /* Hologram.vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hologram.vert.h; path = android/src/main/jni/Hologram.vert.h; sourceTree = "<group>"; };
+		A93374652284CD260059D36E /* Hologram.push_constant.vert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hologram.push_constant.vert.h; path = android/src/main/jni/Hologram.push_constant.vert.h; sourceTree = "<group>"; };
+		A93374662284CD260059D36E /* Hologram.frag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hologram.frag.h; path = android/src/main/jni/Hologram.frag.h; sourceTree = "<group>"; };
 		A93CC3701CD56B8F00EB8A56 /* generate-dispatch-table */ = {isa = PBXFileReference; explicitFileType = text.script.python; path = "generate-dispatch-table"; sourceTree = "<group>"; };
 		A93CC3711CD56FD600EB8A56 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; };
 		A977BCFE1B66BB010067E5BF /* Hologram.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hologram.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		A997897F1CD3D4E2005E7DAC /* Game.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Game.h; sourceTree = "<group>"; };
 		A99789821CD3D4E2005E7DAC /* Helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Helpers.h; sourceTree = "<group>"; };
 		A99789831CD3D4E2005E7DAC /* Hologram.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Hologram.cpp; sourceTree = "<group>"; };
-		A99789841CD3D4E2005E7DAC /* Hologram.frag */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = Hologram.frag; sourceTree = "<group>"; };
 		A99789851CD3D4E2005E7DAC /* Hologram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hologram.h; sourceTree = "<group>"; };
-		A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = Hologram.push_constant.vert; sourceTree = "<group>"; };
-		A99789871CD3D4E2005E7DAC /* Hologram.vert */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; path = Hologram.vert; sourceTree = "<group>"; };
 		A99789881CD3D4E2005E7DAC /* Main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Main.cpp; sourceTree = "<group>"; };
 		A99789891CD3D4E2005E7DAC /* Meshes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Meshes.cpp; sourceTree = "<group>"; };
 		A997898A1CD3D4E2005E7DAC /* Meshes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Meshes.h; sourceTree = "<group>"; };
@@ -88,8 +80,6 @@
 		A9B67B8A1C3AAEA200373FFD /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
 		A9B67B8B1C3AAEA200373FFD /* macOS.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = macOS.xcassets; sourceTree = "<group>"; };
 		A9C2AB97218503A400DDBC03 /* libMoltenVK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMoltenVK.a; path = ../../../MoltenVK/iOS/static/libMoltenVK.a; sourceTree = "<group>"; };
-		A9C2AB98218503A400DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS/framework/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = "<group>"; };
-		A9C2AB9B218503BA00DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MoltenVKGLSLToSPIRVConverter.framework; path = ../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS/framework/MoltenVKGLSLToSPIRVConverter.framework; sourceTree = "<group>"; };
 		A9C2AB9C218503BA00DDBC03 /* libMoltenVK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libMoltenVK.a; path = ../../../MoltenVK/macOS/static/libMoltenVK.a; sourceTree = "<group>"; };
 		A9D516F61CD575E300097D96 /* HelpersDispatchTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HelpersDispatchTable.cpp; sourceTree = "<group>"; };
 		A9D516F71CD575E300097D96 /* HelpersDispatchTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HelpersDispatchTable.h; sourceTree = "<group>"; };
@@ -101,7 +91,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				A9C2AB99218503A400DDBC03 /* libMoltenVK.a in Frameworks */,
-				A9C2AB9A218503A400DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -110,7 +99,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				A9C2AB9E218503BA00DDBC03 /* libMoltenVK.a in Frameworks */,
-				A9C2AB9D218503BA00DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -147,10 +135,10 @@
 				A997897F1CD3D4E2005E7DAC /* Game.h */,
 				A99789821CD3D4E2005E7DAC /* Helpers.h */,
 				A99789831CD3D4E2005E7DAC /* Hologram.cpp */,
-				A99789841CD3D4E2005E7DAC /* Hologram.frag */,
 				A99789851CD3D4E2005E7DAC /* Hologram.h */,
-				A99789861CD3D4E2005E7DAC /* Hologram.push_constant.vert */,
-				A99789871CD3D4E2005E7DAC /* Hologram.vert */,
+				A93374662284CD260059D36E /* Hologram.frag.h */,
+				A93374652284CD260059D36E /* Hologram.push_constant.vert.h */,
+				A93374642284CD260059D36E /* Hologram.vert.h */,
 				A99789881CD3D4E2005E7DAC /* Main.cpp */,
 				A99789891CD3D4E2005E7DAC /* Meshes.cpp */,
 				A997898A1CD3D4E2005E7DAC /* Meshes.h */,
@@ -220,9 +208,7 @@
 			isa = PBXGroup;
 			children = (
 				A9C2AB9C218503BA00DDBC03 /* libMoltenVK.a */,
-				A9C2AB9B218503BA00DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework */,
 				A9C2AB97218503A400DDBC03 /* libMoltenVK.a */,
-				A9C2AB98218503A400DDBC03 /* MoltenVKGLSLToSPIRVConverter.framework */,
 			);
 			name = Frameworks;
 			sourceTree = "<group>";
@@ -312,12 +298,9 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				A99789B51CD3D4E2005E7DAC /* Hologram.vert in Resources */,
 				A9B67B7F1C3AAE9800373FFD /* Icon.png in Resources */,
-				A99789B31CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */,
 				A9B67B801C3AAE9800373FFD /* Main.storyboard in Resources */,
 				A9B67B7E1C3AAE9800373FFD /* Default~ipad.png in Resources */,
-				A99789B11CD3D4E2005E7DAC /* Hologram.frag in Resources */,
 				A9B67B7D1C3AAE9800373FFD /* Default-568h@2x.png in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -327,10 +310,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				A9B67B911C3AAEA200373FFD /* macOS.xcassets in Resources */,
-				A99789B21CD3D4E2005E7DAC /* Hologram.frag in Resources */,
 				A9B67B901C3AAEA200373FFD /* Main.storyboard in Resources */,
-				A99789B41CD3D4E2005E7DAC /* Hologram.push_constant.vert in Resources */,
-				A99789B61CD3D4E2005E7DAC /* Hologram.vert in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -379,9 +359,7 @@
 			buildSettings = {
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				DEVELOPMENT_TEAM = "";
-				FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS/framework\"";
 				GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch";
-				GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
 				INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS/static\"";
@@ -396,9 +374,7 @@
 			buildSettings = {
 				CODE_SIGN_IDENTITY = "iPhone Developer";
 				DEVELOPMENT_TEAM = "";
-				FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/iOS/framework\"";
 				GCC_PREFIX_HEADER = "$(SRCROOT)/iOS/Prefix.pch";
-				GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
 				INFOPLIST_FILE = "$(SRCROOT)/iOS/Info.plist";
 				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/iOS/static\"";
@@ -413,9 +389,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				COMBINE_HIDPI_IMAGES = YES;
-				FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS/framework\"";
 				GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch";
-				GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
 				INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist";
 				LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/macOS/static\"";
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
@@ -432,9 +406,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				COMBINE_HIDPI_IMAGES = YES;
-				FRAMEWORK_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/macOS/framework\"";
 				GCC_PREFIX_HEADER = "$(SRCROOT)/macOS/Prefix.pch";
-				GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
 				INFOPLIST_FILE = "$(SRCROOT)/macOS/Info.plist";
 				LIBRARY_SEARCH_PATHS = "\"$(SRCROOT)/../../../MoltenVK/macOS/static\"";
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
@@ -463,7 +435,6 @@
 					"DEBUG=1",
 					_DEBUG,
 					GLM_FORCE_RADIANS,
-					MVK_USE_MOLTENVK_SHADER_CONVERTER,
 				);
 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 				GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
@@ -471,7 +442,6 @@
 				HEADER_SEARCH_PATHS = (
 					"\"$(SRCROOT)/../../../MoltenVK/include\"",
 					"\"$(SRCROOT)/../VulkanSamples/API-Samples/utils\"",
-					"\"$(SRCROOT)/../../../MoltenVKShaderConverter/include\"",
 				);
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "-ObjC";
@@ -490,17 +460,13 @@
 				ENABLE_BITCODE = NO;
 				GCC_C_LANGUAGE_STANDARD = c99;
 				GCC_PRECOMPILE_PREFIX_HEADER = YES;
-				GCC_PREPROCESSOR_DEFINITIONS = (
-					GLM_FORCE_RADIANS,
-					MVK_USE_MOLTENVK_SHADER_CONVERTER,
-				);
+				GCC_PREPROCESSOR_DEFINITIONS = GLM_FORCE_RADIANS;
 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 				GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
 				HEADER_SEARCH_PATHS = (
 					"\"$(SRCROOT)/../../../MoltenVK/include\"",
 					"\"$(SRCROOT)/../VulkanSamples/API-Samples/utils\"",
-					"\"$(SRCROOT)/../../../MoltenVKShaderConverter/include\"",
 				);
 				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_BUNDLE_IDENTIFIER = "com.moltenvk.${PRODUCT_NAME:identifier}";
diff --git a/Demos/README.md b/Demos/README.md
index 6f1e820..e689634 100644
--- a/Demos/README.md
+++ b/Demos/README.md
@@ -109,12 +109,6 @@
 To customize, modify the arguments created in the `DemoViewController viewDidLoad` method
 found in the `iOS/DemoViewController.mm` or `macOS/DemoViewController.mm` file.
 
-This demo illustrates the use of the **MoltenVK** API `vkGetMoltenVKDeviceConfigurationMVK()` 
-and `vkSetMoltenVKDeviceConfigurationMVK()` functions to enable **MoltenVK** debugging, including
-logging the conversion of shaders from *SPIR-V* to *Metal Shading Language*. See the use of these
-functions in the `Hologram/Hologram.cpp` file. To see the effect, modify the `Hologram-iOS` or 
-`Hologram-macOS` *Scheme* from within *Xcode* to use the **Debug** *Build Configuration* setting.
-
 The `Hologram` demo is a simple example of installing **MoltenVK** as a *static library*.
 
 
diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md
index 24274e2..8310e03 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -260,6 +260,7 @@
 - `VK_AMD_gpu_shader_half_float`
 - `VK_AMD_negative_viewport_height`
 - `VK_IMG_format_pvrtc` (iOS)
+- `VK_NV_glsl_shader`
 
 In order to visibly display your content on *iOS* or *macOS*, you must enable the `VK_MVK_ios_surface` 
 or `VK_MVK_macos_surface` extension, respectively, and use the functions defined for that extension
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index b510ad0..9bc03ed 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -19,6 +19,7 @@
 Released TBD
 
 - Support the `VK_EXT_debug_report` extension.
+- Support the `VK_NV_glsl_shader` extension.
 - Change log indication of error in logs from `[***MoltenVK ERROR***]` to 
   `[mvk-error]`, for consistency with other log level indications. 
 - Tessellation fixes:
diff --git a/ExternalRevisions/VulkanSamples_repo_revision b/ExternalRevisions/VulkanSamples_repo_revision
index 2ea8821..8c62934 100644
--- a/ExternalRevisions/VulkanSamples_repo_revision
+++ b/ExternalRevisions/VulkanSamples_repo_revision
@@ -1 +1 @@
-07b1ccd82ff6b7e6369a0d8d6a5e5e4f7ba0b7d6
+37e808988e758ec4f6f689012fb425bab1398ecf
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index b41d81b..74f1b38 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -222,6 +222,20 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
+		A96B8156227BF6FD008A772B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = A9C86CB61C55B8350096CAF2 /* MoltenVKShaderConverter.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = A937472B1A9A8B2900F29B34;
+			remoteInfo = "MoltenVKGLSLToSPIRVConverter-iOS";
+		};
+		A96B8158227BF715008A772B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = A9C86CB61C55B8350096CAF2 /* MoltenVKShaderConverter.xcodeproj */;
+			proxyType = 1;
+			remoteGlobalIDString = A93747701A9A98D000F29B34;
+			remoteInfo = "MoltenVKGLSLToSPIRVConverter-macOS";
+		};
 		A981497B1FB6B566005F00B4 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = A9C86CB61C55B8350096CAF2 /* MoltenVKShaderConverter.xcodeproj */;
@@ -763,6 +777,7 @@
 			);
 			dependencies = (
 				A981499B1FB6B9CF005F00B4 /* PBXTargetDependency */,
+				A96B8157227BF6FD008A772B /* PBXTargetDependency */,
 			);
 			name = "MoltenVK-iOS";
 			productName = MoltenVK;
@@ -782,6 +797,7 @@
 			);
 			dependencies = (
 				A98149A41FB6B9EB005F00B4 /* PBXTargetDependency */,
+				A96B8159227BF715008A772B /* PBXTargetDependency */,
 			);
 			name = "MoltenVK-macOS";
 			productName = MoltenVK;
@@ -1048,6 +1064,16 @@
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXTargetDependency section */
+		A96B8157227BF6FD008A772B /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = "MoltenVKGLSLToSPIRVConverter-iOS";
+			targetProxy = A96B8156227BF6FD008A772B /* PBXContainerItemProxy */;
+		};
+		A96B8159227BF715008A772B /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			name = "MoltenVKGLSLToSPIRVConverter-macOS";
+			targetProxy = A96B8158227BF715008A772B /* PBXContainerItemProxy */;
+		};
 		A981499B1FB6B9CF005F00B4 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			name = "MVKSPIRVToMSLConverter-iOS";
@@ -1157,7 +1183,7 @@
 				MACH_O_TYPE = staticlib;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
-				PRELINK_LIBS = "${CONFIGURATION_BUILD_DIR}/libMoltenVKSPIRVToMSLConverter.a";
+				PRELINK_LIBS = "${CONFIGURATION_BUILD_DIR}/libMoltenVKSPIRVToMSLConverter.a ${CONFIGURATION_BUILD_DIR}/libMoltenVKGLSLToSPIRVConverter.a";
 				PRODUCT_NAME = MoltenVK;
 				SKIP_INSTALL = YES;
 			};
@@ -1214,7 +1240,7 @@
 				);
 				MACH_O_TYPE = staticlib;
 				MTL_ENABLE_DEBUG_INFO = NO;
-				PRELINK_LIBS = "${CONFIGURATION_BUILD_DIR}/libMoltenVKSPIRVToMSLConverter.a";
+				PRELINK_LIBS = "${CONFIGURATION_BUILD_DIR}/libMoltenVKSPIRVToMSLConverter.a ${CONFIGURATION_BUILD_DIR}/libMoltenVKGLSLToSPIRVConverter.a";
 				PRODUCT_NAME = MoltenVK;
 				SKIP_INSTALL = YES;
 				VALIDATE_PRODUCT = YES;
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 381fd4e..d695a7a 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -566,6 +566,7 @@
     MVKPerformanceTracker functionRetrieval;			/** Retrieve a MTLFunction from a MTLLibrary. */
     MVKPerformanceTracker functionSpecialization;		/** Specialize a retrieved MTLFunction. */
     MVKPerformanceTracker pipelineCompile;				/** Compile MTLFunctions into a pipeline. */
+	MVKPerformanceTracker glslToSPRIV;					/** Convert GLSL to SPIR-V code. */
 } MVKShaderCompilationPerformance;
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index aef11ea..129e6ee 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -317,6 +317,7 @@
 	std::vector<MVKQueueFamily*>& getQueueFamilies();
 	void initPipelineCacheUUID();
 	MTLFeatureSet getHighestMTLFeatureSet();
+	uint64_t getSpirvCrossRevision();
 	bool getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo);
 	void logGPUInfo();
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 58fd374..41358b9 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -1343,35 +1343,36 @@
 
 
 void MVKPhysicalDevice::initPipelineCacheUUID() {
-	size_t uuidSize = sizeof(_properties.pipelineCacheUUID);
 
 	// Clear the UUID
-	memset(&_properties.pipelineCacheUUID, 0, uuidSize);
+	memset(&_properties.pipelineCacheUUID, 0, sizeof(_properties.pipelineCacheUUID));
 
-	uint32_t uuidComponent;
-	size_t uuidComponentSize = sizeof(uint32_t);
+	size_t uuidComponentOffset = 0;
 
-	size_t uuidComponentOffset = uuidSize;
-
-	// Lower 4 bytes contains MoltenVK version
-	uuidComponent = MVK_VERSION;
-	uuidComponentOffset -= uuidComponentSize;
-	*(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(uuidComponent);
+	// First 4 bytes contains MoltenVK version
+	uint32_t mvkVersion = MVK_VERSION;
+	*(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(mvkVersion);
+	uuidComponentOffset += sizeof(mvkVersion);
 
 	// Next 4 bytes contains hightest Metal feature set supported by this device
-	uuidComponent = (uint32_t)getHighestMTLFeatureSet();
-	uuidComponentOffset -= uuidComponentSize;
-	*(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(uuidComponent);
+	uint32_t mtlFeatSet = (uint32_t)getHighestMTLFeatureSet();
+	*(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(mtlFeatSet);
+	uuidComponentOffset += sizeof(mtlFeatSet);
+
+	// Last 8 bytes contain the first part of the SPIRV-Cross Git revision
+	uint64_t spvxRev = getSpirvCrossRevision();
+	*(uint64_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostLongLongToBig(spvxRev);
+	uuidComponentOffset += sizeof(spvxRev);
 }
 
 MTLFeatureSet MVKPhysicalDevice::getHighestMTLFeatureSet() {
 #if MVK_IOS
-	MTLFeatureSet maxFS = MTLFeatureSet_iOS_GPUFamily4_v1;
+	MTLFeatureSet maxFS = MTLFeatureSet_iOS_GPUFamily4_v2;
 	MTLFeatureSet minFS = MTLFeatureSet_iOS_GPUFamily1_v1;
 #endif
 
 #if MVK_MACOS
-	MTLFeatureSet maxFS = MTLFeatureSet_macOS_GPUFamily1_v3;
+	MTLFeatureSet maxFS = MTLFeatureSet_macOS_GPUFamily2_v1;
 	MTLFeatureSet minFS = MTLFeatureSet_macOS_GPUFamily1_v1;
 #endif
 
@@ -1385,6 +1386,25 @@
 	return minFS;
 }
 
+// Retrieve the SPIRV-Cross Git revision hash from a derived header file that was created in the fetchDependencies script.
+uint64_t MVKPhysicalDevice::getSpirvCrossRevision() {
+
+#include "../External/SPIRV-Cross/mvkSpirvCrossRevisionDerived.h"
+
+	static const string revStr(spirvCrossRevisionString, 0, 16);	// We just need the first 16 chars
+	static const string lut("0123456789ABCDEF");
+
+	uint64_t revVal = 0;
+	for (char c : revStr) {
+		size_t cVal = lut.find(toupper(c));
+		if (cVal != string::npos) {
+			revVal <<= 4;
+			revVal += cVal;
+		}
+	}
+	return revVal;
+}
+
 /** Initializes the memory properties of this instance. */
 void MVKPhysicalDevice::initMemoryProperties() {
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 6f307b1..90e7176 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -703,7 +703,7 @@
     addVertexInputToShaderConverterContext(shaderContext, pCreateInfo);
 	id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pVertexSS->module)->getMTLFunction(&shaderContext, _pVertexSS->pSpecializationInfo, _pipelineCache).mtlFunction;
 	if ( !mtlFunction ) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader function could not be compiled into pipeline. See previous logged error."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader function could not be compiled into pipeline. See previous logged error."));
 		return false;
 	}
 	plDesc.vertexFunction = mtlFunction;
@@ -712,16 +712,16 @@
 	_needsVertexOutputBuffer = shaderContext.options.needsOutputBuffer;
 	// If we need the auxiliary buffer and there's no place to put it, we're in serious trouble.
 	if (_needsVertexAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageVertex] >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader requires auxiliary buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader requires auxiliary buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	// Ditto captured output buffer.
 	if (_needsVertexOutputBuffer && _outputBufferIndex.stages[kMVKShaderStageVertex] >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader requires output buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader requires output buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	if (_needsVertexOutputBuffer && _indirectParamsIndex.stages[kMVKShaderStageVertex] >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Vertex shader requires indirect parameters buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader requires indirect parameters buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	return true;
@@ -739,7 +739,7 @@
 	addPrevStageOutputToShaderConverterContext(shaderContext, vtxOutputs);
 	id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pTessCtlSS->module)->getMTLFunction(&shaderContext, _pTessCtlSS->pSpecializationInfo, _pipelineCache).mtlFunction;
 	if ( !mtlFunction ) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation control shader function could not be compiled into pipeline. See previous logged error."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader function could not be compiled into pipeline. See previous logged error."));
 		return false;
 	}
 	plDesc.computeFunction = mtlFunction;
@@ -748,23 +748,23 @@
 	_needsTessCtlPatchOutputBuffer = shaderContext.options.needsPatchOutputBuffer;
 	_needsTessCtlInput = shaderContext.options.needsInputThreadgroupMem;
 	if (_needsTessCtlAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageTessCtl] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation control shader requires auxiliary buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires auxiliary buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	if (_indirectParamsIndex.stages[kMVKShaderStageTessCtl] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation control shader requires indirect parameters buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires indirect parameters buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	if (_needsTessCtlOutputBuffer && _outputBufferIndex.stages[kMVKShaderStageTessCtl] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation control shader requires per-vertex output buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires per-vertex output buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	if (_needsTessCtlPatchOutputBuffer && _tessCtlPatchOutputBufferIndex >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation control shader requires per-patch output buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires per-patch output buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	if (_tessCtlLevelBufferIndex >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation control shader requires tessellation level output buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires tessellation level output buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	return true;
@@ -779,7 +779,7 @@
 	addPrevStageOutputToShaderConverterContext(shaderContext, tcOutputs);
 	id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pTessEvalSS->module)->getMTLFunction(&shaderContext, _pTessEvalSS->pSpecializationInfo, _pipelineCache).mtlFunction;
 	if ( !mtlFunction ) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation evaluation shader function could not be compiled into pipeline. See previous logged error."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation evaluation shader function could not be compiled into pipeline. See previous logged error."));
 		return false;
 	}
 	// Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal.
@@ -788,7 +788,7 @@
 	_needsTessEvalAuxBuffer = shaderContext.options.needsAuxBuffer;
 	// If we need the auxiliary buffer and there's no place to put it, we're in serious trouble.
 	if (_needsTessEvalAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageTessEval] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessEvalNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Tessellation evaluation shader requires auxiliary buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation evaluation shader requires auxiliary buffer, but there is no free slot to pass it."));
 		return false;
 	}
 	return true;
@@ -802,13 +802,13 @@
 		shaderContext.options.shouldCaptureOutput = false;
 		id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pFragmentSS->module)->getMTLFunction(&shaderContext, _pFragmentSS->pSpecializationInfo, _pipelineCache).mtlFunction;
 		if ( !mtlFunction ) {
-			setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Fragment shader function could not be compiled into pipeline. See previous logged error."));
+			setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Fragment shader function could not be compiled into pipeline. See previous logged error."));
 			return false;
 		}
 		plDesc.fragmentFunction = mtlFunction;
 		_needsFragmentAuxBuffer = shaderContext.options.needsAuxBuffer;
 		if (_needsFragmentAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageFragment] >= _device->_pMetalFeatures->maxPerStageBufferCount) {
-			setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Fragment shader requires auxiliary buffer, but there is no free slot to pass it."));
+			setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Fragment shader requires auxiliary buffer, but there is no free slot to pass it."));
 			return false;
 		}
 	}
@@ -1170,11 +1170,11 @@
 		_mtlPipelineState = plc->newMTLComputePipelineState(shaderFunc.mtlFunction);	// retained
 		plc->destroy();
 	} else {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Compute shader function could not be compiled into pipeline. See previous logged error."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader function could not be compiled into pipeline. See previous logged error."));
 	}
 
 	if (_needsAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageCompute] > _device->_pMetalFeatures->maxPerStageBufferCount) {
-		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "Compute shader requires auxiliary buffer, but there is no free slot to pass it."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader requires auxiliary buffer, but there is no free slot to pass it."));
 	}
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
index 4a0ff70..573cf30 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
@@ -21,6 +21,7 @@
 #include "MVKDevice.h"
 #include "MVKSync.h"
 #include <MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h>
+#include <MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h>
 #include <vector>
 #include <mutex>
 
@@ -167,22 +168,22 @@
 	bool convert(SPIRVToMSLConverterContext* pContext);
 
 	/** Returns the original SPIR-V code that was specified when this object was created. */
-	inline const std::vector<uint32_t>& getSPIRV() { return _converter.getSPIRV(); }
+	const std::vector<uint32_t>& getSPIRV() { return _spvConverter.getSPIRV(); }
 
 	/**
 	 * Returns the Metal Shading Language source code as converted by the most recent
 	 * call to convert() function, or set directly using the setMSL() function.
 	 */
-	inline const std::string& getMSL() { return _converter.getMSL(); }
+	const std::string& getMSL() { return _spvConverter.getMSL(); }
 
 	/**
 	 * Returns information about the shader entry point as converted by the most recent
 	 * call to convert() function, or set directly using the setMSL() function.
 	 */
-	inline const SPIRVEntryPoint& getEntryPoint() { return _converter.getEntryPoint(); }
+	const SPIRVEntryPoint& getEntryPoint() { return _spvConverter.getEntryPoint(); }
 
 	/** Returns a key as a means of identifying this shader module in a pipeline cache. */
-	inline MVKShaderModuleKey getKey() { return _key; }
+	MVKShaderModuleKey getKey() { return _key; }
 
 	MVKShaderModule(MVKDevice* device, const VkShaderModuleCreateInfo* pCreateInfo);
 
@@ -191,8 +192,11 @@
 protected:
 	friend MVKShaderCacheIterator;
 
+	MVKGLSLConversionShaderStage getMVKGLSLConversionShaderStage(SPIRVToMSLConverterContext* pContext);
+
 	MVKShaderLibraryCache _shaderLibraryCache;
-	SPIRVToMSLConverter _converter;
+	SPIRVToMSLConverter _spvConverter;
+	GLSLToSPIRVConverter _glslConverter;
 	MVKShaderLibrary* _defaultLibrary;
 	MVKShaderModuleKey _key;
     std::mutex _accessLock;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index 199c0ba..6c30480 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -92,7 +92,7 @@
             }
         }
     } else {
-        reportError(VK_ERROR_INITIALIZATION_FAILED, "Shader module does not contain an entry point named '%s'.", mtlFuncName.UTF8String);
+        reportError(VK_ERROR_INVALID_SHADER_NV, "Shader module does not contain an entry point named '%s'.", mtlFuncName.UTF8String);
     }
 
 	return { mtlFunc, MTLSizeMake(getWorkgroupDimensionSize(_entryPoint.workgroupSize.width, pSpecializationInfo),
@@ -150,7 +150,7 @@
     if (_mtlLibrary) {
         MVKLogInfo("%s succeeded with warnings (Error code %li):\n%s", opDesc, (long)err.code, err.localizedDescription.UTF8String);
     } else {
-		_owner->setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED,
+		_owner->setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV,
 												   "%s failed (Error code %li):\n%s",
 												   opDesc, (long)err.code,
 												   err.localizedDescription.UTF8String));
@@ -244,19 +244,53 @@
 
 bool MVKShaderModule::convert(SPIRVToMSLConverterContext* pContext) {
 	bool shouldLogCode = _device->_pMVKConfig->debugMode;
+	bool shouldLogEstimatedGLSL = shouldLogCode;
+
+	// If the SPIR-V converter does not have any code, but the GLSL converter does,
+	// convert the GLSL code to SPIR-V and set it into the SPIR-V conveter.
+	if ( !_spvConverter.hasSPIRV() && _glslConverter.hasGLSL() ) {
+
+		uint64_t startTime = _device->getPerformanceTimestamp();
+		bool wasConverted = _glslConverter.convert(getMVKGLSLConversionShaderStage(pContext), shouldLogCode, false);
+		_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.glslToSPRIV, startTime);
+
+		if (wasConverted) {
+			if (shouldLogCode) { MVKLogInfo("%s", _glslConverter.getResultLog().c_str()); }
+			_spvConverter.setSPIRV(_glslConverter.getSPIRV());
+		} else {
+			reportError(VK_ERROR_INVALID_SHADER_NV, "Unable to convert GLSL to SPIR-V:\n%s", _glslConverter.getResultLog().c_str());
+		}
+		shouldLogEstimatedGLSL = false;
+	}
 
 	uint64_t startTime = _device->getPerformanceTimestamp();
-	bool wasConverted = _converter.convert(*pContext, shouldLogCode, shouldLogCode, shouldLogCode);
+	bool wasConverted = _spvConverter.convert(*pContext, shouldLogCode, shouldLogCode, shouldLogEstimatedGLSL);
 	_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.spirvToMSL, startTime);
 
 	if (wasConverted) {
-		if (shouldLogCode) { MVKLogInfo("%s", _converter.getResultLog().data()); }
+		if (shouldLogCode) { MVKLogInfo("%s", _spvConverter.getResultLog().c_str()); }
 	} else {
-		reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "Unable to convert SPIR-V to MSL:\n%s", _converter.getResultLog().data());
+		reportError(VK_ERROR_INVALID_SHADER_NV, "Unable to convert SPIR-V to MSL:\n%s", _spvConverter.getResultLog().c_str());
 	}
 	return wasConverted;
 }
 
+// Returns the MVKGLSLConversionShaderStage corresponding to the shader stage in the SPIR-V converter context.
+MVKGLSLConversionShaderStage MVKShaderModule::getMVKGLSLConversionShaderStage(SPIRVToMSLConverterContext* pContext) {
+	switch (pContext->options.entryPointStage) {
+		case spv::ExecutionModelVertex:						return kMVKGLSLConversionShaderStageVertex;
+		case spv::ExecutionModelTessellationControl:		return kMVKGLSLConversionShaderStageTessControl;
+		case spv::ExecutionModelTessellationEvaluation:		return kMVKGLSLConversionShaderStageTessEval;
+		case spv::ExecutionModelGeometry:					return kMVKGLSLConversionShaderStageGeometry;
+		case spv::ExecutionModelFragment:					return kMVKGLSLConversionShaderStageFragment;
+		case spv::ExecutionModelGLCompute:					return kMVKGLSLConversionShaderStageCompute;
+		case spv::ExecutionModelKernel:						return kMVKGLSLConversionShaderStageCompute;
+
+		default:
+			MVKAssert(false, "Bad shader stage provided for GLSL to SPIR-V conversion.");
+			return kMVKGLSLConversionShaderStageAuto;
+	}
+}
 
 #pragma mark Construction
 
@@ -270,7 +304,7 @@
 
     // Ensure something is there.
     if ( (pCreateInfo->pCode == VK_NULL_HANDLE) || (codeSize < 4) ) {
-		setConfigurationResult(reportError(VK_INCOMPLETE, "Shader module contains no SPIR-V code."));
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "vkCreateShaderModule(): Shader module contains no shader code."));
 		return;
 	}
 
@@ -279,36 +313,36 @@
 	// Retrieve the magic number to determine what type of shader code has been loaded.
 	uint32_t magicNum = *pCreateInfo->pCode;
 	switch (magicNum) {
-		case kMVKMagicNumberSPIRVCode: {                        // SPIR-V code
-			size_t spvCount = (pCreateInfo->codeSize + 3) >> 2; // Round up if byte length not exactly on uint32_t boundary
+		case kMVKMagicNumberSPIRVCode: {					// SPIR-V code
+			size_t spvCount = (codeSize + 3) >> 2;			// Round up if byte length not exactly on uint32_t boundary
 
 			uint64_t startTime = _device->getPerformanceTimestamp();
 			codeHash = mvkHash(pCreateInfo->pCode, spvCount);
 			_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.hashShaderCode, startTime);
 
-			_converter.setSPIRV(pCreateInfo->pCode, spvCount);
+			_spvConverter.setSPIRV(pCreateInfo->pCode, spvCount);
 
 			break;
 		}
-		case kMVKMagicNumberMSLSourceCode: {                    // MSL source code
+		case kMVKMagicNumberMSLSourceCode: {				// MSL source code
 			size_t hdrSize = sizeof(MVKMSLSPIRVHeader);
 			char* pMSLCode = (char*)(uintptr_t(pCreateInfo->pCode) + hdrSize);
-			size_t mslCodeLen = pCreateInfo->codeSize - hdrSize;
+			size_t mslCodeLen = codeSize - hdrSize;
 
 			uint64_t startTime = _device->getPerformanceTimestamp();
 			codeHash = mvkHash(&magicNum);
 			codeHash = mvkHash(pMSLCode, mslCodeLen, codeHash);
 			_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.hashShaderCode, startTime);
 
-			_converter.setMSL(pMSLCode, nullptr);
-			_defaultLibrary = new MVKShaderLibrary(this, _converter.getMSL().c_str(), _converter.getEntryPoint());
+			_spvConverter.setMSL(pMSLCode, nullptr);
+			_defaultLibrary = new MVKShaderLibrary(this, _spvConverter.getMSL().c_str(), _spvConverter.getEntryPoint());
 
 			break;
 		}
-		case kMVKMagicNumberMSLCompiledCode: {                  // MSL compiled binary code
+		case kMVKMagicNumberMSLCompiledCode: {				// MSL compiled binary code
 			size_t hdrSize = sizeof(MVKMSLSPIRVHeader);
 			char* pMSLCode = (char*)(uintptr_t(pCreateInfo->pCode) + hdrSize);
-			size_t mslCodeLen = pCreateInfo->codeSize - hdrSize;
+			size_t mslCodeLen = codeSize - hdrSize;
 
 			uint64_t startTime = _device->getPerformanceTimestamp();
 			codeHash = mvkHash(&magicNum);
@@ -319,8 +353,19 @@
 
 			break;
 		}
-		default:
-			setConfigurationResult(reportError(VK_ERROR_FORMAT_NOT_SUPPORTED, "SPIR-V contains invalid magic number %x.", magicNum));
+		default:											// Could be GLSL source code
+			if (_device->_enabledExtensions.vk_NV_glsl_shader.enabled) {
+				const char* pGLSL = (char*)pCreateInfo->pCode;
+				size_t glslLen = codeSize - 1;
+
+				uint64_t startTime = _device->getPerformanceTimestamp();
+				codeHash = mvkHash(pGLSL, codeSize);
+				_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.hashShaderCode, startTime);
+
+				_glslConverter.setGLSL(pGLSL, glslLen);
+			} else {
+				setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "vkCreateShaderModule(): The SPIR-V contains an invalid magic number %x.", magicNum));
+			}
 			break;
 	}
 
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index 764db98..7c5fd14 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -63,7 +63,8 @@
 MVK_EXTENSION(MVK_moltenvk, MVK_MOLTENVK)
 MVK_EXTENSION(AMD_gpu_shader_half_float, AMD_GPU_SHADER_HALF_FLOAT)
 MVK_EXTENSION(AMD_negative_viewport_height, AMD_NEGATIVE_VIEWPORT_HEIGHT)
-MVK_EXTENSION_LAST(IMG_format_pvrtc, IMG_FORMAT_PVRTC)
+MVK_EXTENSION(IMG_format_pvrtc, IMG_FORMAT_PVRTC)
+MVK_EXTENSION_LAST(NV_glsl_shader, NV_GLSL_SHADER)
 
 #undef MVK_EXTENSION
 #undef MVK_EXTENSION_LAST
diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h
index 0ad5c67..fe12106 100644
--- a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h
+++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.h
@@ -32,14 +32,14 @@
 
 /** Enumeration of the pipeline stages for which a shader can be compiled. */
 typedef enum {
-	kMVKShaderStageAuto,
-	kMVKShaderStageVertex,
-	kMVKShaderStageTessControl,
-	kMVKShaderStageTessEval,
-	kMVKShaderStageGeometry,
-	kMVKShaderStageFragment,
-	kMVKShaderStageCompute,
-} MVKShaderStage;
+	kMVKGLSLConversionShaderStageAuto,
+	kMVKGLSLConversionShaderStageVertex,
+	kMVKGLSLConversionShaderStageTessControl,
+	kMVKGLSLConversionShaderStageTessEval,
+	kMVKGLSLConversionShaderStageGeometry,
+	kMVKGLSLConversionShaderStageFragment,
+	kMVKGLSLConversionShaderStageCompute,
+} MVKGLSLConversionShaderStage;
 
 
 /**
@@ -63,7 +63,7 @@
  * should be logged to the converter results log. This can be useful during shader debugging.
  */
 bool mvkConvertGLSLToSPIRV(const char* glslSource,
-                           MVKShaderStage shaderStage,
+                           MVKGLSLConversionShaderStage shaderStage,
                            uint32_t** pSPIRVCode,
                            size_t *pSPIRVLength,
                            char** pResultLog,
@@ -90,7 +90,7 @@
  * should be logged to the converter results log. This can be useful during shader debugging.
  */
 bool mvkConvertGLSLFileToSPIRV(const char* glslFilepath,
-                               MVKShaderStage shaderStage,
+                               MVKGLSLConversionShaderStage shaderStage,
                                uint32_t** pSPIRVCode,
                                size_t *pSPIRVLength,
                                char** pResultLog,
diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm
index 8cfb49b..909abf8 100644
--- a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm
+++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLConversion.mm
@@ -26,7 +26,7 @@
 
 
 MVK_PUBLIC_SYMBOL bool mvkConvertGLSLToSPIRV(const char* glslSource,
-                                             MVKShaderStage shaderStage,
+                                             MVKGLSLConversionShaderStage shaderStage,
                                              uint32_t** pSPIRVCode,
                                              size_t *pSPIRVLength,
                                              char** pResultLog,
@@ -59,7 +59,7 @@
 }
 
 MVK_PUBLIC_SYMBOL bool mvkConvertGLSLFileToSPIRV(const char* glslFilepath,
-                                                 MVKShaderStage shaderStage,
+                                                 MVKGLSLConversionShaderStage shaderStage,
                                                  uint32_t** pSPIRVCode,
                                                  size_t *pSPIRVLength,
                                                  char** pResultLog,
diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp
index b9b45d5..8f19dda 100644
--- a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp
+++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.cpp
@@ -37,13 +37,9 @@
 void configureGLSLCompilerResources(TBuiltInResource* glslCompilerResources);
 
 /** Returns the GLSL compiler language type corresponding to the specified MoltenVK shader stage. */
-EShLanguage eshLanguageFromMVKShaderStage(const MVKShaderStage mvkShaderStage);
+EShLanguage eshLanguageFromMVKGLSLConversionShaderStage(const MVKGLSLConversionShaderStage mvkShaderStage);
 
-MVK_PUBLIC_SYMBOL void GLSLToSPIRVConverter::setGLSL(const string& glslSrc) { _glsl = glslSrc; }
-
-MVK_PUBLIC_SYMBOL const string& GLSLToSPIRVConverter::getGLSL() { return _glsl; }
-
-MVK_PUBLIC_SYMBOL bool GLSLToSPIRVConverter::convert(MVKShaderStage shaderStage,
+MVK_PUBLIC_SYMBOL bool GLSLToSPIRVConverter::convert(MVKGLSLConversionShaderStage shaderStage,
 													 bool shouldLogGLSL,
 													 bool shouldLogSPIRV) {
 	_wasConverted = true;
@@ -54,7 +50,7 @@
 
 	EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
 
-	EShLanguage stage = eshLanguageFromMVKShaderStage(shaderStage);
+	EShLanguage stage = eshLanguageFromMVKGLSLConversionShaderStage(shaderStage);
 	TBuiltInResource glslCompilerResources;
 	configureGLSLCompilerResources(&glslCompilerResources);
 	const char *glslStrings[1];
@@ -91,12 +87,6 @@
 	return _wasConverted;
 }
 
-MVK_PUBLIC_SYMBOL const vector<uint32_t>& GLSLToSPIRVConverter::getSPIRV() { return _spirv; }
-
-MVK_PUBLIC_SYMBOL bool GLSLToSPIRVConverter::getWasConverted() { return _wasConverted; }
-
-MVK_PUBLIC_SYMBOL const string& GLSLToSPIRVConverter::getResultLog() { return _resultLog; }
-
 /** Appends the message text to the result log. */
 void GLSLToSPIRVConverter::logMsg(const char* logMsg) {
 	string trimMsg = trim(logMsg);
@@ -240,15 +230,15 @@
 	glslCompilerResources->limits.generalConstantMatrixVectorIndexing = 1;
 }
 
-EShLanguage eshLanguageFromMVKShaderStage(const MVKShaderStage mvkShaderStage) {
+EShLanguage eshLanguageFromMVKGLSLConversionShaderStage(const MVKGLSLConversionShaderStage mvkShaderStage) {
 	switch (mvkShaderStage) {
-		case kMVKShaderStageVertex:			return EShLangVertex;
-		case kMVKShaderStageTessControl:	return EShLangTessControl;
-		case kMVKShaderStageTessEval:		return EShLangTessEvaluation;
-		case kMVKShaderStageGeometry:		return EShLangGeometry;
-		case kMVKShaderStageFragment:		return EShLangFragment;
-		case kMVKShaderStageCompute:		return EShLangCompute;
-		default:							return EShLangVertex;
+		case kMVKGLSLConversionShaderStageVertex:		return EShLangVertex;
+		case kMVKGLSLConversionShaderStageTessControl:	return EShLangTessControl;
+		case kMVKGLSLConversionShaderStageTessEval:		return EShLangTessEvaluation;
+		case kMVKGLSLConversionShaderStageGeometry:		return EShLangGeometry;
+		case kMVKGLSLConversionShaderStageFragment:		return EShLangFragment;
+		case kMVKGLSLConversionShaderStageCompute:		return EShLangCompute;
+		default:										return EShLangVertex;
 	}
 }
 
diff --git a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h
index 48f2ef1..2bc433f 100644
--- a/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h
+++ b/MoltenVKShaderConverter/MoltenVKGLSLToSPIRVConverter/GLSLToSPIRVConverter.h
@@ -35,11 +35,20 @@
 
 	public:
 
-		/** Sets the GLSL source code that is to be converted to the specified null-terminated string. */
-		void setGLSL(const std::string& glslSrc);
+		/** Sets the GLSL source code that is to be converted to the specified string. */
+		void setGLSL(const std::string& glslSrc) { _glsl = glslSrc; }
+
+		/**
+		 * Sets the GLSL source code that is to be converted from the first length characters
+		 * of the buffer, and ensuring the resulting string is null-terminated.
+		 */
+		void setGLSL(const char* glslSrc, size_t length) { _glsl.assign(glslSrc, length); }
 
 		/** Returns the GLSL source code that was set using the setGLSL() function. */
-		const std::string& getGLSL();
+		const std::string& getGLSL() { return _glsl; }
+
+		/** Returns whether the SPIR-V code has been set. */
+		bool hasGLSL() { return !_glsl.empty(); }
 
 		/**
 		 * Converts GLSL code, set with setGLSL(), to SPIR-V code, which can be retrieved using getSPIRV().
@@ -47,23 +56,23 @@
 		 * The boolean flags indicate whether the original GLSL code and resulting SPIR-V code should
 		 * be logged to the result log of this converter. This can be useful during shader debugging.
 		 */
-		bool convert(MVKShaderStage shaderStage, bool shouldLogGLSL, bool shouldLogSPIRV);
-
-		/** Returns the SPIRV code most recently converted by the convert() function. */
-		const std::vector<uint32_t>& getSPIRV();
+		bool convert(MVKGLSLConversionShaderStage shaderStage, bool shouldLogGLSL, bool shouldLogSPIRV);
 
 		/**
 		 * Returns whether the most recent conversion was successful.
 		 *
 		 * The initial value of this property is NO. It is set to YES upon successful conversion.
 		 */
-		bool getWasConverted();
+		bool wasConverted() { return _wasConverted; }
+
+		/** Returns the SPIRV code most recently converted by the convert() function. */
+		const std::vector<uint32_t>& getSPIRV() { return _spirv; }
 
 		/**
 		 * Returns a human-readable log of the most recent conversion activity.
 		 * This may be empty if the conversion was successful.
 		 */
-		const std::string& getResultLog();
+		const std::string& getResultLog() { return _resultLog; }
 
 	protected:
 		void logMsg(const char* logMsg);
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
index a593c25..4caa021 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
@@ -197,8 +197,6 @@
 // Populates the entry point with info extracted from the SPRI-V compiler.
 void populateEntryPoint(SPIRVEntryPoint& entryPoint, SPIRV_CROSS_NAMESPACE::Compiler* pCompiler, SPIRVToMSLConverterOptions& options);
 
-MVK_PUBLIC_SYMBOL void SPIRVToMSLConverter::setSPIRV(const vector<uint32_t>& spirv) { _spirv = spirv; }
-
 MVK_PUBLIC_SYMBOL void SPIRVToMSLConverter::setSPIRV(const uint32_t* spirvCode, size_t length) {
 	_spirv.clear();			// Clear for reuse
 	_spirv.reserve(length);
@@ -207,8 +205,6 @@
 	}
 }
 
-MVK_PUBLIC_SYMBOL const vector<uint32_t>& SPIRVToMSLConverter::getSPIRV() { return _spirv; }
-
 MVK_PUBLIC_SYMBOL bool SPIRVToMSLConverter::convert(SPIRVToMSLConverterContext& context,
 													bool shouldLogSPIRV,
 													bool shouldLogMSL,
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
index 83e9339..feac98a 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
@@ -225,7 +225,7 @@
 	public:
 
 		/** Sets the SPIRV code. */
-		void setSPIRV(const std::vector<uint32_t>& spirv);
+		void setSPIRV(const std::vector<uint32_t>& spirv) { _spirv = spirv; }
 
 		/**
 		 * Sets the SPIRV code from the specified array of values.
@@ -234,7 +234,10 @@
 		void setSPIRV(const uint32_t* spirvCode, size_t length);
 
 		/** Returns a reference to the SPIRV code, set by one of the setSPIRV() functions. */
-		const std::vector<uint32_t>& getSPIRV();
+		const std::vector<uint32_t>& getSPIRV() { return _spirv; }
+
+		/** Returns whether the SPIR-V code has been set. */
+		bool hasSPIRV() { return !_spirv.empty(); }
 
 		/**
 		 * Converts SPIR-V code, set using setSPIRV() to MSL code, which can be retrieved using getMSL().
@@ -249,6 +252,13 @@
                      bool shouldLogGLSL = false);
 
 		/**
+		 * Returns whether the most recent conversion was successful.
+		 *
+		 * The initial value of this property is NO. It is set to YES upon successful conversion.
+		 */
+		bool wasConverted() { return _wasConverted; }
+
+		/**
 		 * Returns the Metal Shading Language source code most recently converted
          * by the convert() function, or set directly using the setMSL() function.
 		 */
@@ -258,13 +268,6 @@
         const SPIRVEntryPoint& getEntryPoint() { return _entryPoint; }
 
 		/**
-		 * Returns whether the most recent conversion was successful.
-		 *
-		 * The initial value of this property is NO. It is set to YES upon successful conversion.
-		 */
-		bool getWasConverted() { return _wasConverted; }
-
-		/**
 		 * Returns a human-readable log of the most recent conversion activity.
 		 * This may be empty if the conversion was successful.
 		 */
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp
index a193e81..cf441bc 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.cpp
@@ -84,7 +84,7 @@
 
 	string pathExtn = pathExtension(absPath);
 	if (_shouldReadGLSL && isGLSLFileExtension(pathExtn)) {
-		return convertGLSL(absPath, emptyPath, emptyPath, kMVKShaderStageAuto);
+		return convertGLSL(absPath, emptyPath, emptyPath, kMVKGLSLConversionShaderStageAuto);
 	} else if (_shouldReadSPIRV && isSPIRVFileExtension(pathExtn)) {
 		return convertSPIRV(absPath, emptyPath);
 	}
@@ -97,7 +97,7 @@
 bool MoltenVKShaderConverterTool::convertGLSL(string& glslInFile,
 											string& spvOutFile,
 											string& mslOutFile,
-											MVKShaderStage shaderStage) {
+											MVKGLSLConversionShaderStage shaderStage) {
 	string path;
 	vector<char> fileContents;
 	string glslCode;
@@ -120,11 +120,11 @@
 	}
 	glslCode.append(fileContents.begin(), fileContents.end());
 
-	if (shaderStage == kMVKShaderStageAuto) {
+	if (shaderStage == kMVKGLSLConversionShaderStageAuto) {
 		string pathExtn = pathExtension(glslInFile);
 		shaderStage = shaderStageFromFileExtension(pathExtn);
 	}
-	if (shaderStage == kMVKShaderStageAuto) {
+	if (shaderStage == kMVKGLSLConversionShaderStageAuto) {
 		errMsg = "Could not determine shader type from GLSL file: " + absolutePath(path);
 		log(errMsg.data());
 		return false;
@@ -254,11 +254,11 @@
 	}
 }
 
-MVKShaderStage MoltenVKShaderConverterTool::shaderStageFromFileExtension(string& pathExtension) {
-    for (auto& fx : _glslVtxFileExtns) { if (fx == pathExtension) { return kMVKShaderStageVertex; } }
-    for (auto& fx : _glslFragFileExtns) { if (fx == pathExtension) { return kMVKShaderStageFragment; } }
-    for (auto& fx : _glslCompFileExtns) { if (fx == pathExtension) { return kMVKShaderStageCompute; } }
-	return kMVKShaderStageAuto;
+MVKGLSLConversionShaderStage MoltenVKShaderConverterTool::shaderStageFromFileExtension(string& pathExtension) {
+    for (auto& fx : _glslVtxFileExtns) { if (fx == pathExtension) { return kMVKGLSLConversionShaderStageVertex; } }
+    for (auto& fx : _glslFragFileExtns) { if (fx == pathExtension) { return kMVKGLSLConversionShaderStageFragment; } }
+    for (auto& fx : _glslCompFileExtns) { if (fx == pathExtension) { return kMVKGLSLConversionShaderStageCompute; } }
+	return kMVKGLSLConversionShaderStageAuto;
 }
 
 bool MoltenVKShaderConverterTool::isGLSLFileExtension(string& pathExtension) {
@@ -372,7 +372,7 @@
     extractTokens(_defaultCompShaderExtns, _glslCompFileExtns);
 	extractTokens(_defaultSPIRVShaderExtns, _spvFileExtns);
 	_origPathExtnSep = "_";
-	_shaderStage = kMVKShaderStageAuto;
+	_shaderStage = kMVKGLSLConversionShaderStageAuto;
 	_shouldUseDirectoryRecursion = false;
 	_shouldReadGLSL = false;
 	_shouldReadSPIRV = false;
@@ -482,13 +482,13 @@
 
 			switch (shdrTypeStr.front()) {
 				case 'v':
-					_shaderStage = kMVKShaderStageVertex;
+					_shaderStage = kMVKGLSLConversionShaderStageVertex;
 					break;
 				case 'f':
-					_shaderStage = kMVKShaderStageFragment;
+					_shaderStage = kMVKGLSLConversionShaderStageFragment;
 					break;
 				case 'c':
-					_shaderStage = kMVKShaderStageCompute;
+					_shaderStage = kMVKGLSLConversionShaderStageCompute;
 					break;
 				default:
 					return false;
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h
index 1850433..b4baa61 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverterTool/MoltenVKShaderConverterTool.h
@@ -64,13 +64,13 @@
 		MoltenVKShaderConverterTool(int argc, const char* argv[]);
 
 	protected:
-		MVKShaderStage shaderStageFromFileExtension(std::string& pathExtension);
+		MVKGLSLConversionShaderStage shaderStageFromFileExtension(std::string& pathExtension);
 		bool isGLSLFileExtension(std::string& pathExtension);
 		bool isSPIRVFileExtension(std::string& pathExtension);
 		bool convertGLSL(std::string& glslInFile,
 						 std::string& spvOutFile,
 						 std::string& mslOutFile,
-						 MVKShaderStage shaderStage);
+						 MVKGLSLConversionShaderStage shaderStage);
 		bool convertSPIRV(std::string& spvInFile,
 						  std::string& mslOutFile);
 		bool convertSPIRV(const std::vector<uint32_t>& spv,
@@ -100,7 +100,7 @@
 		std::vector<std::string> _glslFragFileExtns;
         std::vector<std::string> _glslCompFileExtns;
 		std::vector<std::string> _spvFileExtns;
-		MVKShaderStage _shaderStage;
+		MVKGLSLConversionShaderStage _shaderStage;
 		MVKPerformanceTracker _glslConversionPerformance;
 		MVKPerformanceTracker _spvConversionPerformance;
 		uint32_t _mslVersionMajor;
diff --git a/fetchDependencies b/fetchDependencies
index 6526b4b..92e2ec6 100755
--- a/fetchDependencies
+++ b/fetchDependencies
@@ -80,13 +80,13 @@
 		cd $1
 		git fetch --all
 		git checkout --force $3
-		cd -
+		cd -  > /dev/null
 	else
 		rm -rf $1
 		git clone $2 $1
 		cd $1
 		git checkout $3
-		cd -
+		cd -  > /dev/null
 	fi
 }
 
@@ -105,7 +105,7 @@
 		make -j $(sysctl -n hw.activecpu)
 	fi
 
-	cd -
+	cd -  > /dev/null
 }
 
 
@@ -189,6 +189,11 @@
 	update_repo ${REPO_NAME} ${REPO_URL} ${REPO_REV}
 fi
 
+# Record the SPIRV-Cross revision as a derived header file suitable for including in a build
+HDR_FILE=${REPO_NAME}/mvkSpirvCrossRevisionDerived.h
+echo "// Auto-generated by MoltenVK" > ${HDR_FILE}
+echo "static const char* spirvCrossRevisionString = \"${REPO_REV}\";" >> ${HDR_FILE}
+
 
 # ----------------- glslang -------------------
 
@@ -212,7 +217,7 @@
 
 	cd ${REPO_NAME}
 	./update_glslang_sources.py
-	cd -
+	cd -  > /dev/null
 fi
 
 #Make sure the embedded spirv-tools is built
@@ -248,7 +253,7 @@
 cd "${REPO_NAME}/Sample-Programs/Hologram"
 ./generate-dispatch-table HelpersDispatchTable.h
 ./generate-dispatch-table HelpersDispatchTable.cpp
-cd -
+cd -  > /dev/null
 
 
 # ----------------- Cleanup -------------------