Avoid reading env vars inside library constructor functions.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index fa02809..3db0682 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -29,6 +29,7 @@
 - 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.3*.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index ab70f77..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 {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index f948005..5746f45 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -662,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/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 43541a4..6aad20c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -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/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/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 5d4eee4..0ccba28 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
@@ -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/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;