Merge pull request #1674 from billhollings/device-feature-tracking-enhancements

MVKDevice consolidate enabling device feature tracking.
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index c8899d5..cda7856 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -201,7 +201,6 @@
 		A94FB7E21C7DFB4800632CA3 /* MVKDescriptorSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7821C7DFB4800632CA3 /* MVKDescriptorSet.mm */; };
 		A94FB7E31C7DFB4800632CA3 /* MVKDescriptorSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7821C7DFB4800632CA3 /* MVKDescriptorSet.mm */; };
 		A94FB7E41C7DFB4800632CA3 /* MVKDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = A94FB7831C7DFB4800632CA3 /* MVKDevice.h */; };
-		A94FB7E51C7DFB4800632CA3 /* MVKDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = A94FB7831C7DFB4800632CA3 /* MVKDevice.h */; };
 		A94FB7E61C7DFB4800632CA3 /* MVKDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7841C7DFB4800632CA3 /* MVKDevice.mm */; };
 		A94FB7E71C7DFB4800632CA3 /* MVKDevice.mm in Sources */ = {isa = PBXBuildFile; fileRef = A94FB7841C7DFB4800632CA3 /* MVKDevice.mm */; };
 		A94FB7E81C7DFB4800632CA3 /* MVKDeviceMemory.h in Headers */ = {isa = PBXBuildFile; fileRef = A94FB7851C7DFB4800632CA3 /* MVKDeviceMemory.h */; };
@@ -304,6 +303,10 @@
 		A98149641FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h in Headers */ = {isa = PBXBuildFile; fileRef = A981494C1FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h */; };
 		A981496B1FB6A998005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A981496A1FB6A998005F00B4 /* MVKStrings.h */; };
 		A981496C1FB6A998005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A981496A1FB6A998005F00B4 /* MVKStrings.h */; };
+		A987B668289AFB6100F933C8 /* MVKDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = A94FB7831C7DFB4800632CA3 /* MVKDevice.h */; };
+		A987B669289AFB8A00F933C8 /* MVKDeviceFeatureStructs.def in Headers */ = {isa = PBXBuildFile; fileRef = A987B666289AFB2400F933C8 /* MVKDeviceFeatureStructs.def */; };
+		A987B66A289AFB8B00F933C8 /* MVKDeviceFeatureStructs.def in Headers */ = {isa = PBXBuildFile; fileRef = A987B666289AFB2400F933C8 /* MVKDeviceFeatureStructs.def */; };
+		A987B66B289AFB8C00F933C8 /* MVKDeviceFeatureStructs.def in Headers */ = {isa = PBXBuildFile; fileRef = A987B666289AFB2400F933C8 /* MVKDeviceFeatureStructs.def */; };
 		A99C90EE229455B300A061DA /* MVKCmdDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = A99C90EC229455B200A061DA /* MVKCmdDebug.h */; };
 		A99C90EF229455B300A061DA /* MVKCmdDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = A99C90EC229455B200A061DA /* MVKCmdDebug.h */; };
 		A99C90F0229455B300A061DA /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; };
@@ -511,6 +514,7 @@
 		A981494B1FB6A3F7005F00B4 /* MVKWatermarkShaderSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKWatermarkShaderSource.h; sourceTree = "<group>"; };
 		A981494C1FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKWatermarkTextureContent.h; sourceTree = "<group>"; };
 		A981496A1FB6A998005F00B4 /* MVKStrings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKStrings.h; sourceTree = "<group>"; };
+		A987B666289AFB2400F933C8 /* MVKDeviceFeatureStructs.def */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = MVKDeviceFeatureStructs.def; sourceTree = "<group>"; };
 		A99C90EC229455B200A061DA /* MVKCmdDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKCmdDebug.h; sourceTree = "<group>"; };
 		A99C90ED229455B300A061DA /* MVKCmdDebug.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCmdDebug.mm; sourceTree = "<group>"; };
 		A99C91002295FAC500A061DA /* MVKVulkanAPIObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKVulkanAPIObject.mm; sourceTree = "<group>"; };
@@ -620,6 +624,7 @@
 				A94FB7821C7DFB4800632CA3 /* MVKDescriptorSet.mm */,
 				A94FB7831C7DFB4800632CA3 /* MVKDevice.h */,
 				A94FB7841C7DFB4800632CA3 /* MVKDevice.mm */,
+				A987B666289AFB2400F933C8 /* MVKDeviceFeatureStructs.def */,
 				A94FB7851C7DFB4800632CA3 /* MVKDeviceMemory.h */,
 				A94FB7861C7DFB4800632CA3 /* MVKDeviceMemory.mm */,
 				A94FB7871C7DFB4800632CA3 /* MVKFramebuffer.h */,
@@ -804,6 +809,7 @@
 				2FEA0A4C24902F9F00EEF3AD /* MVKCommandPipelineStateFactoryShaderSource.h in Headers */,
 				2FEA0A4D24902F9F00EEF3AD /* MVKDescriptorSet.h in Headers */,
 				2FEA0A4E24902F9F00EEF3AD /* NSString+MoltenVK.h in Headers */,
+				A987B66A289AFB8B00F933C8 /* MVKDeviceFeatureStructs.def in Headers */,
 				2FEA0A4F24902F9F00EEF3AD /* CAMetalLayer+MoltenVK.h in Headers */,
 				2FEA0A5024902F9F00EEF3AD /* MVKCodec.h in Headers */,
 				2FEA0A5124902F9F00EEF3AD /* MVKRenderPass.h in Headers */,
