Merge pull request #789 from billhollings/master

Fix inconsistency in reporting device local memory between type and heap on macOS.
diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj
index 2633b8d..39b0214 100644
--- a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj
+++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/project.pbxproj
@@ -545,7 +545,7 @@
 		29B97313FDCFA39411CA2CEA /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1120;
+				LastUpgradeCheck = 1130;
 			};
 			buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "API-Samples" */;
 			compatibilityVersion = "Xcode 8.0";
diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme
index 9e750ec..cd9f6b1 100644
--- a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme
+++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme
index 72454f3..9e3343d 100644
--- a/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme
+++ b/Demos/LunarG-VulkanSamples/API-Samples/API-Samples.xcodeproj/xcshareddata/xcschemes/API-Samples-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj
index 5d1ebb2..3be4f3a 100644
--- a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj
+++ b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/project.pbxproj
@@ -234,7 +234,7 @@
 		29B97313FDCFA39411CA2CEA /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1120;
+				LastUpgradeCheck = 1130;
 			};
 			buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Cube" */;
 			compatibilityVersion = "Xcode 8.0";
diff --git a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme
index 3b37ff1..0280935 100644
--- a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme
+++ b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme
index a8a8b2d..f5b92b4 100644
--- a/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme
+++ b/Demos/LunarG-VulkanSamples/Cube/Cube.xcodeproj/xcshareddata/xcschemes/Cube-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj
index e8b32c1..06e807d 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj
+++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/project.pbxproj
@@ -265,7 +265,7 @@
 		29B97313FDCFA39411CA2CEA /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1120;
+				LastUpgradeCheck = 1130;
 			};
 			buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Hologram" */;
 			compatibilityVersion = "Xcode 8.0";
diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme
index d47a50e..e04eb5b 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme
+++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme
index ae913ef..c6531c9 100644
--- a/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme
+++ b/Demos/LunarG-VulkanSamples/Hologram/Hologram.xcodeproj/xcshareddata/xcschemes/Hologram-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index d292e64..3db0682 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -27,8 +27,12 @@
 - Fix image subresource sizing calculations for heap-based textures.
 - Fix `MTLHeap` memory leak in `MVKDeviceMemory`.
 - Fix tessellation break when control stage declares but does not use position builtin.
+- Fix inconsistency in reporting device local memory between type and heap on macOS.
+- Fix bug where dynamic shader buffers are overflowing.
+- Avoid reading env vars inside library constructor functions.
+- Update `VK_MVK_MOLTENVK_SPEC_VERSION` to `23`.
 - Cube demo use `VK_EXT_metal_surface` extension.
-- Support *Xcode 11.2*.
+- Support *Xcode 11.3*.
 
 
 
diff --git a/ExternalDependencies.xcodeproj/project.pbxproj b/ExternalDependencies.xcodeproj/project.pbxproj
index 6c2b6aa..347cf12 100644
--- a/ExternalDependencies.xcodeproj/project.pbxproj
+++ b/ExternalDependencies.xcodeproj/project.pbxproj
@@ -2946,7 +2946,7 @@
 		A9F55D25198BE6A7004EC31B /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1120;