@@ -863,6 +869,7 @@
 			files = (
 				A909F65F213B190700FCD6BE /* MVKExtensions.h in Headers */,
 				A94FB7B41C7DFB4800632CA3 /* vk_mvk_moltenvk.h in Headers */,
+				A987B669289AFB8A00F933C8 /* MVKDeviceFeatureStructs.def in Headers */,
 				A94FB7B01C7DFB4800632CA3 /* mvk_datatypes.h in Headers */,
 				A948BB7F1E51642700DE59F2 /* mvk_vulkan.h in Headers */,
 				A98149511FB6A3F7005F00B4 /* MVKEnvironment.h in Headers */,
@@ -936,6 +943,7 @@
 			files = (
 				A909F660213B190700FCD6BE /* MVKExtensions.h in Headers */,
 				A94FB7B51C7DFB4800632CA3 /* vk_mvk_moltenvk.h in Headers */,
+				A987B66B289AFB8C00F933C8 /* MVKDeviceFeatureStructs.def in Headers */,
 				A94FB7B11C7DFB4800632CA3 /* mvk_datatypes.h in Headers */,
 				A948BB801E51642700DE59F2 /* mvk_vulkan.h in Headers */,
 				A98149521FB6A3F7005F00B4 /* MVKEnvironment.h in Headers */,
@@ -959,7 +967,6 @@
 				A98149621FB6A3F7005F00B4 /* MVKWatermarkShaderSource.h in Headers */,
 				A9E53DE42100B197002781DD /* MTLSamplerDescriptor+MoltenVK.h in Headers */,
 				A94FB8191C7DFB4800632CA3 /* MVKSync.h in Headers */,
-				A94FB7E51C7DFB4800632CA3 /* MVKDevice.h in Headers */,
 				A9F3D9DF24732A4D00745190 /* MVKSmallVector.h in Headers */,
 				A94FB7D51C7DFB4800632CA3 /* MVKCommandPool.h in Headers */,
 				A94FB80D1C7DFB4800632CA3 /* MVKShaderModule.h in Headers */,
@@ -982,6 +989,7 @@
 				A94FB7F11C7DFB4800632CA3 /* MVKImage.h in Headers */,
 				4553AEFE2251617100E8EBCD /* MVKBlockObserver.h in Headers */,
 				A94FB7B91C7DFB4800632CA3 /* MVKCmdTransfer.h in Headers */,
+				A987B668289AFB6100F933C8 /* MVKDevice.h in Headers */,
 				A966A5E023C535D000BBF9B4 /* MVKDescriptor.h in Headers */,
 				A94FB7C91C7DFB4800632CA3 /* MVKCmdDraw.h in Headers */,
 				A94FB7D11C7DFB4800632CA3 /* MVKCommandBuffer.h in Headers */,
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index ffd7838..70d6030 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -219,8 +219,8 @@
 	 * it may cause unexpected visual artifacts and unnecessary GPU load.
 	 *
 	 * This feature is incompatible with updating descriptors after binding. If any of the
-	 * *UpdateAfterBind feature flags of VkPhysicalDeviceDescriptorIndexingFeaturesEXT or
-	 * VkPhysicalDeviceInlineUniformBlockFeaturesEXT have been enabled, the value of this
+	 * *UpdateAfterBind feature flags of VkPhysicalDeviceDescriptorIndexingFeatures or
+	 * VkPhysicalDeviceInlineUniformBlockFeatures have been enabled, the value of this
 	 * setting will be ignored and treated as if it is false.
 	 *
 	 * The value of this parameter may be changed at any time during application runtime,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index c55398b..7ada59a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -773,31 +773,17 @@
 #pragma mark Properties directly accessible
 
 	/** The list of Vulkan extensions, indicating whether each has been enabled by the app for this device. */
-	const MVKExtensionList _enabledExtensions;
+	MVKExtensionList _enabledExtensions;
 
 	/** Device features available and enabled. */
-	const VkPhysicalDeviceFeatures _enabledFeatures;
-	const VkPhysicalDevice16BitStorageFeatures _enabledStorage16Features;
-	const VkPhysicalDevice8BitStorageFeaturesKHR _enabledStorage8Features;
-	const VkPhysicalDeviceFloat16Int8FeaturesKHR _enabledF16I8Features;
-	const VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR _enabledUBOLayoutFeatures;
-	const VkPhysicalDeviceVariablePointerFeatures _enabledVarPtrFeatures;
-	const VkPhysicalDeviceDescriptorIndexingFeaturesEXT _enabledDescriptorIndexingFeatures;
-	const VkPhysicalDeviceInlineUniformBlockFeaturesEXT _enabledInlineUniformBlockFeatures;
-	const VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT _enabledInterlockFeatures;
-	const VkPhysicalDeviceHostQueryResetFeaturesEXT _enabledHostQryResetFeatures;
-	const VkPhysicalDeviceSamplerYcbcrConversionFeatures _enabledSamplerYcbcrConversionFeatures;
-	const VkPhysicalDevicePrivateDataFeaturesEXT _enabledPrivateDataFeatures;
-	const VkPhysicalDeviceScalarBlockLayoutFeaturesEXT _enabledScalarLayoutFeatures;
-	const VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT _enabledTexelBuffAlignFeatures;
-	const VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT _enabledVtxAttrDivFeatures;
-	const VkPhysicalDevicePortabilitySubsetFeaturesKHR _enabledPortabilityFeatures;
-	const VkPhysicalDeviceImagelessFramebufferFeaturesKHR _enabledImagelessFramebufferFeatures;
-	const VkPhysicalDeviceDynamicRenderingFeatures _enabledDynamicRenderingFeatures;
-	const VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures _enabledSeparateDepthStencilLayoutsFeatures;
-	const VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR _enabledFragmentShaderBarycentricFeatures;
-	const VkPhysicalDeviceBufferDeviceAddressFeatures _enabledBufferDeviceAddressFeatures;
-	const VkPhysicalDeviceBufferDeviceAddressFeaturesEXT _enabledBufferDeviceAddressFeaturesEXT;
+	VkPhysicalDeviceFeatures _enabledFeatures;
+
+	// List of extended device feature enabling structures, as public member variables.
+#define MVK_DEVICE_FEATURE(structName, enumName, flagCount) \
+	VkPhysicalDevice##structName##Features _enabled##structName##Features;
+#define MVK_DEVICE_FEATURE_EXTN(structName, enumName, extnSfx, flagCount) \
+	VkPhysicalDevice##structName##Features##extnSfx _enabled##structName##Features;
+#include "MVKDeviceFeatureStructs.def"
 
 	/** Pointer to the Metal-specific features of the underlying physical device. */
 	const MVKPhysicalDeviceMetalFeatures* _pMetalFeatures;
@@ -852,7 +838,8 @@
 	void initQueues(const VkDeviceCreateInfo* pCreateInfo);
 	void reservePrivateData(const VkDeviceCreateInfo* pCreateInfo);
 	void enableFeatures(const VkDeviceCreateInfo* pCreateInfo);
-	void enableFeatures(const VkBool32* pEnable, const VkBool32* pRequested, const VkBool32* pAvailable, uint32_t count);
+	void enableFeatures(VkBaseInStructure* pEnabled, const VkBaseInStructure* pRequested, const VkBaseInStructure* pAvailable, uint32_t count);
+	void enableFeatures(VkBool32* pEnabledBools, const VkBool32* pRequestedBools, const VkBool32* pAvailableBools, uint32_t count);
 	void enableExtensions(const VkDeviceCreateInfo* pCreateInfo);
     const char* getActivityPerformanceDescription(MVKPerformanceTracker& activity, MVKPerformanceStatistics& perfStats);
 	void logActivityPerformance(MVKPerformanceTracker& activity, MVKPerformanceStatistics& perfStats, bool isInline = false);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 539a569..30a49d5 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -97,6 +97,8 @@
 	features->features = _features;
 	for (auto* next = (VkBaseOutStructure*)features->pNext; next; next = next->pNext) {
 		switch ((uint32_t)next->sType) {
+
+			// For consistency and ease of admin, keep the following list in the same order as in MVKDeviceFeatureStructs.def
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: {
 				auto* storageFeatures = (VkPhysicalDevice16BitStorageFeatures*)next;
 				storageFeatures->storageBuffer16BitAccess = true;
@@ -105,64 +107,23 @@
 				storageFeatures->storageInputOutput16 = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR: {
-				auto* storageFeatures = (VkPhysicalDevice8BitStorageFeaturesKHR*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES: {
+				auto* storageFeatures = (VkPhysicalDevice8BitStorageFeatures*)next;
 				storageFeatures->storageBuffer8BitAccess = true;
 				storageFeatures->uniformAndStorageBuffer8BitAccess = true;
 				storageFeatures->storagePushConstant8 = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR: {
-				auto* f16Features = (VkPhysicalDeviceFloat16Int8FeaturesKHR*)next;
-				f16Features->shaderFloat16 = true;
-				f16Features->shaderInt8 = true;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES:
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT: {
+				auto* bufferDeviceAddressFeatures = (VkPhysicalDeviceBufferDeviceAddressFeatures*)next;
+				bufferDeviceAddressFeatures->bufferDeviceAddress = mvkOSVersionIsAtLeast(12.05, 16.0);
+				bufferDeviceAddressFeatures->bufferDeviceAddressCaptureReplay = false;
+				bufferDeviceAddressFeatures->bufferDeviceAddressMultiDevice = false;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: {
-				auto* multiviewFeatures = (VkPhysicalDeviceMultiviewFeatures*)next;
-				multiviewFeatures->multiview = true;
-				multiviewFeatures->multiviewGeometryShader = false;
-				multiviewFeatures->multiviewTessellationShader = false; // FIXME
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES: {
-				auto* protectedMemFeatures = (VkPhysicalDeviceProtectedMemoryFeatures*)next;
-				protectedMemFeatures->protectedMemory = false;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: {
-				auto* samplerYcbcrConvFeatures = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*)next;
-				samplerYcbcrConvFeatures->samplerYcbcrConversion = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: {
-				auto* shaderDrawParamsFeatures = (VkPhysicalDeviceShaderDrawParametersFeatures*)next;
-				shaderDrawParamsFeatures->shaderDrawParameters = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES: {
-				auto* shaderSGTypesFeatures = (VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures*)next;
-				shaderSGTypesFeatures->shaderSubgroupExtendedTypes = _metalFeatures.simdPermute || _metalFeatures.quadPermute;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES: {
-				auto* timelineSem4Features = (VkPhysicalDeviceTimelineSemaphoreFeatures*)next;
-				timelineSem4Features->timelineSemaphore = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR: {
-				auto* uboLayoutFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR*)next;
-				uboLayoutFeatures->uniformBufferStandardLayout = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES: {
-				auto* varPtrFeatures = (VkPhysicalDeviceVariablePointerFeatures*)next;
-				varPtrFeatures->variablePointersStorageBuffer = true;
-				varPtrFeatures->variablePointers = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT: {
-				auto* pDescIdxFeatures = (VkPhysicalDeviceDescriptorIndexingFeaturesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES: {
+				auto* pDescIdxFeatures = (VkPhysicalDeviceDescriptorIndexingFeatures*)next;
 				pDescIdxFeatures->shaderInputAttachmentArrayDynamicIndexing = _metalFeatures.arrayOfTextures;
 				pDescIdxFeatures->shaderUniformTexelBufferArrayDynamicIndexing = _metalFeatures.arrayOfTextures;
 				pDescIdxFeatures->shaderStorageTexelBufferArrayDynamicIndexing = _metalFeatures.arrayOfTextures;
@@ -185,60 +146,110 @@
 				pDescIdxFeatures->runtimeDescriptorArray = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT: {
-				auto* interlockFeatures = (VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT*)next;
-				interlockFeatures->fragmentShaderSampleInterlock = _metalFeatures.rasterOrderGroups;
-				interlockFeatures->fragmentShaderPixelInterlock = _metalFeatures.rasterOrderGroups;
-				interlockFeatures->fragmentShaderShadingRateInterlock = false;    // Requires variable rate shading; not supported yet in Metal
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: {
+				auto* dynamicRenderingFeatures = (VkPhysicalDeviceDynamicRenderingFeatures*)next;
+				dynamicRenderingFeatures->dynamicRendering = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT: {
-				auto* hostQueryResetFeatures = (VkPhysicalDeviceHostQueryResetFeaturesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES: {
+				auto* hostQueryResetFeatures = (VkPhysicalDeviceHostQueryResetFeatures*)next;
 				hostQueryResetFeatures->hostQueryReset = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES_EXT: {
-				auto *imageRobustnessFeatures = (VkPhysicalDeviceImageRobustnessFeaturesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES: {
+				auto* imagelessFramebufferFeatures = (VkPhysicalDeviceImagelessFramebufferFeatures*)next;
+				imagelessFramebufferFeatures->imagelessFramebuffer = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES: {
+				auto *imageRobustnessFeatures = (VkPhysicalDeviceImageRobustnessFeatures*)next;
 				imageRobustnessFeatures->robustImageAccess = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT: {
-				auto* privateDataFeatures = (VkPhysicalDevicePrivateDataFeaturesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES: {
+				auto* inlineUniformBlockFeatures = (VkPhysicalDeviceInlineUniformBlockFeatures*)next;
+				inlineUniformBlockFeatures->inlineUniformBlock = true;
+				inlineUniformBlockFeatures->descriptorBindingInlineUniformBlockUpdateAfterBind = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: {
+				auto* multiviewFeatures = (VkPhysicalDeviceMultiviewFeatures*)next;
+				multiviewFeatures->multiview = true;
+				multiviewFeatures->multiviewGeometryShader = false;
+				multiviewFeatures->multiviewTessellationShader = false;		// FIXME
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES: {
+				auto* privateDataFeatures = (VkPhysicalDevicePrivateDataFeatures*)next;
 				privateDataFeatures->privateData = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT: {
-				auto* robustness2Features = (VkPhysicalDeviceRobustness2FeaturesEXT*)next;
-				robustness2Features->robustBufferAccess2 = false;
-				robustness2Features->robustImageAccess2 = true;
-				robustness2Features->nullDescriptor = false;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES: {
+				auto* protectedMemFeatures = (VkPhysicalDeviceProtectedMemoryFeatures*)next;
+				protectedMemFeatures->protectedMemory = false;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT: {
-				auto* scalarLayoutFeatures = (VkPhysicalDeviceScalarBlockLayoutFeaturesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: {
+				auto* samplerYcbcrConvFeatures = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*)next;
+				samplerYcbcrConvFeatures->samplerYcbcrConversion = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES: {
+				auto* scalarLayoutFeatures = (VkPhysicalDeviceScalarBlockLayoutFeatures*)next;
 				scalarLayoutFeatures->scalarBlockLayout = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT: {
-				auto* subgroupSizeFeatures = (VkPhysicalDeviceSubgroupSizeControlFeaturesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES: {
+				auto* separateDepthStencilLayoutsFeatures = (VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures*)next;
+				separateDepthStencilLayoutsFeatures->separateDepthStencilLayouts = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: {
+				auto* shaderDrawParamsFeatures = (VkPhysicalDeviceShaderDrawParametersFeatures*)next;
+				shaderDrawParamsFeatures->shaderDrawParameters = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES: {
+				auto* f16Features = (VkPhysicalDeviceShaderFloat16Int8Features*)next;
+				f16Features->shaderFloat16 = true;
+				f16Features->shaderInt8 = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES: {
+				auto* shaderSGTypesFeatures = (VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures*)next;
+				shaderSGTypesFeatures->shaderSubgroupExtendedTypes = _metalFeatures.simdPermute || _metalFeatures.quadPermute;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES: {
+				auto* subgroupSizeFeatures = (VkPhysicalDeviceSubgroupSizeControlFeatures*)next;
 				subgroupSizeFeatures->subgroupSizeControl = _metalFeatures.simdPermute || _metalFeatures.quadPermute;
 				subgroupSizeFeatures->computeFullSubgroups = _metalFeatures.simdPermute || _metalFeatures.quadPermute;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT: {
-				auto* texelBuffAlignFeatures = (VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT*)next;
-				texelBuffAlignFeatures->texelBufferAlignment = _metalFeatures.texelBuffers && [_mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)];
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT: {
-				auto* astcHDRFeatures = (VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES: {
+				auto* astcHDRFeatures = (VkPhysicalDeviceTextureCompressionASTCHDRFeatures*)next;
 				astcHDRFeatures->textureCompressionASTC_HDR = _metalFeatures.astcHDRTextures;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: {
-				auto* divisorFeatures = (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT*)next;
-				divisorFeatures->vertexAttributeInstanceRateDivisor = true;
-				divisorFeatures->vertexAttributeInstanceRateZeroDivisor = true;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES: {
+				auto* timelineSem4Features = (VkPhysicalDeviceTimelineSemaphoreFeatures*)next;
+				timelineSem4Features->timelineSemaphore = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES: {
+				auto* uboLayoutFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeatures*)next;
+				uboLayoutFeatures->uniformBufferStandardLayout = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES: {
+				auto* varPtrFeatures = (VkPhysicalDeviceVariablePointerFeatures*)next;
+				varPtrFeatures->variablePointersStorageBuffer = true;
+				varPtrFeatures->variablePointers = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR: {
+				auto* barycentricFeatures = (VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR*)next;
+				barycentricFeatures->fragmentShaderBarycentric = true;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR: {
@@ -261,50 +272,36 @@
 				portabilityFeatures->vertexAttributeAccessBeyondStride = true;	// Costs additional buffers. Should make configuration switch.
 				break;
 			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT: {
+				auto* interlockFeatures = (VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT*)next;
+				interlockFeatures->fragmentShaderSampleInterlock = _metalFeatures.rasterOrderGroups;
+				interlockFeatures->fragmentShaderPixelInterlock = _metalFeatures.rasterOrderGroups;
+				interlockFeatures->fragmentShaderShadingRateInterlock = false;    // Requires variable rate shading; not supported yet in Metal
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT: {
+				auto* robustness2Features = (VkPhysicalDeviceRobustness2FeaturesEXT*)next;
+				robustness2Features->robustBufferAccess2 = false;
+				robustness2Features->robustImageAccess2 = true;
+				robustness2Features->nullDescriptor = false;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT: {
+				auto* texelBuffAlignFeatures = (VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT*)next;
+				texelBuffAlignFeatures->texelBufferAlignment = _metalFeatures.texelBuffers && [_mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)];
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: {
+				auto* divisorFeatures = (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT*)next;
+				divisorFeatures->vertexAttributeInstanceRateDivisor = true;
+				divisorFeatures->vertexAttributeInstanceRateZeroDivisor = true;
+				break;
+			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL: {
 				auto* shaderIntFuncsFeatures = (VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL*)next;
 				shaderIntFuncsFeatures->shaderIntegerFunctions2 = true;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT: {
-				auto* inlineUniformBlockFeatures = (VkPhysicalDeviceInlineUniformBlockFeaturesEXT*)next;
-				inlineUniformBlockFeatures->inlineUniformBlock = true;
-				inlineUniformBlockFeatures->descriptorBindingInlineUniformBlockUpdateAfterBind = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES: {
-				auto* imagelessFramebufferFeatures = (VkPhysicalDeviceImagelessFramebufferFeaturesKHR*)next;
-				imagelessFramebufferFeatures->imagelessFramebuffer = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: {
-				auto* dynamicRenderingFeatures = (VkPhysicalDeviceDynamicRenderingFeatures*)next;
-				dynamicRenderingFeatures->dynamicRendering = true;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES: {
-				auto* separateDepthStencilLayoutsFeatures = (VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures*)next;
-				separateDepthStencilLayoutsFeatures->separateDepthStencilLayouts = true;
-				break;
-			}
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR: {
-                auto* barycentricFeatures = (VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR*)next;
-				barycentricFeatures->fragmentShaderBarycentric = true;
-                break;
-            }
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES: {
-                auto* bufferDeviceAddressFeatures = (VkPhysicalDeviceBufferDeviceAddressFeatures*)next;
-                bufferDeviceAddressFeatures->bufferDeviceAddress = mvkOSVersionIsAtLeast(12.05, 16.0);
-                bufferDeviceAddressFeatures->bufferDeviceAddressCaptureReplay = false;
-                bufferDeviceAddressFeatures->bufferDeviceAddressMultiDevice = false;
-            }
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT: {
-				auto* bufferDeviceAddressFeatures = (VkPhysicalDeviceBufferDeviceAddressFeaturesEXT*)next;
-				bufferDeviceAddressFeatures->bufferDeviceAddress = mvkOSVersionIsAtLeast(12.05, 16.0);
-				bufferDeviceAddressFeatures->bufferDeviceAddressCaptureReplay = false;
-				bufferDeviceAddressFeatures->bufferDeviceAddressMultiDevice = false;
-				break;
-			}
 			default:
 				break;
 		}
@@ -4134,30 +4131,7 @@
 
 #pragma mark Construction
 
-MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo) :
-	_enabledExtensions(this),
-	_enabledFeatures(),
-	_enabledStorage16Features(),
-	_enabledStorage8Features(),
-	_enabledF16I8Features(),
-	_enabledUBOLayoutFeatures(),
-	_enabledVarPtrFeatures(),
-	_enabledDescriptorIndexingFeatures(),
-	_enabledInlineUniformBlockFeatures(),
-	_enabledInterlockFeatures(),
-	_enabledHostQryResetFeatures(),
-	_enabledSamplerYcbcrConversionFeatures(),
-	_enabledPrivateDataFeatures(),
-	_enabledScalarLayoutFeatures(),
-	_enabledTexelBuffAlignFeatures(),
-	_enabledVtxAttrDivFeatures(),
-	_enabledPortabilityFeatures(),
-	_enabledImagelessFramebufferFeatures(),
-	_enabledDynamicRenderingFeatures(),
-	_enabledSeparateDepthStencilLayoutsFeatures(),
-	_enabledFragmentShaderBarycentricFeatures(),
-	_enabledBufferDeviceAddressFeatures(),
-	_enabledBufferDeviceAddressFeaturesEXT() {
+MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo) : _enabledExtensions(this) {
 
 		// If the physical device is lost, bail.
 	if (physicalDevice->getConfigurationResult() != VK_SUCCESS) {
@@ -4265,123 +4239,42 @@
 }
 
 void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) {
+	VkStructureType sType;
+	VkBaseInStructure* pPrevStruct = nullptr;
 
-	// Start with all features disabled
+	// Clear and set the sType of each VkDevice enabled feature iVar (_enabledXXXFeatures),
+	// and create a chain of identical structs that will be sent to the MVKPhysicalDevice
+	// to query which features are supported.
+#define MVK_DEVICE_FEATURE(structName, enumName, flagCount) \
+	sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##enumName##_FEATURES; \
+	mvkClear(&_enabled##structName##Features); \
+	_enabled##structName##Features.sType = sType; \
+	VkPhysicalDevice##structName##Features pd##structName##Features; \
+	pd##structName##Features.sType = sType; \
+	pd##structName##Features.pNext = pPrevStruct; \
+	pPrevStruct = (VkBaseInStructure*)&pd##structName##Features;
+
+#define MVK_DEVICE_FEATURE_EXTN(structName, enumName, extnSfx, flagCount) \
+	sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##enumName##_FEATURES_##extnSfx; \
+	mvkClear(&_enabled##structName##Features); \
+	_enabled##structName##Features.sType = sType; \
+	VkPhysicalDevice##structName##Features##extnSfx pd##structName##Features; \
+	pd##structName##Features.sType = sType; \
+	pd##structName##Features.pNext = pPrevStruct; \
+	pPrevStruct = (VkBaseInStructure*)&pd##structName##Features;
+
+#include "MVKDeviceFeatureStructs.def"
+
+	sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
 	mvkClear(&_enabledFeatures);
-	mvkClear(&_enabledStorage16Features);
-	mvkClear(&_enabledStorage8Features);
-	mvkClear(&_enabledF16I8Features);
-	mvkClear(&_enabledUBOLayoutFeatures);
-	mvkClear(&_enabledVarPtrFeatures);
-	mvkClear(&_enabledDescriptorIndexingFeatures);
-	mvkClear(&_enabledInlineUniformBlockFeatures);
-	mvkClear(&_enabledInterlockFeatures);
-	mvkClear(&_enabledHostQryResetFeatures);
-	mvkClear(&_enabledSamplerYcbcrConversionFeatures);
-	mvkClear(&_enabledPrivateDataFeatures);
-	mvkClear(&_enabledScalarLayoutFeatures);
-	mvkClear(&_enabledTexelBuffAlignFeatures);
-	mvkClear(&_enabledVtxAttrDivFeatures);
-	mvkClear(&_enabledPortabilityFeatures);
-	mvkClear(&_enabledImagelessFramebufferFeatures);
-	mvkClear(&_enabledDynamicRenderingFeatures);
-	mvkClear(&_enabledSeparateDepthStencilLayoutsFeatures);
-	mvkClear(&_enabledFragmentShaderBarycentricFeatures);
-	mvkClear(&_enabledBufferDeviceAddressFeatures);
-	mvkClear(&_enabledBufferDeviceAddressFeaturesEXT);
-
-	VkPhysicalDeviceBufferDeviceAddressFeaturesEXT pdBufferDeviceAddressFeaturesEXT;
-	pdBufferDeviceAddressFeaturesEXT.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT;
-	pdBufferDeviceAddressFeaturesEXT.pNext = nullptr;
-
-	VkPhysicalDeviceBufferDeviceAddressFeatures pdBufferDeviceAddressFeatures;
-	pdBufferDeviceAddressFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES;
-	pdBufferDeviceAddressFeatures.pNext = &pdBufferDeviceAddressFeaturesEXT;
-
-	VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR pdFragmentShaderBarycentricFeatures;
-	pdFragmentShaderBarycentricFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR;
-	pdFragmentShaderBarycentricFeatures.pNext = &pdBufferDeviceAddressFeatures;
-
-	VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures pdSeparateDepthStencilLayoutsFeatures;
-	pdSeparateDepthStencilLayoutsFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES;
-	pdSeparateDepthStencilLayoutsFeatures.pNext = &pdFragmentShaderBarycentricFeatures;
-
-	VkPhysicalDeviceDynamicRenderingFeatures pdDynamicRenderingFeatures;
-	pdDynamicRenderingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
-	pdDynamicRenderingFeatures.pNext = &pdSeparateDepthStencilLayoutsFeatures;
-
-	VkPhysicalDeviceImagelessFramebufferFeaturesKHR pdImagelessFramebufferFeatures;
-	pdImagelessFramebufferFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES;
-	pdImagelessFramebufferFeatures.pNext = &pdDynamicRenderingFeatures;
-    
-	// Fetch the available physical device features.
-	VkPhysicalDevicePortabilitySubsetFeaturesKHR pdPortabilityFeatures;
-	pdPortabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR;
-	pdPortabilityFeatures.pNext = &pdImagelessFramebufferFeatures;
-
-	VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT pdVtxAttrDivFeatures;
-	pdVtxAttrDivFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
-	pdVtxAttrDivFeatures.pNext = &pdPortabilityFeatures;
-
-	VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT pdTexelBuffAlignFeatures;
-	pdTexelBuffAlignFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT;
-	pdTexelBuffAlignFeatures.pNext = &pdVtxAttrDivFeatures;
-
-	VkPhysicalDeviceScalarBlockLayoutFeaturesEXT pdScalarLayoutFeatures;
-	pdScalarLayoutFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT;
-	pdScalarLayoutFeatures.pNext = &pdTexelBuffAlignFeatures;
-
-	VkPhysicalDevicePrivateDataFeaturesEXT pdPrivateDataFeatures;
-	pdPrivateDataFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT;
-	pdPrivateDataFeatures.pNext = &pdScalarLayoutFeatures;
-
-	VkPhysicalDeviceSamplerYcbcrConversionFeatures pdSamplerYcbcrConversionFeatures;
-	pdSamplerYcbcrConversionFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
-	pdSamplerYcbcrConversionFeatures.pNext = &pdPrivateDataFeatures;
-
-	VkPhysicalDeviceHostQueryResetFeaturesEXT pdHostQryResetFeatures;
-	pdHostQryResetFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
-	pdHostQryResetFeatures.pNext = &pdSamplerYcbcrConversionFeatures;
-
-	VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT pdInterlockFeatures;
-	pdInterlockFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT;
-	pdInterlockFeatures.pNext = &pdHostQryResetFeatures;
-
-	VkPhysicalDeviceInlineUniformBlockFeaturesEXT pdInlnUnfmBlkFeatures;
-	pdInlnUnfmBlkFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT;
-	pdInlnUnfmBlkFeatures.pNext = &pdInterlockFeatures;
-
-	VkPhysicalDeviceDescriptorIndexingFeaturesEXT pdDescIdxFeatures;
-	pdDescIdxFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
-	pdDescIdxFeatures.pNext = &pdInlnUnfmBlkFeatures;
-
-	VkPhysicalDeviceVariablePointerFeatures pdVarPtrFeatures;
-	pdVarPtrFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES;
-	pdVarPtrFeatures.pNext = &pdDescIdxFeatures;
-
-	VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR pdUBOLayoutFeatures;
-	pdUBOLayoutFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR;
-	pdUBOLayoutFeatures.pNext = &pdVarPtrFeatures;
-
-	VkPhysicalDeviceFloat16Int8FeaturesKHR pdF16I8Features;
-	pdF16I8Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
-	pdF16I8Features.pNext = &pdUBOLayoutFeatures;
-
-	VkPhysicalDevice8BitStorageFeaturesKHR pdStorage8Features;
-	pdStorage8Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR;
-	pdStorage8Features.pNext = &pdF16I8Features;
-
-	VkPhysicalDevice16BitStorageFeatures pdStorage16Features;
-	pdStorage16Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES;
-	pdStorage16Features.pNext = &pdStorage8Features;
-
 	VkPhysicalDeviceFeatures2 pdFeats2;
-	pdFeats2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
-	pdFeats2.pNext = &pdStorage16Features;
+	pdFeats2.sType = sType;
+	pdFeats2.pNext = pPrevStruct;
 
 	_physicalDevice->getFeatures(&pdFeats2);
 
-	//Enable device features based on requested and available features
+	//Enable device features based on requested and available features,
+	// including extended features that are requested in the pNext chain.
 	if (pCreateInfo->pEnabledFeatures) {
 		enableFeatures(&_enabledFeatures.robustBufferAccess,
 					   &pCreateInfo->pEnabledFeatures->robustBufferAccess,
@@ -4397,163 +4290,42 @@
 							   &pdFeats2.features.robustBufferAccess, 55);
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: {
-				auto* requestedFeatures = (VkPhysicalDevice16BitStorageFeatures*)next;
-				enableFeatures(&_enabledStorage16Features.storageBuffer16BitAccess,
-							   &requestedFeatures->storageBuffer16BitAccess,
-							   &pdStorage16Features.storageBuffer16BitAccess, 4);
-				break;
+
+#define MVK_DEVICE_FEATURE(structName, enumName, flagCount) \
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##enumName##_FEATURES: { \
+				enableFeatures((VkBaseInStructure*)&_enabled##structName##Features, \
+							   next, \
+							   (VkBaseInStructure*)&pd##structName##Features, \
+							   flagCount); \
+				break; \
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR: {
-				auto* requestedFeatures = (VkPhysicalDevice8BitStorageFeaturesKHR*)next;
-				enableFeatures(&_enabledStorage8Features.storageBuffer8BitAccess,
-							   &requestedFeatures->storageBuffer8BitAccess,
-							   &pdStorage8Features.storageBuffer8BitAccess, 3);
-				break;
+#define MVK_DEVICE_FEATURE_EXTN(structName, enumName, extnSfx, flagCount) \
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##enumName##_FEATURES_##extnSfx: { \
+				enableFeatures((VkBaseInStructure*)&_enabled##structName##Features, \
+							   next, \
+							   (VkBaseInStructure*)&pd##structName##Features, \
+							   flagCount); \
+				break; \
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR: {
-				auto* requestedFeatures = (VkPhysicalDeviceFloat16Int8FeaturesKHR*)next;
-				enableFeatures(&_enabledF16I8Features.shaderFloat16,
-							   &requestedFeatures->shaderFloat16,
-							   &pdF16I8Features.shaderFloat16, 2);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR: {
-				auto* requestedFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR*)next;
-				enableFeatures(&_enabledUBOLayoutFeatures.uniformBufferStandardLayout,
-							   &requestedFeatures->uniformBufferStandardLayout,
-							   &pdUBOLayoutFeatures.uniformBufferStandardLayout, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES: {
-				auto* requestedFeatures = (VkPhysicalDeviceVariablePointerFeatures*)next;
-				enableFeatures(&_enabledVarPtrFeatures.variablePointersStorageBuffer,
-							   &requestedFeatures->variablePointersStorageBuffer,
-							   &pdVarPtrFeatures.variablePointersStorageBuffer, 2);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceDescriptorIndexingFeaturesEXT*)next;
-				enableFeatures(&_enabledDescriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing,
-							   &requestedFeatures->shaderInputAttachmentArrayDynamicIndexing,
-							   &pdDescIdxFeatures.shaderInputAttachmentArrayDynamicIndexing, 20);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceInlineUniformBlockFeaturesEXT*)next;
-				enableFeatures(&_enabledInlineUniformBlockFeatures.inlineUniformBlock,
-							   &requestedFeatures->inlineUniformBlock,
-							   &pdInlnUnfmBlkFeatures.inlineUniformBlock, 2);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT*)next;
-				enableFeatures(&_enabledInterlockFeatures.fragmentShaderSampleInterlock,
-							   &requestedFeatures->fragmentShaderSampleInterlock,
-							   &pdInterlockFeatures.fragmentShaderSampleInterlock, 3);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceHostQueryResetFeaturesEXT*)next;
-				enableFeatures(&_enabledHostQryResetFeatures.hostQueryReset,
-							   &requestedFeatures->hostQueryReset,
-							   &pdHostQryResetFeatures.hostQueryReset, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: {
-				auto* requestedFeatures = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*)next;
-				enableFeatures(&_enabledSamplerYcbcrConversionFeatures.samplerYcbcrConversion,
-							   &requestedFeatures->samplerYcbcrConversion,
-							   &pdSamplerYcbcrConversionFeatures.samplerYcbcrConversion, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDevicePrivateDataFeaturesEXT*)next;
-				enableFeatures(&_enabledPrivateDataFeatures.privateData,
-							   &requestedFeatures->privateData,
-							   &pdPrivateDataFeatures.privateData, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceScalarBlockLayoutFeaturesEXT*)next;
-				enableFeatures(&_enabledScalarLayoutFeatures.scalarBlockLayout,
-							   &requestedFeatures->scalarBlockLayout,
-							   &pdScalarLayoutFeatures.scalarBlockLayout, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT*)next;
-				enableFeatures(&_enabledTexelBuffAlignFeatures.texelBufferAlignment,
-							   &requestedFeatures->texelBufferAlignment,
-							   &pdTexelBuffAlignFeatures.texelBufferAlignment, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT*)next;
-				enableFeatures(&_enabledVtxAttrDivFeatures.vertexAttributeInstanceRateDivisor,
-							   &requestedFeatures->vertexAttributeInstanceRateDivisor,
-							   &pdVtxAttrDivFeatures.vertexAttributeInstanceRateDivisor, 2);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR: {
-				auto* requestedFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesKHR*)next;
-				enableFeatures(&_enabledPortabilityFeatures.constantAlphaColorBlendFactors,
-							   &requestedFeatures->constantAlphaColorBlendFactors,
-							   &pdPortabilityFeatures.constantAlphaColorBlendFactors, 15);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES: {
-				auto* requestedFeatures = (VkPhysicalDeviceImagelessFramebufferFeaturesKHR*)next;
-				enableFeatures(&_enabledImagelessFramebufferFeatures.imagelessFramebuffer,
-							   &requestedFeatures->imagelessFramebuffer,
-							   &pdImagelessFramebufferFeatures.imagelessFramebuffer, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: {
-				auto* requestedFeatures = (VkPhysicalDeviceDynamicRenderingFeatures*)next;
-				enableFeatures(&_enabledDynamicRenderingFeatures.dynamicRendering,
-							   &requestedFeatures->dynamicRendering,
-							   &pdDynamicRenderingFeatures.dynamicRendering, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES: {
-				auto* requestedFeatures = (VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures*)next;
-				enableFeatures(&_enabledSeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts,
-							   &requestedFeatures->separateDepthStencilLayouts,
-							   &pdSeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR: {
-				auto* requestedFeatures = (VkPhysicalDeviceFragmentShaderBarycentricFeaturesKHR*)next;
-				enableFeatures(&_enabledFragmentShaderBarycentricFeatures.fragmentShaderBarycentric,
-							   &requestedFeatures->fragmentShaderBarycentric,
-							   &pdFragmentShaderBarycentricFeatures.fragmentShaderBarycentric, 1);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES: {
-				auto* requestedFeatures = (VkPhysicalDeviceBufferDeviceAddressFeatures*)next;
-				enableFeatures(&_enabledBufferDeviceAddressFeatures.bufferDeviceAddress,
-							   &requestedFeatures->bufferDeviceAddress,
-							   &pdBufferDeviceAddressFeatures.bufferDeviceAddress, 3);
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT: {
-				auto* requestedFeatures = (VkPhysicalDeviceBufferDeviceAddressFeaturesEXT*)next;
-				enableFeatures(&_enabledBufferDeviceAddressFeaturesEXT.bufferDeviceAddress,
-							   &requestedFeatures->bufferDeviceAddress,
-							   &pdBufferDeviceAddressFeaturesEXT.bufferDeviceAddress, 3);
-				break;
-			}
+#include "MVKDeviceFeatureStructs.def"
+
 			default:
 				break;
 		}
 	}
 }
 
-void MVKDevice::enableFeatures(const VkBool32* pEnable, const VkBool32* pRequested, const VkBool32* pAvailable, uint32_t count) {
+void MVKDevice::enableFeatures(VkBaseInStructure* pEnabled, const VkBaseInStructure* pRequested, const VkBaseInStructure* pAvailable, uint32_t count) {
+	enableFeatures((VkBool32*)(&(pEnabled->pNext) + 1),
+				   (VkBool32*)(&(pRequested->pNext) + 1),
+				   (VkBool32*)(&(pAvailable->pNext) + 1),
+				   count);
+}
+
+void MVKDevice::enableFeatures(VkBool32* pEnabledBools, const VkBool32* pRequestedBools, const VkBool32* pAvailableBools, uint32_t count) {
 	for (uint32_t i = 0; i < count; i++) {
-		((VkBool32*)pEnable)[i] = pRequested[i] && pAvailable[i];
-		if (pRequested[i] && !pAvailable[i]) {
+		pEnabledBools[i] = pRequestedBools[i] && pAvailableBools[i];
+		if (pRequestedBools[i] && !pAvailableBools[i]) {
 			setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateDevice(): Requested feature is not available on this device."));
 		}
 	}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceFeatureStructs.def b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceFeatureStructs.def
new file mode 100644
index 0000000..dba229d
--- /dev/null
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceFeatureStructs.def
@@ -0,0 +1,71 @@
+/*
+ * MVKDeviceFeatureStructs.def
+ *
+ * Copyright (c) 2015-2022 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// To use this file, define the macro MVK_DEVICE_FEATURE and optionally
+// MVK_DEVICE_FEATURE_EXTN if needed, then #include this file.
+
+// To add a new device feature struct, simply add a line below.
+// The order is the typical approach of alphabetical but separated
+// into sections based on Vulkan extensions.
+
+#ifndef MVK_DEVICE_FEATURE
+#error MVK_DEVICE_FEATURE must be defined before including this file
+#endif
+#ifndef MVK_DEVICE_FEATURE_EXTN
+#define MVK_DEVICE_FEATURE_EXTN(structName, enumName, extnSfx, flagCount) MVK_DEVICE_FEATURE(structName, enumName, flagCount)
+#endif
+
+// INTEL is a common macro defined elsewhere in the system.
+// We need to temporarily undefine it so that it doesn't expand when used in a macro below.
+#pragma push_macro("INTEL")
+#undef INTEL
+
+MVK_DEVICE_FEATURE(16BitStorage,                   16BIT_STORAGE,                      4)
+MVK_DEVICE_FEATURE(8BitStorage,                    8BIT_STORAGE,                       3)
+MVK_DEVICE_FEATURE(BufferDeviceAddress,            BUFFER_DEVICE_ADDRESS,              3)
+MVK_DEVICE_FEATURE(DescriptorIndexing,             DESCRIPTOR_INDEXING,               20)
+MVK_DEVICE_FEATURE(DynamicRendering,               DYNAMIC_RENDERING,                  1)
+MVK_DEVICE_FEATURE(HostQueryReset,                 HOST_QUERY_RESET,                   1)
+MVK_DEVICE_FEATURE(ImagelessFramebuffer,           IMAGELESS_FRAMEBUFFER,              1)
+MVK_DEVICE_FEATURE(ImageRobustness,                IMAGE_ROBUSTNESS,                   1)
+MVK_DEVICE_FEATURE(InlineUniformBlock,             INLINE_UNIFORM_BLOCK,               2)
+MVK_DEVICE_FEATURE(Multiview,                      MULTIVIEW,                          3)
+MVK_DEVICE_FEATURE(PrivateData,                    PRIVATE_DATA,                       1)
+MVK_DEVICE_FEATURE(ProtectedMemory,                PROTECTED_MEMORY,                   1)
+MVK_DEVICE_FEATURE(SamplerYcbcrConversion,         SAMPLER_YCBCR_CONVERSION,           1)
+MVK_DEVICE_FEATURE(ScalarBlockLayout,              SCALAR_BLOCK_LAYOUT,                1)
+MVK_DEVICE_FEATURE(SeparateDepthStencilLayouts,    SEPARATE_DEPTH_STENCIL_LAYOUTS,     1)
+MVK_DEVICE_FEATURE(ShaderDrawParameters,           SHADER_DRAW_PARAMETERS,             1)
+MVK_DEVICE_FEATURE(ShaderFloat16Int8,              SHADER_FLOAT16_INT8,                2)
+MVK_DEVICE_FEATURE(ShaderSubgroupExtendedTypes,    SHADER_SUBGROUP_EXTENDED_TYPES,     1)
+MVK_DEVICE_FEATURE(SubgroupSizeControl,            SUBGROUP_SIZE_CONTROL,              2)
+MVK_DEVICE_FEATURE(TextureCompressionASTCHDR,      TEXTURE_COMPRESSION_ASTC_HDR,       1)
+MVK_DEVICE_FEATURE(TimelineSemaphore,              TIMELINE_SEMAPHORE,                 1)
+MVK_DEVICE_FEATURE(UniformBufferStandardLayout,    UNIFORM_BUFFER_STANDARD_LAYOUT,     1)
+MVK_DEVICE_FEATURE(VariablePointer,                VARIABLE_POINTER,                   2)
+MVK_DEVICE_FEATURE_EXTN(FragmentShaderBarycentric, FRAGMENT_SHADER_BARYCENTRIC, KHR,   1)
+MVK_DEVICE_FEATURE_EXTN(PortabilitySubset,         PORTABILITY_SUBSET,          KHR,  15)
+MVK_DEVICE_FEATURE_EXTN(FragmentShaderInterlock,   FRAGMENT_SHADER_INTERLOCK,   EXT,   3)
+MVK_DEVICE_FEATURE_EXTN(Robustness2,               ROBUSTNESS_2,                EXT,   3)
+MVK_DEVICE_FEATURE_EXTN(TexelBufferAlignment,      TEXEL_BUFFER_ALIGNMENT,      EXT,   1)
+MVK_DEVICE_FEATURE_EXTN(VertexAttributeDivisor,    VERTEX_ATTRIBUTE_DIVISOR,    EXT,   2)
+MVK_DEVICE_FEATURE_EXTN(ShaderIntegerFunctions2,   SHADER_INTEGER_FUNCTIONS_2,  INTEL, 1)
+
+#pragma pop_macro("INTEL")
+#undef MVK_DEVICE_FEATURE_EXTN
+#undef MVK_DEVICE_FEATURE