+				LastUpgradeCheck = 1130;
 				ORGANIZATIONNAME = "The Brenwill Workshop Ltd.";
 				TargetAttributes = {
 					A972A7E421CEC72F0013AB25 = {
diff --git "a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies \050Debug\051.xcscheme" "b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies \050Debug\051.xcscheme"
index c17eb8e..3b2aa3e 100644
--- "a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies \050Debug\051.xcscheme"
+++ "b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies \050Debug\051.xcscheme"
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-iOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-iOS.xcscheme
index 45a2a05..27269b7 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-iOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-macOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-macOS.xcscheme
index 399a124..73b694c 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-macOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies.xcscheme
index f35830b..db75dc5 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/ExternalDependencies.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-iOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-iOS.xcscheme
index bce227c..cbe74af 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-iOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-macOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-macOS.xcscheme
index 69ff72d..f943b8a 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-macOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Cross-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-iOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-iOS.xcscheme
index 2ff7ab0..937b24b 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-iOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-macOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-macOS.xcscheme
index 343ed0b..62f0e9f 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-macOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/SPIRV-Tools-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-iOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-iOS.xcscheme
index bdd951d..bc9dce8 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-iOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-macOS.xcscheme b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-macOS.xcscheme
index 9ee0b0f..1405451 100644
--- a/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-macOS.xcscheme
+++ b/ExternalDependencies.xcodeproj/xcshareddata/xcschemes/glslang-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index a13dc32..5cbd127 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -830,7 +830,7 @@
 		A9F55D25198BE6A7004EC31B /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1120;
+				LastUpgradeCheck = 1130;
 				ORGANIZATIONNAME = "The Brenwill Workshop Ltd.";
 				TargetAttributes = {
 					A9B8EE091A98D796009C5A02 = {
diff --git a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme
index 4f79a17..3c6827a 100644
--- a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme
+++ b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme
index f229392..bbd8694 100644
--- a/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme
+++ b/MoltenVK/MoltenVK.xcodeproj/xcshareddata/xcschemes/MoltenVK-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h
index d6ca91e..96f7f91 100644
--- a/MoltenVK/MoltenVK/API/mvk_datatypes.h
+++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h
@@ -455,12 +455,12 @@
 /** Macro indicating the Vulkan memory type bits corresponding to Metal private memory (not host visible). */
 #define MVK_VK_MEMORY_TYPE_METAL_PRIVATE	(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
 
+/** Macro indicating the Vulkan memory type bits corresponding to Metal shared memory (host visible and coherent). */
+#define MVK_VK_MEMORY_TYPE_METAL_SHARED		(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
+
 /** Macro indicating the Vulkan memory type bits corresponding to Metal managed memory (host visible and non-coherent). */
 #define MVK_VK_MEMORY_TYPE_METAL_MANAGED	(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
 
-/** Macro indicating the Vulkan memory type bits corresponding to Metal shared memory (host visible and coherent). */
-#define MVK_VK_MEMORY_TYPE_METAL_SHARED		(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
-
 /** Macro indicating the Vulkan memory type bits corresponding to Metal memoryless memory (not host visible and lazily allocated). */
 #define MVK_VK_MEMORY_TYPE_METAL_MEMORYLESS	(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
 
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 2d53d2e..45bdfa8 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -55,7 +55,7 @@
 #define MVK_MAKE_VERSION(major, minor, patch)    (((major) * 10000) + ((minor) * 100) + (patch))
 #define MVK_VERSION     MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)
 
-#define VK_MVK_MOLTENVK_SPEC_VERSION            22
+#define VK_MVK_MOLTENVK_SPEC_VERSION            23
 #define VK_MVK_MOLTENVK_EXTENSION_NAME          "VK_MVK_moltenvk"
 
 /**
@@ -528,7 +528,7 @@
     uint32_t mslVersion;                        /**< The version of the Metal Shading Language available on this device. The format of the integer is MMmmpp, with two decimal digts each for Major, minor, and patch version values (eg. MSL 1.2 would appear as 010200). */
 	VkBool32 indirectDrawing;                   /**< If true, draw calls support parameters held in a GPU buffer. */
 	VkBool32 baseVertexInstanceDrawing;         /**< If true, draw calls support specifiying the base vertex and instance. */
-    VkBool32 dynamicMTLBuffers;                 /**< If true, dynamic MTLBuffers for setting vertex, fragment, and compute bytes are supported. */
+    uint32_t dynamicMTLBufferSize;              /**< If greater than zero, dynamic MTLBuffers for setting vertex, fragment, and compute bytes are supported, and their content must be below this value. */
     VkBool32 shaderSpecialization;              /**< If true, shader specialization (aka Metal function constants) is supported. */
     VkBool32 ioSurfaces;                        /**< If true, VkImages can be underlaid by IOSurfaces via the vkUseIOSurfaceMVK() function, to support inter-process image transfers. */
     VkBool32 texelBuffers;                      /**< If true, texel buffers are supported, allowing the contents of a buffer to be interpreted as an image via a VkBufferView. */
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index c8fbbdd..5b74c1d 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -502,7 +502,7 @@
                                        const void* bytes,
                                        NSUInteger length,
                                        uint32_t mtlBuffIndex) {
-    if (_pDeviceMetalFeatures->dynamicMTLBuffers) {
+    if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
         [mtlEncoder setVertexBytes: bytes length: length atIndex: mtlBuffIndex];
     } else {
         const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
@@ -514,7 +514,7 @@
                                          const void* bytes,
                                          NSUInteger length,
                                          uint32_t mtlBuffIndex) {
-    if (_pDeviceMetalFeatures->dynamicMTLBuffers) {
+    if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
         [mtlEncoder setFragmentBytes: bytes length: length atIndex: mtlBuffIndex];
     } else {
         const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
@@ -526,7 +526,7 @@
                                         const void* bytes,
                                         NSUInteger length,
                                         uint32_t mtlBuffIndex) {
-    if (_pDeviceMetalFeatures->dynamicMTLBuffers) {
+    if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
         [mtlEncoder setBytes: bytes length: length atIndex: mtlBuffIndex];
     } else {
         const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
index 1925aea..12d5627 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
@@ -137,7 +137,7 @@
 	}
 
 	void reset() {
-		memset(this, 0, sizeof(*this));
+		mvkClear(this);
 		mtlSampleCount = mvkSampleCountFromVkSampleCountFlagBits(VK_SAMPLE_COUNT_1_BIT);
 	}
 
@@ -180,7 +180,7 @@
 
         // Start with all zeros to ensure memory comparisons will work,
         // even if the structure contains alignment gaps.
-        memset(this, 0, sizeof(*this));
+        mvkClear(this);
 
         enabled = false,
         stencilCompareFunction = MTLCompareFunctionAlways;
@@ -233,7 +233,7 @@
 	MVKMTLDepthStencilDescriptorData_t() {
 		// Start with all zeros to ensure memory comparisons will work,
 		// even if the structure contains alignment gaps.
-		memset(this, 0, sizeof(*this));
+		mvkClear(this);
 		disable(true, true);
 	}
 
@@ -276,7 +276,7 @@
 		return mvkHash((uint64_t*)this, sizeof(*this) / sizeof(uint64_t));
 	}
 
-    MVKImageDescriptorData_t() { memset(this, 0, sizeof(*this)); }
+    MVKImageDescriptorData_t() { mvkClear(this); }
 
 } __attribute__((aligned(sizeof(uint64_t)))) MVKImageDescriptorData;
 
@@ -313,7 +313,7 @@
 		return mvkHash((uint64_t*)this, sizeof(*this) / sizeof(uint64_t));
 	}
 
-    MVKBufferDescriptorData_t() { memset(this, 0, sizeof(*this)); }
+    MVKBufferDescriptorData_t() { mvkClear(this); }
 
 } __attribute__((aligned(sizeof(uint64_t)))) MVKBufferDescriptorData;
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index 800d5c1..845368b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -338,6 +338,8 @@
 	void initProperties();
 	void initGPUInfoProperties();
 	void initMemoryProperties();
+	void setMemoryHeap(uint32_t heapIndex, VkDeviceSize heapSize, VkMemoryHeapFlags heapFlags);
+	void setMemoryType(uint32_t typeIndex, uint32_t heapIndex, VkMemoryPropertyFlags propertyFlags);
 	uint64_t getVRAMSize();
 	uint64_t getRecommendedMaxWorkingSetSize();
 	uint64_t getCurrentAllocatedSize();
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index c21786d..ca5b289 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -272,7 +272,7 @@
         case VK_IMAGE_TYPE_1D:
 			maxExt.height = 1;
 			maxExt.depth = 1;
-			if (_mvkTexture1DAs2D) {
+			if (mvkTreatTexture1DAs2D()) {
 				maxExt.width = pLimits->maxImageDimension2D;
 				maxLevels = mvkMipmapLevels3D(maxExt);
 			} else {
@@ -752,8 +752,8 @@
 			switch (next->sType) {
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT: {
 				auto* budgetProps = (VkPhysicalDeviceMemoryBudgetPropertiesEXT*)next;
-				memset(budgetProps->heapBudget, 0, sizeof(budgetProps->heapBudget));
-				memset(budgetProps->heapUsage, 0, sizeof(budgetProps->heapUsage));
+				mvkClear(budgetProps->heapBudget);
+				mvkClear(budgetProps->heapUsage);
 				budgetProps->heapBudget[0] = (VkDeviceSize)getRecommendedMaxWorkingSetSize();
 				budgetProps->heapUsage[0] = (VkDeviceSize)getCurrentAllocatedSize();
 				if (!getHasUnifiedMemory()) {
@@ -787,11 +787,11 @@
 
 /** Initializes the Metal-specific physical device features of this instance. */
 void MVKPhysicalDevice::initMetalFeatures() {
-	memset(&_metalFeatures, 0, sizeof(_metalFeatures));	// Start with everything cleared
+	mvkClear(&_metalFeatures);	// Start with everything cleared
 
 	_metalFeatures.maxPerStageBufferCount = 31;
     _metalFeatures.maxMTLBufferSize = (256 * MEBI);
-    _metalFeatures.dynamicMTLBuffers = false;
+    _metalFeatures.dynamicMTLBufferSize = 0;
 
     _metalFeatures.maxPerStageSamplerCount = 16;
     _metalFeatures.maxQueryBufferSize = (64 * KIBI);
@@ -812,7 +812,7 @@
 
     if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v2] ) {
 		_metalFeatures.mslVersionEnum = MTLLanguageVersion1_1;
-        _metalFeatures.dynamicMTLBuffers = true;
+        _metalFeatures.dynamicMTLBufferSize = (4 * KIBI);
 		_metalFeatures.maxTextureDimension = (8 * KIBI);
     }
 
@@ -881,7 +881,7 @@
 
     if ( [_mtlDevice supportsFeatureSet: MTLFeatureSet_macOS_GPUFamily1_v2] ) {
 		_metalFeatures.mslVersionEnum = MTLLanguageVersion1_2;
-        _metalFeatures.dynamicMTLBuffers = true;
+        _metalFeatures.dynamicMTLBufferSize = (4 * KIBI);
         _metalFeatures.shaderSpecialization = true;
         _metalFeatures.stencilViews = true;
         _metalFeatures.samplerClampToBorder = true;
@@ -979,7 +979,7 @@
 
 // Initializes the physical device features of this instance.
 void MVKPhysicalDevice::initFeatures() {
-	memset(&_features, 0, sizeof(_features));	// Start with everything cleared
+	mvkClear(&_features);	// Start with everything cleared
 
     _features.robustBufferAccess = true;  // XXX Required by Vulkan spec
     _features.fullDrawIndexUint32 = true;
@@ -1130,7 +1130,7 @@
 
 /** Initializes the physical device properties of this instance. */
 void MVKPhysicalDevice::initProperties() {
-	memset(&_properties, 0, sizeof(_properties));	// Start with everything cleared
+	mvkClear(&_properties);	// Start with everything cleared
 
 	_properties.apiVersion = MVK_VULKAN_API_VERSION;
 	_properties.driverVersion = MVK_VERSION;
@@ -1667,7 +1667,7 @@
 void MVKPhysicalDevice::initPipelineCacheUUID() {
 
 	// Clear the UUID
-	memset(&_properties.pipelineCacheUUID, 0, sizeof(_properties.pipelineCacheUUID));
+	mvkClear(&_properties.pipelineCacheUUID);
 
 	size_t uuidComponentOffset = 0;
 
@@ -1749,105 +1749,110 @@
 	return revVal;
 }
 
-/** Initializes the memory properties of this instance. */
+void MVKPhysicalDevice::setMemoryHeap(uint32_t heapIndex, VkDeviceSize heapSize, VkMemoryHeapFlags heapFlags) {
+	_memoryProperties.memoryHeaps[heapIndex].size = heapSize;
+	_memoryProperties.memoryHeaps[heapIndex].flags = heapFlags;
+}
+
+void MVKPhysicalDevice::setMemoryType(uint32_t typeIndex, uint32_t heapIndex, VkMemoryPropertyFlags propertyFlags) {
+	_memoryProperties.memoryTypes[typeIndex].heapIndex = heapIndex;
+	_memoryProperties.memoryTypes[typeIndex].propertyFlags = propertyFlags;
+}
+
+// Initializes the memory properties of this instance.
+// Metal Shared:
+//	- applies to both buffers and textures
+//	- default mode for buffers on both iOS & macOS
+//	- default mode for textures on iOS
+//	- one copy of memory visible to both CPU & GPU
+//	- coherent at command buffer boundaries
+// Metal Private:
+//	- applies to both buffers and textures
+//	- accessed only by GPU through render, compute, or BLIT operations
+//	- no access by CPU
+//	- always use for framebuffers and renderable textures
+// Metal Managed:
+//	- applies to both buffers and textures
+//	- default mode for textures on macOS
+//	- two copies of each buffer or texture when discrete memory available
+//	- convenience of shared mode, performance of private mode
+//	- on unified systems behaves like shared memory and has only one copy of content
+//	- when writing, use:
+//		- buffer didModifyRange:
+//		- texture replaceRegion:
+//	- when reading, use:
+//		- encoder synchronizeResource: followed by
+//		- cmdbuff waitUntilCompleted (or completion handler)
+//		- buffer/texture getBytes:
+// Metal Memoryless:
+//	- applies only to textures used as transient render targets
+//	- only available with TBDR devices (i.e. on iOS)
+//	- no device memory is reserved at all
+//	- storage comes from tile memory
+//	- contents are undefined after rendering
+//	- use for temporary renderable textures
 void MVKPhysicalDevice::initMemoryProperties() {
 
-	// Metal Shared:
-	//	- applies to both buffers and textures
-	//	- default mode for buffers on both iOS & macOS
-	//	- default mode for textures on iOS
-	//	- one copy of memory visible to both CPU & GPU
-	//	- coherent at command buffer boundaries
-	// Metal Private:
-	//	- applies to both buffers and textures
-	//	- accessed only by GPU through render, compute, or BLIT operations
-	//	- no access by CPU
-	//	- always use for framebuffers and renderable textures
-	// Metal Managed:
-	//	- applies to both buffers and textures
-	//	- default mode for textures on macOS
-	//	- two copies of each buffer or texture when discrete memory available
-	//	- convenience of shared mode, performance of private mode
-	//	- on unified systems behaves like shared memory and has only one copy of content
-	//	- when writing, use:
-	//		- buffer didModifyRange:
-	//		- texture replaceRegion:
-	//	- when reading, use:
-	//		- encoder synchronizeResource: followed by
-	//		- cmdbuff waitUntilCompleted (or completion handler)
-	//		- buffer/texture getBytes:
-	// Metal Memoryless:
-	//	- applies only to textures used as transient render targets
-	//	- only available with TBDR devices (i.e. on iOS)
-	//	- no device memory is reserved at all
-	//	- storage comes from tile memory
-	//	- contents are undefined after rendering
-	//	- use for temporary renderable textures
+	mvkClear(&_memoryProperties);	// Start with everything cleared
 
-    _memoryProperties = (VkPhysicalDeviceMemoryProperties){
-        .memoryHeapCount = 1,
-        .memoryHeaps = {
-            {
-                .flags = (VK_MEMORY_HEAP_DEVICE_LOCAL_BIT),
-                .size = (VkDeviceSize)getVRAMSize(),
-            },
-        },
-        // NB this list needs to stay sorted by propertyFlags (as bit sets)
-        .memoryTypes = {
-            {
-                .heapIndex = 0,
-                .propertyFlags = MVK_VK_MEMORY_TYPE_METAL_PRIVATE,    // Private storage
-            },
-#if MVK_MACOS
-            {
-                .heapIndex = 0,
-                .propertyFlags = MVK_VK_MEMORY_TYPE_METAL_MANAGED,    // Managed storage
-            },
-#endif
-            {
-                .heapIndex = 0,
-                .propertyFlags = MVK_VK_MEMORY_TYPE_METAL_SHARED,    // Shared storage
-            },
-#if MVK_IOS
-            {
-                .heapIndex = 0,
-                .propertyFlags = MVK_VK_MEMORY_TYPE_METAL_MEMORYLESS,    // Memoryless storage
-            },
-#endif
-        },
-    };
+	// Main heap
+	uint32_t mainHeapIdx = 0;
+	setMemoryHeap(mainHeapIdx, getVRAMSize(), VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
 
+	// Optional second heap for shared memory
+	uint32_t sharedHeapIdx;
+	VkMemoryPropertyFlags sharedTypePropFlags;
+	if (getHasUnifiedMemory()) {
+		// Shared memory goes in the single main heap in unified memory, and per Vulkan spec must be marked local
+		sharedHeapIdx = mainHeapIdx;
+		sharedTypePropFlags = MVK_VK_MEMORY_TYPE_METAL_SHARED | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+	} else {
+		// Define a second heap to mark the shared memory as non-local
+		sharedHeapIdx = mainHeapIdx + 1;
+		setMemoryHeap(sharedHeapIdx, mvkGetSystemMemorySize(), 0);
+		sharedTypePropFlags = MVK_VK_MEMORY_TYPE_METAL_SHARED;
+	}
+
+	_memoryProperties.memoryHeapCount = sharedHeapIdx + 1;
+
+	// Memory types
+	uint32_t typeIdx = 0;
+
+	// Private storage
+	uint32_t privateBit = 1 << typeIdx;
+	setMemoryType(typeIdx, mainHeapIdx, MVK_VK_MEMORY_TYPE_METAL_PRIVATE);
+	typeIdx++;
+
+	// Shared storage
+	uint32_t sharedBit = 1 << typeIdx;
+	setMemoryType(typeIdx, sharedHeapIdx, sharedTypePropFlags);
+	typeIdx++;
+
+	// Managed storage
+	uint32_t managedBit = 0;
 #if MVK_MACOS
-	_memoryProperties.memoryTypeCount = 3;
-	_privateMemoryTypes			= 0x1;			// Private only
-	_lazilyAllocatedMemoryTypes	= 0x0;			// Not supported on macOS
-	_hostCoherentMemoryTypes 	= 0x4;			// Shared only
-	_hostVisibleMemoryTypes		= 0x6;			// Shared & managed
-	_allMemoryTypes				= 0x7;			// Private, shared, & managed
+	managedBit = 1 << typeIdx;
+	setMemoryType(typeIdx, mainHeapIdx, MVK_VK_MEMORY_TYPE_METAL_MANAGED);
+	typeIdx++;
 #endif
+
+	// Memoryless storage
+	uint32_t memlessBit = 0;
 #if MVK_IOS
-	_memoryProperties.memoryTypeCount = 2;		// Managed storage not available on iOS
-	_privateMemoryTypes			= 0x1;			// Private only
-	_lazilyAllocatedMemoryTypes	= 0x0;			// Not supported on this version
-	_hostCoherentMemoryTypes 	= 0x2;			// Shared only
-	_hostVisibleMemoryTypes		= 0x2;			// Shared only
-	_allMemoryTypes				= 0x3;			// Private & shared
-	if ([getMTLDevice() supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v3]) {
-		_memoryProperties.memoryTypeCount = 3;	// Memoryless storage available
-		_privateMemoryTypes			= 0x5;		// Private & memoryless
-		_lazilyAllocatedMemoryTypes	= 0x4;		// Memoryless only
-		_allMemoryTypes				= 0x7;		// Private, shared & memoryless
+	if ([_mtlDevice supportsFeatureSet: MTLFeatureSet_iOS_GPUFamily1_v3]) {
+		memlessBit = 1 << typeIdx;
+		setMemoryType(typeIdx, mainHeapIdx, MVK_VK_MEMORY_TYPE_METAL_MEMORYLESS);
+		typeIdx++;
 	}
 #endif
-#if MVK_MACOS
-	if (!getHasUnifiedMemory()) {
-		// This means we really have two heaps. The second heap is system memory.
-		_memoryProperties.memoryHeapCount = 2;
-		_memoryProperties.memoryHeaps[1].size = mvkGetSystemMemorySize();
-		_memoryProperties.memoryHeaps[1].flags = 0;
-		_memoryProperties.memoryTypes[2].heapIndex = 1;	// Shared memory in the shared heap
-	}
-#endif
+
+	_memoryProperties.memoryTypeCount = typeIdx;
+
+	_privateMemoryTypes			= privateBit | memlessBit;
+	_hostVisibleMemoryTypes		= sharedBit | managedBit;
+	_hostCoherentMemoryTypes 	= sharedBit;
+	_lazilyAllocatedMemoryTypes	= memlessBit;
+	_allMemoryTypes				= privateBit | sharedBit | managedBit | memlessBit;
 }
 
 bool MVKPhysicalDevice::getHasUnifiedMemory() {
@@ -1855,21 +1860,20 @@
 	return true;
 #endif
 #if MVK_MACOS
-	return [_mtlDevice respondsToSelector: @selector(hasUnifiedMemory)] && _mtlDevice.hasUnifiedMemory;
+	return (([_mtlDevice respondsToSelector: @selector(hasUnifiedMemory)] && _mtlDevice.hasUnifiedMemory)
+			|| _mtlDevice.isLowPower
+			|| getInstance()->getPhysicalDeviceCount() == 1);
 #endif
 }
 
 uint64_t MVKPhysicalDevice::getVRAMSize() {
-#if MVK_IOS
-	// All iOS devices are UMA, so return the system memory size.
-	return mvkGetSystemMemorySize();
-#endif
-#if MVK_MACOS
-	if (getHasUnifiedMemory()) { return mvkGetSystemMemorySize(); }
-	// There's actually no way to query the total physical VRAM on the device in Metal.
-	// Just default to using the recommended max working set size (i.e. the budget).
-	return getRecommendedMaxWorkingSetSize();
-#endif
+	if (getHasUnifiedMemory()) {
+		return mvkGetSystemMemorySize();
+	} else {
+		// There's actually no way to query the total physical VRAM on the device in Metal.
+		// Just default to using the recommended max working set size (i.e. the budget).
+		return getRecommendedMaxWorkingSetSize();
+	}
 }
 
 uint64_t MVKPhysicalDevice::getRecommendedMaxWorkingSetSize() {
@@ -2050,7 +2054,7 @@
 }
 
 VkResult MVKDevice::getDeviceGroupPresentCapabilities(VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) {
-	memset(pDeviceGroupPresentCapabilities->presentMask, 0, sizeof(pDeviceGroupPresentCapabilities->presentMask));
+	mvkClear(pDeviceGroupPresentCapabilities->presentMask);
 	pDeviceGroupPresentCapabilities->presentMask[0] = 0x1;
 
 	pDeviceGroupPresentCapabilities->modes = VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR;
@@ -2680,18 +2684,18 @@
 void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) {
 
 	// Start with all features disabled
-	memset((void*)&_enabledFeatures, 0, sizeof(_enabledFeatures));
-	memset((void*)&_enabledStorage16Features, 0, sizeof(_enabledStorage16Features));
-	memset((void*)&_enabledStorage8Features, 0, sizeof(_enabledStorage8Features));
-	memset((void*)&_enabledF16I8Features, 0, sizeof(_enabledF16I8Features));
-	memset((void*)&_enabledUBOLayoutFeatures, 0, sizeof(_enabledUBOLayoutFeatures));
-	memset((void*)&_enabledVarPtrFeatures, 0, sizeof(_enabledVarPtrFeatures));
-	memset((void*)&_enabledInterlockFeatures, 0, sizeof(_enabledInterlockFeatures));
-	memset((void*)&_enabledHostQryResetFeatures, 0, sizeof(_enabledHostQryResetFeatures));
-	memset((void*)&_enabledScalarLayoutFeatures, 0, sizeof(_enabledScalarLayoutFeatures));
-	memset((void*)&_enabledTexelBuffAlignFeatures, 0, sizeof(_enabledTexelBuffAlignFeatures));
-	memset((void*)&_enabledVtxAttrDivFeatures, 0, sizeof(_enabledVtxAttrDivFeatures));
-	memset((void*)&_enabledPortabilityFeatures, 0, sizeof(_enabledPortabilityFeatures));
+	mvkClear(&_enabledFeatures);
+	mvkClear(&_enabledStorage16Features);
+	mvkClear(&_enabledStorage8Features);
+	mvkClear(&_enabledF16I8Features);
+	mvkClear(&_enabledUBOLayoutFeatures);
+	mvkClear(&_enabledVarPtrFeatures);
+	mvkClear(&_enabledInterlockFeatures);
+	mvkClear(&_enabledHostQryResetFeatures);
+	mvkClear(&_enabledScalarLayoutFeatures);
+	mvkClear(&_enabledTexelBuffAlignFeatures);
+	mvkClear(&_enabledVtxAttrDivFeatures);
+	mvkClear(&_enabledPortabilityFeatures);
 
 	// Fetch the available physical device features.
 	VkPhysicalDevicePortabilitySubsetFeaturesEXTX pdPortabilityFeatures;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 1e8ac3b..5746f45 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -231,7 +231,9 @@
 	useTexelBuffer = useTexelBuffer && _deviceMemory && _deviceMemory->_mtlBuffer;				// Buffer is available to overlay
 
 #if MVK_MACOS
-	useTexelBuffer = useTexelBuffer && !isMemoryHostCoherent();	// macOS cannot use shared memory for texel buffers
+	// macOS cannot use shared memory for texel buffers.
+	// Test _deviceMemory->isMemoryHostCoherent() directly because local version overrides.
+	useTexelBuffer = useTexelBuffer && _deviceMemory && !_deviceMemory->isMemoryHostCoherent();
 #endif
 
 	return useTexelBuffer;
@@ -476,12 +478,12 @@
 MTLStorageMode MVKImage::getMTLStorageMode() {
     if ( !_deviceMemory ) return MTLStorageModePrivate;
 
-    // For macOS, textures cannot use Shared storage mode, so change to Managed storage mode.
     MTLStorageMode stgMode = _deviceMemory->getMTLStorageMode();
 
     if (_ioSurface && stgMode == MTLStorageModePrivate) { stgMode = MTLStorageModeShared; }
 
 #if MVK_MACOS
+	// For macOS, textures cannot use Shared storage mode, so change to Managed storage mode.
     if (stgMode == MTLStorageModeShared) { stgMode = MTLStorageModeManaged; }
 #endif
     return stgMode;
@@ -660,7 +662,7 @@
 	if (validSamples == VK_SAMPLE_COUNT_1_BIT) { return validSamples; }
 
 	// Don't use getImageType() because it hasn't been set yet.
-	if ( !((pCreateInfo->imageType == VK_IMAGE_TYPE_2D) || ((pCreateInfo->imageType == VK_IMAGE_TYPE_1D) && _mvkTexture1DAs2D)) ) {
+	if ( !((pCreateInfo->imageType == VK_IMAGE_TYPE_2D) || ((pCreateInfo->imageType == VK_IMAGE_TYPE_1D) && mvkTreatTexture1DAs2D())) ) {
 		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, multisampling can only be used with a 2D image type. Setting sample count to 1."));
 		validSamples = VK_SAMPLE_COUNT_1_BIT;
 	}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
index 645ed49..c90cd9b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
@@ -71,6 +71,9 @@
 	/** Returns the function pointer corresponding to the named entry point, or NULL if it doesn't exist. */
 	PFN_vkVoidFunction getProcAddr(const char* pName);
 
+	/** Returns the number of available physical devices. */
+	uint32_t getPhysicalDeviceCount() { return (uint32_t)_physicalDevices.size(); }
+
 	/**
 	 * If pPhysicalDevices is null, the value of pCount is updated with the number of 
 	 * physical devices supported by this instance.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index c5a7ae8..592035c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -50,7 +50,7 @@
 VkResult MVKInstance::getPhysicalDevices(uint32_t* pCount, VkPhysicalDevice* pPhysicalDevices) {
 
 	// Get the number of physical devices
-	uint32_t pdCnt = (uint32_t)_physicalDevices.size();
+	uint32_t pdCnt = getPhysicalDeviceCount();
 
 	// If properties aren't actually being requested yet, simply update the returned count
 	if ( !pPhysicalDevices ) {
@@ -72,13 +72,11 @@
 
 VkResult MVKInstance::getPhysicalDeviceGroups(uint32_t* pCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProps) {
 
-	// According to the Vulkan spec:
-	//  "Every physical device *must* be in exactly one device group."
-	// Since we don't really support this yet, we must return one group for every
-	// device.
+	// According to the Vulkan spec: "Every physical device *must* be in exactly one device group."
+	// Since we don't really support this yet, we must return one group for every device.
 
 	// Get the number of physical devices
-	uint32_t pdCnt = (uint32_t)_physicalDevices.size();
+	uint32_t pdCnt = getPhysicalDeviceCount();
 
 	// If properties aren't actually being requested yet, simply update the returned count
 	if ( !pPhysicalDeviceGroupProps ) {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 5dfa27f..6aad20c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -326,7 +326,7 @@
 	}
 
 	// Track dynamic state in _dynamicStateEnabled array
-	memset(&_dynamicStateEnabled, false, sizeof(_dynamicStateEnabled));		// start with all dynamic state disabled
+	mvkClear(&_dynamicStateEnabled);		// start with all dynamic state disabled
 	const VkPipelineDynamicStateCreateInfo* pDS = pCreateInfo->pDynamicState;
 	if (pDS) {
 		for (uint32_t i = 0; i < pDS->dynamicStateCount; i++) {
@@ -1158,7 +1158,7 @@
     _tessCtlPatchOutputBufferIndex = layout->getTessCtlPatchOutputBufferIndex();
     _tessCtlLevelBufferIndex = layout->getTessCtlLevelBufferIndex();
 
-	shaderContext.options.mslOptions.texture_1D_as_2D = _mvkTexture1DAs2D;
+	shaderContext.options.mslOptions.texture_1D_as_2D = mvkTreatTexture1DAs2D();
     shaderContext.options.mslOptions.enable_point_size_builtin = isRenderingPoints(pCreateInfo, reflectData);
     shaderContext.options.shouldFlipVertexY = _device->_pMVKConfig->shaderConversionFlipVertexY;
     shaderContext.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle && !getDevice()->_pMetalFeatures->nativeTextureSwizzle;
@@ -1351,7 +1351,7 @@
 	shaderContext.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle && !getDevice()->_pMetalFeatures->nativeTextureSwizzle;
 	shaderContext.options.mslOptions.texture_buffer_native = _device->_pMetalFeatures->textureBuffers;
 	shaderContext.options.mslOptions.dispatch_base = _allowsDispatchBase;
-	shaderContext.options.mslOptions.texture_1D_as_2D = _mvkTexture1DAs2D;
+	shaderContext.options.mslOptions.texture_1D_as_2D = mvkTreatTexture1DAs2D();
 
     MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout;
     layout->populateShaderConverterContext(shaderContext);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm
index b174d07..acaf35b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm
@@ -259,7 +259,7 @@
     } else {  // Host-side reset
         id<MTLBuffer> vizBuff = getVisibilityResultMTLBuffer();
         size_t size = std::min(lastOffset, vizBuff.length) - firstOffset;
-        memset((char *)[vizBuff contents] + firstOffset, 0, size);
+        mvkClear((char *)[vizBuff contents] + firstOffset, size);
     }
 }
 
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.mm b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
index 31d4a69..1eba10d 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
@@ -33,7 +33,7 @@
 // Returns a VkExtensionProperties struct populated with a name and version
 static VkExtensionProperties mvkMakeExtProps(const char* extensionName, uint32_t specVersion) {
 	VkExtensionProperties extProps;
-	memset(extProps.extensionName, 0, sizeof(extProps.extensionName));
+	mvkClear(extProps.extensionName);
 	if (extensionName) { strcpy(extProps.extensionName, extensionName); }
 	extProps.specVersion = specVersion;
 	return extProps;
diff --git a/MoltenVK/MoltenVK/Layers/MVKLayers.mm b/MoltenVK/MoltenVK/Layers/MVKLayers.mm
index d741b8b..fabb969 100644
--- a/MoltenVK/MoltenVK/Layers/MVKLayers.mm
+++ b/MoltenVK/MoltenVK/Layers/MVKLayers.mm
@@ -18,6 +18,7 @@
 
 #include "MVKLayers.h"
 #include "MVKEnvironment.h"
+#include "MVKFoundation.h"
 #include "vk_mvk_moltenvk.h"
 #include <mutex>
 
@@ -40,9 +41,9 @@
 MVKLayer::MVKLayer() : _supportedInstanceExtensions(nullptr, true) {
 
 	// The core driver layer
-	memset(_layerProperties.layerName, 0, sizeof(_layerProperties.layerName));
+	mvkClear(_layerProperties.layerName);
 	strcpy(_layerProperties.layerName, "MoltenVK");
-	memset(_layerProperties.description, 0, sizeof(_layerProperties.description));
+	mvkClear(_layerProperties.description);
 	strcpy(_layerProperties.description, "MoltenVK driver layer");
 	_layerProperties.specVersion = MVK_VULKAN_API_VERSION;
 	_layerProperties.implementationVersion = MVK_VERSION;
diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
index 2e76c54..8b83b5b 100644
--- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
+++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
@@ -36,14 +36,17 @@
 #endif
 
 static uint32_t _mvkLogLevel = MVK_CONFIG_LOG_LEVEL;
-
-// Initialize log level from environment
 static bool _mvkLoggingInitialized = false;
-__attribute__((constructor)) static void MVKInitLogging() {
-	if (_mvkLoggingInitialized ) { return; }
-	_mvkLoggingInitialized = true;
 
-	MVK_SET_FROM_ENV_OR_BUILD_INT32(_mvkLogLevel, MVK_CONFIG_LOG_LEVEL);
+// Returns log level from environment variable.
+// We do this once lazily instead of in a library constructor function to
+// ensure the NSProcessInfo environment is available when called upon.
+static inline uint32_t getMVKLogLevel() {
+	if ( !_mvkLoggingInitialized ) {
+		_mvkLoggingInitialized = true;
+		MVK_SET_FROM_ENV_OR_BUILD_INT32(_mvkLogLevel, MVK_CONFIG_LOG_LEVEL);
+	}
+	return _mvkLogLevel;
 }
 
 static const char* getReportingLevelString(int aslLvl) {
@@ -99,7 +102,7 @@
 	MVKVulkanAPIObject* mvkAPIObj = mvkObj ? mvkObj->getVulkanAPIObject() : nullptr;
 	MVKInstance* mvkInst = mvkAPIObj ? mvkAPIObj->getInstance() : nullptr;
 	bool hasDebugCallbacks = mvkInst && mvkInst->hasDebugCallbacks();
-	bool shouldLog = (aslLvl < (_mvkLogLevel << 2));
+	bool shouldLog = (aslLvl < (getMVKLogLevel() << 2));
 
 	// Fail fast to avoid further unnecessary processing.
 	if ( !(shouldLog || hasDebugCallbacks) ) { return; }
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
index 4481344..97c2197 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
@@ -416,18 +416,25 @@
     container.erase(remove(container.begin(), container.end(), val), container.end());
 }
 
+/** If pVal is not null, clears the memory occupied by *pVal by writing zeros to all the bytes. */
+template<typename T>
+void mvkClear(T* pVal, size_t clearSize = sizeof(T)) { if (pVal) { memset(pVal, 0, clearSize); } }
+
+/** If pVal is not null, overrides the const declaration, and clears the memory occupied by *pVal by writing zeros to all the bytes. */
+template<typename T>
+void mvkClear(const T* pVal, size_t clearSize = sizeof(T)) { mvkClear((T*)pVal, clearSize); }
+
 /**
  * If pSrc and pDst are not null, copies at most copySize bytes from the contents of the source
- * struct to the destination struct, and returns the number of bytes copied, which is the smaller
+ * value to the destination value, and returns the number of bytes copied, which is the smaller
  * of copySize and the actual size of the struct. If either pSrc or pDst are null, returns zero.
  */
 template<typename S>
-size_t mvkCopyStruct(S* pDst, const S* pSrc, size_t copySize = sizeof(S)) {
-	size_t bytesCopied = 0;
-	if (pSrc && pDst) {
-		bytesCopied = std::min(copySize, sizeof(S));
-		memcpy(pDst, pSrc, bytesCopied);
-	}
+size_t mvkCopy(S* pDst, const S* pSrc, size_t copySize = sizeof(S)) {
+	if ( !(pSrc && pDst) ) { return 0; }
+
+	size_t bytesCopied = std::min(copySize, sizeof(S));
+	memcpy(pDst, pSrc, bytesCopied);
 	return bytesCopied;
 }
 
@@ -449,7 +456,7 @@
         *pDest = *pSrc;
         return true;
     }
-    if (pDest) { memset(pDest, 0, sizeof(T)); }
+    if (pDest) { mvkClear(pDest); }
     return false;
 }
 
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp
index 66c880d..75681b7 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp
@@ -31,10 +31,6 @@
  * which is part of the public external MoltenVK C API.
  */
 
-/** Support the MVK_CONFIG_TEXTURE_1D_AS_2D runtime environment variable. */
-extern bool _mvkTexture1DAs2D;
-
-
 #pragma mark -
 #pragma mark Support for VK_EXT_debug_report extension
 
@@ -91,4 +87,7 @@
 /** Enumerates all formats that support the given features, calling a specified function for each one. */
 void mvkEnumerateSupportedFormats(VkFormatProperties properties, bool any, std::function<bool(VkFormat)> func);
 
+/** Returns whether 1D textures should be treated as Metal 2D textures with height 1. */
+bool mvkTreatTexture1DAs2D();
+
 #endif
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
index c0a631f..0ccba28 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
@@ -499,9 +499,9 @@
 static void MVKInitFormatMaps() {
 
     // Set all VkFormats and MTLPixelFormats to undefined/invalid
-    memset(_fmtDescIndicesByVkFormatsCore, 0, sizeof(_fmtDescIndicesByVkFormatsCore));
-    memset(_fmtDescIndicesByMTLPixelFormats, 0, sizeof(_fmtDescIndicesByMTLPixelFormats));
-    memset(_fmtDescIndicesByMTLVertexFormats, 0, sizeof(_fmtDescIndicesByMTLVertexFormats));
+    mvkClear(_fmtDescIndicesByVkFormatsCore);
+    mvkClear(_fmtDescIndicesByMTLPixelFormats);
+    mvkClear(_fmtDescIndicesByMTLVertexFormats);
 
 	_pFmtDescIndicesByVkFormatsExt = new MVKFormatIndexByVkFormatMap();
 
@@ -822,7 +822,7 @@
 																  bool isMultisample) {
 	switch (vkImageType) {
 		case VK_IMAGE_TYPE_3D: return MTLTextureType3D;
-		case VK_IMAGE_TYPE_1D: return (_mvkTexture1DAs2D
+		case VK_IMAGE_TYPE_1D: return (mvkTreatTexture1DAs2D()
 									   ? mvkMTLTextureTypeFromVkImageType(VK_IMAGE_TYPE_2D, arraySize, isMultisample)
 									   : (arraySize > 1 ? MTLTextureType1DArray : MTLTextureType1D));
 		case VK_IMAGE_TYPE_2D:
@@ -854,8 +854,8 @@
 		case VK_IMAGE_VIEW_TYPE_3D:			return MTLTextureType3D;
 		case VK_IMAGE_VIEW_TYPE_CUBE:		return MTLTextureTypeCube;
 		case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:	return MTLTextureTypeCubeArray;
-		case VK_IMAGE_VIEW_TYPE_1D:			return _mvkTexture1DAs2D ? mvkMTLTextureTypeFromVkImageViewType(VK_IMAGE_VIEW_TYPE_2D, isMultisample) : MTLTextureType1D;
-		case VK_IMAGE_VIEW_TYPE_1D_ARRAY:	return _mvkTexture1DAs2D ? mvkMTLTextureTypeFromVkImageViewType(VK_IMAGE_VIEW_TYPE_2D_ARRAY, isMultisample) : MTLTextureType1DArray;
+		case VK_IMAGE_VIEW_TYPE_1D:			return mvkTreatTexture1DAs2D() ? mvkMTLTextureTypeFromVkImageViewType(VK_IMAGE_VIEW_TYPE_2D, isMultisample) : MTLTextureType1D;
+		case VK_IMAGE_VIEW_TYPE_1D_ARRAY:	return mvkTreatTexture1DAs2D() ? mvkMTLTextureTypeFromVkImageViewType(VK_IMAGE_VIEW_TYPE_2D_ARRAY, isMultisample) : MTLTextureType1DArray;
 
 		case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
 #if MVK_MACOS
@@ -1450,12 +1450,24 @@
 	return (mtlStorageMode << MTLResourceStorageModeShift) | (mtlCPUCacheMode << MTLResourceCPUCacheModeShift);
 }
 
+static bool _mvkTexture1DAs2D = MVK_CONFIG_TEXTURE_1D_AS_2D;
+static bool _mvkTexture1DAs2DInitialized = false;
+
+// Returns environment variable indicating whether to use Metal 2D textures for 1D textures.
+// We do this once lazily instead of in a library constructor function to
+// ensure the NSProcessInfo environment is available when called upon.
+bool mvkTreatTexture1DAs2D() {
+	if ( !_mvkTexture1DAs2DInitialized ) {
+		_mvkTexture1DAs2DInitialized = true;
+		MVK_SET_FROM_ENV_OR_BUILD_INT32(_mvkTexture1DAs2D, MVK_CONFIG_TEXTURE_1D_AS_2D);
+	}
+	return _mvkTexture1DAs2D;
+}
+
 
 #pragma mark -
 #pragma mark Library initialization
 
-bool _mvkTexture1DAs2D = MVK_CONFIG_TEXTURE_1D_AS_2D;
-
 /**
  * Called automatically when the framework is loaded and initialized.
  *
@@ -1464,12 +1476,9 @@
 static bool _mvkDataTypesInitialized = false;
 __attribute__((constructor)) static void MVKInitDataTypes() {
 	if (_mvkDataTypesInitialized ) { return; }
+	_mvkDataTypesInitialized = true;
 
 	MVKInitFormatMaps();
-
-	MVK_SET_FROM_ENV_OR_BUILD_BOOL(_mvkTexture1DAs2D, MVK_CONFIG_TEXTURE_1D_AS_2D);
-
-	_mvkDataTypesInitialized = true;
 }
 
 	
diff --git a/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm
index 749bd03..7ab76f6 100644
--- a/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vk_mvk_moltenvk.mm
@@ -34,10 +34,10 @@
 // value of *pCopySize is the same as the actual size of the struct, or VK_INCOMPLETE otherwise.
 // If either pSrc or pDst are null, sets the value of *pCopySize to the size of the struct and returns VK_SUCCESS.
 template<typename S>
-VkResult mvkCopyStruct(S* pDst, const S* pSrc, size_t* pCopySize) {
+VkResult mvkCopy(S* pDst, const S* pSrc, size_t* pCopySize) {
 	if (pSrc && pDst) {
 		size_t origSize = *pCopySize;
-		*pCopySize = mvkCopyStruct(pDst, pSrc, origSize);
+		*pCopySize = mvkCopy(pDst, pSrc, origSize);
 		return (*pCopySize == origSize) ? VK_SUCCESS : VK_INCOMPLETE;
 	} else {
 		*pCopySize = sizeof(S);
@@ -51,7 +51,7 @@
 	size_t*                                     pConfigurationSize) {
 
 	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
-	return mvkCopyStruct(pConfiguration, mvkInst->getMoltenVKConfiguration(), pConfigurationSize);
+	return mvkCopy(pConfiguration, mvkInst->getMoltenVKConfiguration(), pConfigurationSize);
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkSetMoltenVKConfigurationMVK(
@@ -60,7 +60,7 @@
 	size_t*                                     pConfigurationSize) {
 
 	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
-	return mvkCopyStruct((MVKConfiguration*)mvkInst->getMoltenVKConfiguration(), pConfiguration, pConfigurationSize);
+	return mvkCopy((MVKConfiguration*)mvkInst->getMoltenVKConfiguration(), pConfiguration, pConfigurationSize);
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkGetPhysicalDeviceMetalFeaturesMVK(
@@ -69,7 +69,7 @@
 	size_t*                                     pMetalFeaturesSize) {
 
 	MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
-	return mvkCopyStruct(pMetalFeatures, mvkPD->getMetalFeatures(), pMetalFeaturesSize);
+	return mvkCopy(pMetalFeatures, mvkPD->getMetalFeatures(), pMetalFeaturesSize);
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkGetSwapchainPerformanceMVK(
@@ -79,7 +79,7 @@
 	size_t*                                     pSwapchainPerfSize) {
 
 	MVKSwapchain* mvkSC = (MVKSwapchain*)swapchain;
-	return mvkCopyStruct(pSwapchainPerf, mvkSC->getPerformanceStatistics(), pSwapchainPerfSize);
+	return mvkCopy(pSwapchainPerf, mvkSC->getPerformanceStatistics(), pSwapchainPerfSize);
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkGetPerformanceStatisticsMVK(
@@ -89,7 +89,7 @@
 
 	MVKPerformanceStatistics mvkPerf;
 	MVKDevice::getMVKDevice(device)->getPerformanceStatistics(&mvkPerf);
-	return mvkCopyStruct(pPerf, &mvkPerf, pPerfSize);
+	return mvkCopy(pPerf, &mvkPerf, pPerfSize);
 }
 
 MVK_PUBLIC_SYMBOL void vkGetVersionStringsMVK(
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 0170dca..6ebfd3d 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -51,19 +51,24 @@
 #   define MVK_CONFIG_TRACE_VULKAN_CALLS    0
 #endif
 
-static uint32_t _mvkTraceVulkanCalls = 0;
+static uint32_t _mvkTraceVulkanCalls = MVK_CONFIG_TRACE_VULKAN_CALLS;
 static bool _mvkVulkanCallTracingInitialized = false;
-__attribute__((constructor)) static void MVKInitVulkanCallTracing() {
-	if (_mvkVulkanCallTracingInitialized ) { return; }
-	_mvkVulkanCallTracingInitialized = true;
 
-	MVK_SET_FROM_ENV_OR_BUILD_INT32(_mvkTraceVulkanCalls, MVK_CONFIG_TRACE_VULKAN_CALLS);
+// Returns Vulkan call trace level from environment variable.
+// We do this once lazily instead of in a library constructor function to
+// ensure the NSProcessInfo environment is available when called upon.
+static inline uint32_t getCallTraceLevel() {
+	if ( !_mvkVulkanCallTracingInitialized ) {
+		_mvkVulkanCallTracingInitialized = true;
+		MVK_SET_FROM_ENV_OR_BUILD_INT32(_mvkTraceVulkanCalls, MVK_CONFIG_TRACE_VULKAN_CALLS);
+	}
+	return _mvkTraceVulkanCalls;
 }
 
 // Optionally log start of function calls to stderr
 static inline uint64_t MVKTraceVulkanCallStartImpl(const char* funcName) {
 	uint64_t timestamp = 0;
-	switch(_mvkTraceVulkanCalls) {
+	switch(getCallTraceLevel()) {
 		case 3:			// Fall through
 			timestamp = mvkGetTimestamp();
 		case 2:
@@ -81,7 +86,7 @@
 
 // Optionally log end of function calls and timings to stderr
 static inline void MVKTraceVulkanCallEndImpl(const char* funcName, uint64_t startTime) {
-	switch(_mvkTraceVulkanCalls) {
+	switch(getCallTraceLevel()) {
 		case 3:
 			fprintf(stderr, "[mvk-trace] } %s() (%.4f ms)\n", funcName, mvkGetElapsedMilliseconds(startTime));
 			break;
diff --git a/MoltenVKPackaging.xcodeproj/project.pbxproj b/MoltenVKPackaging.xcodeproj/project.pbxproj
index e3cede1..7598811 100644
--- a/MoltenVKPackaging.xcodeproj/project.pbxproj
+++ b/MoltenVKPackaging.xcodeproj/project.pbxproj
@@ -248,7 +248,7 @@
 		A90B2B1D1A9B6170008EE819 /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1120;
+				LastUpgradeCheck = 1130;
 				TargetAttributes = {
 					A9FEADBC1F3517480010240E = {
 						DevelopmentTeam = VU3TCKU48B;
diff --git "a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050Debug\051.xcscheme" "b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050Debug\051.xcscheme"
index 1b8423f..91cc9a2 100644
--- "a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050Debug\051.xcscheme"
+++ "b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050Debug\051.xcscheme"
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git "a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050iOS only\051.xcscheme" "b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050iOS only\051.xcscheme"
index 47cad9b..5c76128 100644
--- "a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050iOS only\051.xcscheme"
+++ "b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050iOS only\051.xcscheme"
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git "a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050macOS only\051.xcscheme" "b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050macOS only\051.xcscheme"
index dd09b2e..ea0cde8 100644
--- "a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050macOS only\051.xcscheme"
+++ "b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package \050macOS only\051.xcscheme"
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package.xcscheme b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package.xcscheme
index c181fd6..9d862a0 100644
--- a/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package.xcscheme
+++ b/MoltenVKPackaging.xcodeproj/xcshareddata/xcschemes/MoltenVK Package.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "NO"
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj
index feaf033..a8da40a 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/project.pbxproj
@@ -415,7 +415,7 @@
 		A9F55D25198BE6A7004EC31B /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1120;
+				LastUpgradeCheck = 1130;
 				ORGANIZATIONNAME = "The Brenwill Workshop Ltd.";
 				TargetAttributes = {
 					A9092A8C1A81717B00051823 = {
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme
index 27bf446..078a2bb 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme
index 4dc2c24..c89ef49 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKGLSLToSPIRVConverter-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme
index 933b768..d2f230a 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-iOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme
index 932557b..5b630c7 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKSPIRVToMSLConverter-macOS.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme
index cbff5c4..2172574 100644
--- a/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme
+++ b/MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj/xcshareddata/xcschemes/MoltenVKShaderConverter.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1120"
+   LastUpgradeVersion = "1130"
    version = "2.0">
    <BuildAction
       parallelizeBuildables = "YES"