Merge pull request #1675 from billhollings/support-vk12-device-feat-prop-structs

Support Vulkan 1.2 device feature and property structs.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index 7ada59a..c88d65e 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -398,7 +398,8 @@
 	void initPipelineCacheUUID();
 	uint32_t getHighestGPUCapability();
 	uint32_t getMoltenVKGitRevision();
-	void populate(VkPhysicalDeviceIDProperties* pDevIdProps);
+	void populateDeviceIDProperties(VkPhysicalDeviceVulkan11Properties* pVk11Props);
+	void populateSubgroupProperties(VkPhysicalDeviceVulkan11Properties* pVk11Props);
 	void logGPUInfo();
 
 	id<MTLDevice> _mtlDevice;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 30a49d5..763e639 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -93,57 +93,143 @@
 }
 
 void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
+
+	// Create a SSOT for these Vulkan 1.1 features, which can be queried via two mechanisms here.
+	VkPhysicalDeviceVulkan11Features supportedFeats11 = {
+		.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
+		.pNext = nullptr,
+		.storageBuffer16BitAccess = true,
+		.uniformAndStorageBuffer16BitAccess = true,
+		.storagePushConstant16 = true,
+		.storageInputOutput16 = true,
+		.multiview = true,
+		.multiviewGeometryShader = false,
+		.multiviewTessellationShader = false,		// FIXME
+		.variablePointersStorageBuffer = true,
+		.variablePointers = true,
+		.protectedMemory = false,
+		.samplerYcbcrConversion = true,
+		.shaderDrawParameters = true,
+	};
+
+	// Create a SSOT for these Vulkan 1.2 features, which can be queried via two mechanisms here.
+	VkPhysicalDeviceVulkan12Features supportedFeats12 = {
+		.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
+		.pNext = nullptr,
+		.samplerMirrorClampToEdge = _metalFeatures.samplerMirrorClampToEdge,
+		.drawIndirectCount = false,									// VK_KHR_draw_indirect_count
+		.storageBuffer8BitAccess = true,
+		.uniformAndStorageBuffer8BitAccess = true,
+		.storagePushConstant8 = true,
+		.shaderBufferInt64Atomics = false,							// VK_KHR_shader_atomic_int64
+		.shaderSharedInt64Atomics = false,							// VK_KHR_shader_atomic_int64
+		.shaderFloat16 = true,
+		.shaderInt8 = true,
+		.descriptorIndexing = false,		// Requires _metalFeatures.arrayOfTextures && _metalFeatures.arrayOfSamplers && shaderStorageBufferArrayNonUniformIndexing
+		.shaderInputAttachmentArrayDynamicIndexing = _metalFeatures.arrayOfTextures,
+		.shaderUniformTexelBufferArrayDynamicIndexing = _metalFeatures.arrayOfTextures,
+		.shaderStorageTexelBufferArrayDynamicIndexing = _metalFeatures.arrayOfTextures,
+		.shaderUniformBufferArrayNonUniformIndexing = false,
+		.shaderSampledImageArrayNonUniformIndexing = _metalFeatures.arrayOfTextures && _metalFeatures.arrayOfSamplers,
+		.shaderStorageBufferArrayNonUniformIndexing = false,
+		.shaderStorageImageArrayNonUniformIndexing = _metalFeatures.arrayOfTextures,
+		.shaderInputAttachmentArrayNonUniformIndexing = _metalFeatures.arrayOfTextures,
+		.shaderUniformTexelBufferArrayNonUniformIndexing = _metalFeatures.arrayOfTextures,
+		.shaderStorageTexelBufferArrayNonUniformIndexing = _metalFeatures.arrayOfTextures,
+		.descriptorBindingUniformBufferUpdateAfterBind = true,
+		.descriptorBindingSampledImageUpdateAfterBind = true,
+		.descriptorBindingStorageImageUpdateAfterBind = true,
+		.descriptorBindingStorageBufferUpdateAfterBind = true,
+		.descriptorBindingUniformTexelBufferUpdateAfterBind = true,
+		.descriptorBindingStorageTexelBufferUpdateAfterBind = true,
+		.descriptorBindingUpdateUnusedWhilePending = true,
+		.descriptorBindingPartiallyBound = true,
+		.descriptorBindingVariableDescriptorCount = true,
+		.runtimeDescriptorArray = true,
+		.samplerFilterMinmax = false,								// VK_EXT_sampler_filter_minmax
+		.scalarBlockLayout = true,
+		.imagelessFramebuffer = true,
+		.uniformBufferStandardLayout = true,
+		.shaderSubgroupExtendedTypes = _metalFeatures.simdPermute || _metalFeatures.quadPermute,
+		.separateDepthStencilLayouts = true,
+		.hostQueryReset = true,
+		.timelineSemaphore = true,
+		.bufferDeviceAddress = mvkOSVersionIsAtLeast(12.05, 16.0),
+		.bufferDeviceAddressCaptureReplay = false,
+		.bufferDeviceAddressMultiDevice = false,
+		.vulkanMemoryModel = false,									// VK_KHR_vulkan_memory_model
+		.vulkanMemoryModelDeviceScope = false,						// VK_KHR_vulkan_memory_model
+		.vulkanMemoryModelAvailabilityVisibilityChains = false,		// VK_KHR_vulkan_memory_model
+		.shaderOutputViewportIndex = true,
+		.shaderOutputLayer = true,
+		.subgroupBroadcastDynamicId = true,
+	};
+
 	features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
 	features->features = _features;
 	for (auto* next = (VkBaseOutStructure*)features->pNext; next; next = next->pNext) {
 		switch ((uint32_t)next->sType) {
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES: {
+				// Copy from supportedFeats11, but keep pNext as is.
+				auto* pFeats11 = (VkPhysicalDeviceVulkan11Features*)next;
+				supportedFeats11.pNext = pFeats11->pNext;
+				*pFeats11 = supportedFeats11;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: {
+				// Copy from supportedFeats12, but keep pNext as is.
+				auto* pFeats12 = (VkPhysicalDeviceVulkan12Features*)next;
+				supportedFeats12.pNext = pFeats12->pNext;
+				*pFeats12 = supportedFeats12;
+				break;
+			}
 
 			// 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;
-				storageFeatures->uniformAndStorageBuffer16BitAccess = true;
-				storageFeatures->storagePushConstant16 = true;
-				storageFeatures->storageInputOutput16 = true;
+				storageFeatures->storageBuffer16BitAccess = supportedFeats11.storageBuffer16BitAccess;
+				storageFeatures->uniformAndStorageBuffer16BitAccess = supportedFeats11.uniformAndStorageBuffer16BitAccess;
+				storageFeatures->storagePushConstant16 = supportedFeats11.storagePushConstant16;
+				storageFeatures->storageInputOutput16 = supportedFeats11.storageInputOutput16;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES: {
 				auto* storageFeatures = (VkPhysicalDevice8BitStorageFeatures*)next;
-				storageFeatures->storageBuffer8BitAccess = true;
-				storageFeatures->uniformAndStorageBuffer8BitAccess = true;
-				storageFeatures->storagePushConstant8 = true;
+				storageFeatures->storageBuffer8BitAccess = supportedFeats12.storagePushConstant8;
+				storageFeatures->uniformAndStorageBuffer8BitAccess = supportedFeats12.storagePushConstant8;
+				storageFeatures->storagePushConstant8 = supportedFeats12.storagePushConstant8;
 				break;
 			}
 			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;
+				bufferDeviceAddressFeatures->bufferDeviceAddress = supportedFeats12.bufferDeviceAddress;
+				bufferDeviceAddressFeatures->bufferDeviceAddressCaptureReplay = supportedFeats12.bufferDeviceAddressCaptureReplay;
+				bufferDeviceAddressFeatures->bufferDeviceAddressMultiDevice = supportedFeats12.bufferDeviceAddressMultiDevice;
 				break;
 			}
 			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;
-				pDescIdxFeatures->shaderUniformBufferArrayNonUniformIndexing = false;
-				pDescIdxFeatures->shaderSampledImageArrayNonUniformIndexing = _metalFeatures.arrayOfTextures && _metalFeatures.arrayOfSamplers;
-				pDescIdxFeatures->shaderStorageBufferArrayNonUniformIndexing = false;
-				pDescIdxFeatures->shaderStorageImageArrayNonUniformIndexing = _metalFeatures.arrayOfTextures;
-				pDescIdxFeatures->shaderInputAttachmentArrayNonUniformIndexing = _metalFeatures.arrayOfTextures;
-				pDescIdxFeatures->shaderUniformTexelBufferArrayNonUniformIndexing = _metalFeatures.arrayOfTextures;
-				pDescIdxFeatures->shaderStorageTexelBufferArrayNonUniformIndexing = _metalFeatures.arrayOfTextures;
-				pDescIdxFeatures->descriptorBindingUniformBufferUpdateAfterBind = true;
-				pDescIdxFeatures->descriptorBindingSampledImageUpdateAfterBind = true;
-				pDescIdxFeatures->descriptorBindingStorageImageUpdateAfterBind = true;
-				pDescIdxFeatures->descriptorBindingStorageBufferUpdateAfterBind = true;
-				pDescIdxFeatures->descriptorBindingUniformTexelBufferUpdateAfterBind = true;
-				pDescIdxFeatures->descriptorBindingStorageTexelBufferUpdateAfterBind = true;
-				pDescIdxFeatures->descriptorBindingUpdateUnusedWhilePending = true;
-				pDescIdxFeatures->descriptorBindingPartiallyBound = true;
-				pDescIdxFeatures->descriptorBindingVariableDescriptorCount = true;
-				pDescIdxFeatures->runtimeDescriptorArray = true;
+				pDescIdxFeatures->shaderInputAttachmentArrayDynamicIndexing = supportedFeats12.shaderInputAttachmentArrayDynamicIndexing;
+				pDescIdxFeatures->shaderUniformTexelBufferArrayDynamicIndexing = supportedFeats12.shaderUniformTexelBufferArrayDynamicIndexing;
+				pDescIdxFeatures->shaderStorageTexelBufferArrayDynamicIndexing = supportedFeats12.shaderStorageTexelBufferArrayDynamicIndexing;
+				pDescIdxFeatures->shaderUniformBufferArrayNonUniformIndexing = supportedFeats12.shaderUniformBufferArrayNonUniformIndexing;
+				pDescIdxFeatures->shaderSampledImageArrayNonUniformIndexing = supportedFeats12.shaderSampledImageArrayNonUniformIndexing;
+				pDescIdxFeatures->shaderStorageBufferArrayNonUniformIndexing = supportedFeats12.shaderStorageBufferArrayNonUniformIndexing;
+				pDescIdxFeatures->shaderStorageImageArrayNonUniformIndexing = supportedFeats12.shaderStorageImageArrayNonUniformIndexing;
+				pDescIdxFeatures->shaderInputAttachmentArrayNonUniformIndexing = supportedFeats12.shaderInputAttachmentArrayNonUniformIndexing;
+				pDescIdxFeatures->shaderUniformTexelBufferArrayNonUniformIndexing = supportedFeats12.shaderUniformTexelBufferArrayNonUniformIndexing;
+				pDescIdxFeatures->shaderStorageTexelBufferArrayNonUniformIndexing = supportedFeats12.shaderStorageTexelBufferArrayNonUniformIndexing;
+				pDescIdxFeatures->descriptorBindingUniformBufferUpdateAfterBind = supportedFeats12.descriptorBindingUniformBufferUpdateAfterBind;
+				pDescIdxFeatures->descriptorBindingSampledImageUpdateAfterBind = supportedFeats12.descriptorBindingSampledImageUpdateAfterBind;
+				pDescIdxFeatures->descriptorBindingStorageImageUpdateAfterBind = supportedFeats12.descriptorBindingStorageImageUpdateAfterBind;
+				pDescIdxFeatures->descriptorBindingStorageBufferUpdateAfterBind = supportedFeats12.descriptorBindingStorageBufferUpdateAfterBind;
+				pDescIdxFeatures->descriptorBindingUniformTexelBufferUpdateAfterBind = supportedFeats12.descriptorBindingUniformTexelBufferUpdateAfterBind;
+				pDescIdxFeatures->descriptorBindingStorageTexelBufferUpdateAfterBind = supportedFeats12.descriptorBindingStorageTexelBufferUpdateAfterBind;
+				pDescIdxFeatures->descriptorBindingUpdateUnusedWhilePending = supportedFeats12.descriptorBindingUpdateUnusedWhilePending;
+				pDescIdxFeatures->descriptorBindingPartiallyBound = supportedFeats12.descriptorBindingPartiallyBound;
+				pDescIdxFeatures->descriptorBindingVariableDescriptorCount = supportedFeats12.descriptorBindingVariableDescriptorCount;
+				pDescIdxFeatures->runtimeDescriptorArray = supportedFeats12.runtimeDescriptorArray;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: {
@@ -153,12 +239,12 @@
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES: {
 				auto* hostQueryResetFeatures = (VkPhysicalDeviceHostQueryResetFeatures*)next;
-				hostQueryResetFeatures->hostQueryReset = true;
+				hostQueryResetFeatures->hostQueryReset = supportedFeats12.hostQueryReset;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES: {
 				auto* imagelessFramebufferFeatures = (VkPhysicalDeviceImagelessFramebufferFeatures*)next;
-				imagelessFramebufferFeatures->imagelessFramebuffer = true;
+				imagelessFramebufferFeatures->imagelessFramebuffer = supportedFeats12.imagelessFramebuffer;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_ROBUSTNESS_FEATURES: {
@@ -174,9 +260,9 @@
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES: {
 				auto* multiviewFeatures = (VkPhysicalDeviceMultiviewFeatures*)next;
-				multiviewFeatures->multiview = true;
-				multiviewFeatures->multiviewGeometryShader = false;
-				multiviewFeatures->multiviewTessellationShader = false;		// FIXME
+				multiviewFeatures->multiview = supportedFeats11.multiview;
+				multiviewFeatures->multiviewGeometryShader = supportedFeats11.multiviewGeometryShader;
+				multiviewFeatures->multiviewTessellationShader = supportedFeats11.multiviewTessellationShader;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES: {
@@ -186,38 +272,38 @@
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES: {
 				auto* protectedMemFeatures = (VkPhysicalDeviceProtectedMemoryFeatures*)next;
-				protectedMemFeatures->protectedMemory = false;
+				protectedMemFeatures->protectedMemory = supportedFeats11.protectedMemory;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES: {
 				auto* samplerYcbcrConvFeatures = (VkPhysicalDeviceSamplerYcbcrConversionFeatures*)next;
-				samplerYcbcrConvFeatures->samplerYcbcrConversion = true;
+				samplerYcbcrConvFeatures->samplerYcbcrConversion = supportedFeats11.samplerYcbcrConversion;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES: {
 				auto* scalarLayoutFeatures = (VkPhysicalDeviceScalarBlockLayoutFeatures*)next;
-				scalarLayoutFeatures->scalarBlockLayout = true;
+				scalarLayoutFeatures->scalarBlockLayout = supportedFeats12.scalarBlockLayout;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SEPARATE_DEPTH_STENCIL_LAYOUTS_FEATURES: {
 				auto* separateDepthStencilLayoutsFeatures = (VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures*)next;
-				separateDepthStencilLayoutsFeatures->separateDepthStencilLayouts = true;
+				separateDepthStencilLayoutsFeatures->separateDepthStencilLayouts = supportedFeats12.separateDepthStencilLayouts;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES: {
 				auto* shaderDrawParamsFeatures = (VkPhysicalDeviceShaderDrawParametersFeatures*)next;
-				shaderDrawParamsFeatures->shaderDrawParameters = true;
+				shaderDrawParamsFeatures->shaderDrawParameters = supportedFeats11.shaderDrawParameters;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES: {
 				auto* f16Features = (VkPhysicalDeviceShaderFloat16Int8Features*)next;
-				f16Features->shaderFloat16 = true;
-				f16Features->shaderInt8 = true;
+				f16Features->shaderFloat16 = supportedFeats12.shaderFloat16;
+				f16Features->shaderInt8 = supportedFeats12.shaderInt8;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES: {
 				auto* shaderSGTypesFeatures = (VkPhysicalDeviceShaderSubgroupExtendedTypesFeatures*)next;
-				shaderSGTypesFeatures->shaderSubgroupExtendedTypes = _metalFeatures.simdPermute || _metalFeatures.quadPermute;
+				shaderSGTypesFeatures->shaderSubgroupExtendedTypes = supportedFeats12.shaderSubgroupExtendedTypes;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES: {
@@ -233,18 +319,18 @@
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES: {
 				auto* timelineSem4Features = (VkPhysicalDeviceTimelineSemaphoreFeatures*)next;
-				timelineSem4Features->timelineSemaphore = true;
+				timelineSem4Features->timelineSemaphore = supportedFeats12.timelineSemaphore;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES: {
 				auto* uboLayoutFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeatures*)next;
-				uboLayoutFeatures->uniformBufferStandardLayout = true;
+				uboLayoutFeatures->uniformBufferStandardLayout = supportedFeats12.uniformBufferStandardLayout;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES: {
 				auto* varPtrFeatures = (VkPhysicalDeviceVariablePointerFeatures*)next;
-				varPtrFeatures->variablePointersStorageBuffer = true;
-				varPtrFeatures->variablePointers = true;
+				varPtrFeatures->variablePointersStorageBuffer = supportedFeats11.variablePointersStorageBuffer;
+				varPtrFeatures->variablePointers = supportedFeats11.variablePointers;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_KHR: {
@@ -313,136 +399,195 @@
 }
 
 void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) {
+	uint32_t uintMax = std::numeric_limits<uint32_t>::max();
+	uint32_t maxSamplerCnt = getMaxSamplerCount();
+	bool isTier2 = isUsingMetalArgumentBuffers() && (_metalFeatures.argumentBuffersTier >= MTLArgumentBuffersTier2);
+
+	// Create a SSOT for these Vulkan 1.1 properties, which can be queried via two mechanisms here.
+	VkPhysicalDeviceVulkan11Properties supportedProps11;
+	supportedProps11.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES;
+	supportedProps11.pNext = nullptr;
+	populateDeviceIDProperties(&supportedProps11);
+	populateSubgroupProperties(&supportedProps11);
+	supportedProps11.pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES;
+	supportedProps11.maxMultiviewViewCount = 32;
+	supportedProps11.maxMultiviewInstanceIndex = canUseInstancingForMultiview() ? uintMax / 32 : uintMax;
+	supportedProps11.protectedNoFault = false;
+	supportedProps11.maxPerSetDescriptors = 4 * (_metalFeatures.maxPerStageBufferCount +
+												 _metalFeatures.maxPerStageTextureCount +
+												 _metalFeatures.maxPerStageSamplerCount);
+	supportedProps11.maxMemoryAllocationSize = _metalFeatures.maxMTLBufferSize;
+
+	// Create a SSOT for these Vulkan 1.2 properties, which can be queried via two mechanisms here.
+	VkPhysicalDeviceVulkan12Properties supportedProps12;
+	supportedProps12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES;
+	supportedProps12.pNext = nullptr;
+	supportedProps12.driverID = VK_DRIVER_ID_MOLTENVK;
+	strcpy(supportedProps12.driverName, "MoltenVK");
+	strcpy(supportedProps12.driverInfo, mvkGetMoltenVKVersionString(MVK_VERSION).c_str());
+	supportedProps12.conformanceVersion.major = 0;
+	supportedProps12.conformanceVersion.minor = 0;
+	supportedProps12.conformanceVersion.subminor = 0;
+	supportedProps12.conformanceVersion.patch = 0;
+	supportedProps12.denormBehaviorIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY;	// VK_KHR_shader_float_controls
+	supportedProps12.roundingModeIndependence = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY;		// VK_KHR_shader_float_controls
+	supportedProps12.shaderSignedZeroInfNanPreserveFloat16 = false;										// VK_KHR_shader_float_controls
+	supportedProps12.shaderSignedZeroInfNanPreserveFloat32 = false;										// VK_KHR_shader_float_controls
+	supportedProps12.shaderSignedZeroInfNanPreserveFloat64 = false;										// VK_KHR_shader_float_controls
+	supportedProps12.shaderDenormPreserveFloat16 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderDenormPreserveFloat32 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderDenormPreserveFloat64 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderDenormFlushToZeroFloat16 = false;											// VK_KHR_shader_float_controls
+	supportedProps12.shaderDenormFlushToZeroFloat32 = false;											// VK_KHR_shader_float_controls
+	supportedProps12.shaderDenormFlushToZeroFloat64 = false;											// VK_KHR_shader_float_controls
+	supportedProps12.shaderRoundingModeRTEFloat16 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderRoundingModeRTEFloat32 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderRoundingModeRTEFloat64 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderRoundingModeRTZFloat16 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderRoundingModeRTZFloat32 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.shaderRoundingModeRTZFloat64 = false;												// VK_KHR_shader_float_controls
+	supportedProps12.maxUpdateAfterBindDescriptorsInAllPools				= kMVKUndefinedLargeUInt32;
+	supportedProps12.shaderUniformBufferArrayNonUniformIndexingNative		= false;
+	supportedProps12.shaderSampledImageArrayNonUniformIndexingNative		= _metalFeatures.arrayOfTextures && _metalFeatures.arrayOfSamplers;
+	supportedProps12.shaderStorageBufferArrayNonUniformIndexingNative		= false;
+	supportedProps12.shaderStorageImageArrayNonUniformIndexingNative		= _metalFeatures.arrayOfTextures;
+	supportedProps12.shaderInputAttachmentArrayNonUniformIndexingNative		= _metalFeatures.arrayOfTextures;
+	supportedProps12.robustBufferAccessUpdateAfterBind						= _features.robustBufferAccess;
+	supportedProps12.quadDivergentImplicitLod								= false;
+	supportedProps12.maxPerStageDescriptorUpdateAfterBindSamplers			= isTier2 ? maxSamplerCnt : _properties.limits.maxPerStageDescriptorSamplers;
+	supportedProps12.maxPerStageDescriptorUpdateAfterBindUniformBuffers		= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorUniformBuffers;
+	supportedProps12.maxPerStageDescriptorUpdateAfterBindStorageBuffers		= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorStorageBuffers;
+	supportedProps12.maxPerStageDescriptorUpdateAfterBindSampledImages		= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorSampledImages;
+	supportedProps12.maxPerStageDescriptorUpdateAfterBindStorageImages		= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorStorageImages;
+	supportedProps12.maxPerStageDescriptorUpdateAfterBindInputAttachments	= _properties.limits.maxPerStageDescriptorInputAttachments;
+	supportedProps12.maxPerStageUpdateAfterBindResources					= isTier2 ? 500000 : _properties.limits.maxPerStageResources;
+	supportedProps12.maxDescriptorSetUpdateAfterBindSamplers				= isTier2 ? maxSamplerCnt : _properties.limits.maxDescriptorSetSamplers;
+	supportedProps12.maxDescriptorSetUpdateAfterBindUniformBuffers			= isTier2 ? 500000 : _properties.limits.maxDescriptorSetUniformBuffers;
+	supportedProps12.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic	= isTier2 ? 500000 : _properties.limits.maxDescriptorSetUniformBuffersDynamic;
+	supportedProps12.maxDescriptorSetUpdateAfterBindStorageBuffers			= isTier2 ? 500000 : _properties.limits.maxDescriptorSetStorageBuffers;
+	supportedProps12.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic	= isTier2 ? 500000 : _properties.limits.maxDescriptorSetStorageBuffersDynamic;
+	supportedProps12.maxDescriptorSetUpdateAfterBindSampledImages			= isTier2 ? 500000 : _properties.limits.maxDescriptorSetSampledImages;
+	supportedProps12.maxDescriptorSetUpdateAfterBindStorageImages			= isTier2 ? 500000 : _properties.limits.maxDescriptorSetStorageImages;
+	supportedProps12.maxDescriptorSetUpdateAfterBindInputAttachments		= _properties.limits.maxDescriptorSetInputAttachments;
+	supportedProps12.supportedDepthResolveModes = (_metalFeatures.depthResolve
+												   ? VK_RESOLVE_MODE_SAMPLE_ZERO_BIT | VK_RESOLVE_MODE_MIN_BIT | VK_RESOLVE_MODE_MAX_BIT
+												   : VK_RESOLVE_MODE_SAMPLE_ZERO_BIT);
+	supportedProps12.supportedStencilResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;	// Metal allows you to set the stencil resolve filter to either Sample0 or the same sample used for depth resolve. This is impossible to express in Vulkan.
+	supportedProps12.independentResolveNone = true;
+	supportedProps12.independentResolve = true;
+	supportedProps12.filterMinmaxSingleComponentFormats = false;			// VK_EXT_sampler_filter_minmax;
+	supportedProps12.filterMinmaxImageComponentMapping = false;				// VK_EXT_sampler_filter_minmax;
+	supportedProps12.maxTimelineSemaphoreValueDifference = std::numeric_limits<uint64_t>::max();
+	supportedProps12.framebufferIntegerColorSampleCounts = _metalFeatures.supportedSampleCounts;
+
 	properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
 	properties->properties = _properties;
 	for (auto* next = (VkBaseOutStructure*)properties->pNext; next; next = next->pNext) {
 		switch ((uint32_t)next->sType) {
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES: {
-				auto* depthStencilResolveProps = (VkPhysicalDeviceDepthStencilResolveProperties*)next;
-
-				// We can always support resolve from sample zero. Other modes require additional capabilities.
-				depthStencilResolveProps->supportedDepthResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
-				if (_metalFeatures.depthResolve) {
-					depthStencilResolveProps->supportedDepthResolveModes |= VK_RESOLVE_MODE_MIN_BIT | VK_RESOLVE_MODE_MAX_BIT;
-				}
-				// Metal allows you to set the stencil resolve filter to either
-				// Sample0 or DepthResolvedSample--in other words, you can always use sample 0,
-				// but you can also use the sample chosen for depth resolve. This is impossible
-				// to express in Vulkan.
-				depthStencilResolveProps->supportedStencilResolveModes = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
-				depthStencilResolveProps->independentResolveNone = true;
-				depthStencilResolveProps->independentResolve = true;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_PROPERTIES: {
+				// Copy from supportedProps11, but keep pNext as is.
+				auto* pProps11 = (VkPhysicalDeviceVulkan11Properties*)next;
+				supportedProps11.pNext = pProps11->pNext;
+				*pProps11 = supportedProps11;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES: {
-				auto* physicalDeviceDriverProps = (VkPhysicalDeviceDriverPropertiesKHR*)next;
-				strcpy(physicalDeviceDriverProps->driverName, "MoltenVK");
-				strcpy(physicalDeviceDriverProps->driverInfo, mvkGetMoltenVKVersionString(MVK_VERSION).c_str());
-				physicalDeviceDriverProps->driverID = VK_DRIVER_ID_MOLTENVK;
-				physicalDeviceDriverProps->conformanceVersion.major = 0;
-				physicalDeviceDriverProps->conformanceVersion.minor = 0;
-				physicalDeviceDriverProps->conformanceVersion.subminor = 0;
-				physicalDeviceDriverProps->conformanceVersion.patch = 0;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_PROPERTIES: {
+				// Copy from supportedProps12, but keep pNext as is.
+				auto* pProps12 = (VkPhysicalDeviceVulkan12Properties*)next;
+				supportedProps12.pNext = pProps12->pNext;
+				*pProps12 = supportedProps12;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES: {
-				populate((VkPhysicalDeviceIDProperties*)next);
+				auto* dvcIDProps = (VkPhysicalDeviceIDProperties*)next;
+				mvkCopy(dvcIDProps->deviceUUID, supportedProps11.deviceUUID, VK_UUID_SIZE);
+				mvkCopy(dvcIDProps->driverUUID, supportedProps11.driverUUID, VK_UUID_SIZE);
+				mvkCopy(dvcIDProps->deviceLUID, supportedProps11.deviceLUID, VK_LUID_SIZE);
+				dvcIDProps->deviceNodeMask = supportedProps11.deviceNodeMask;
+				dvcIDProps->deviceLUIDValid = supportedProps11.deviceLUIDValid;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES: {
-				auto* maint3Props = (VkPhysicalDeviceMaintenance3Properties*)next;
-				maint3Props->maxPerSetDescriptors = (_metalFeatures.maxPerStageBufferCount + _metalFeatures.maxPerStageTextureCount + _metalFeatures.maxPerStageSamplerCount) * 4;
-				maint3Props->maxMemoryAllocationSize = _metalFeatures.maxMTLBufferSize;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES: {
+				auto* subgroupProps = (VkPhysicalDeviceSubgroupProperties*)next;
+				subgroupProps->subgroupSize = supportedProps11.subgroupSize;
+				subgroupProps->supportedStages = supportedProps11.subgroupSupportedStages;
+				subgroupProps->supportedOperations = supportedProps11.subgroupSupportedOperations;
+				subgroupProps->quadOperationsInAllStages = supportedProps11.subgroupQuadOperationsInAllStages;
 				break;
 			}
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES: {
-                auto* multiviewProps = (VkPhysicalDeviceMultiviewProperties*)next;
-                multiviewProps->maxMultiviewViewCount = 32;
-                if (canUseInstancingForMultiview()) {
-                    multiviewProps->maxMultiviewInstanceIndex = std::numeric_limits<uint32_t>::max() / 32;
-                } else {
-                    multiviewProps->maxMultiviewInstanceIndex = std::numeric_limits<uint32_t>::max();
-                }
-				break;
-            }
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES: {
 				auto* pointClipProps = (VkPhysicalDevicePointClippingProperties*)next;
-				pointClipProps->pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES;
+				pointClipProps->pointClippingBehavior = supportedProps11.pointClippingBehavior;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES: {
+				auto* multiviewProps = (VkPhysicalDeviceMultiviewProperties*)next;
+				multiviewProps->maxMultiviewViewCount = supportedProps11.maxMultiviewViewCount;
+				multiviewProps->maxMultiviewInstanceIndex = supportedProps11.maxMultiviewInstanceIndex;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES: {
 				auto* protectedMemProps = (VkPhysicalDeviceProtectedMemoryProperties*)next;
-				protectedMemProps->protectedNoFault = false;
+				protectedMemProps->protectedNoFault = supportedProps11.protectedNoFault;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: {
-				auto* pushDescProps = (VkPhysicalDevicePushDescriptorPropertiesKHR*)next;
-				pushDescProps->maxPushDescriptors = _properties.limits.maxPerStageResources;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES: {
+				auto* maint3Props = (VkPhysicalDeviceMaintenance3Properties*)next;
+				maint3Props->maxPerSetDescriptors = supportedProps11.maxPerSetDescriptors;
+				maint3Props->maxMemoryAllocationSize = supportedProps11.maxMemoryAllocationSize;
 				break;
 			}
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES: {
-                auto* subgroupProps = (VkPhysicalDeviceSubgroupProperties*)next;
-                subgroupProps->subgroupSize = _metalFeatures.maxSubgroupSize;
-                subgroupProps->supportedStages = VK_SHADER_STAGE_COMPUTE_BIT;
-                if (_features.tessellationShader) {
-                    subgroupProps->supportedStages |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
-                }
-                if (mvkOSVersionIsAtLeast(10.15, 13.0)) {
-                    subgroupProps->supportedStages |= VK_SHADER_STAGE_FRAGMENT_BIT;
-                }
-                subgroupProps->supportedOperations = VK_SUBGROUP_FEATURE_BASIC_BIT;
-                if (_metalFeatures.simdPermute || _metalFeatures.quadPermute) {
-                    subgroupProps->supportedOperations |= VK_SUBGROUP_FEATURE_VOTE_BIT |
-                        VK_SUBGROUP_FEATURE_BALLOT_BIT |
-                        VK_SUBGROUP_FEATURE_SHUFFLE_BIT |
-                        VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT;
-                }
-                if (_metalFeatures.simdReduction) {
-                    subgroupProps->supportedOperations |= VK_SUBGROUP_FEATURE_ARITHMETIC_BIT;
-                }
-                if (_metalFeatures.quadPermute) {
-                    subgroupProps->supportedOperations |= VK_SUBGROUP_FEATURE_QUAD_BIT;
-                }
-                subgroupProps->quadOperationsInAllStages = _metalFeatures.quadPermute;
+
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES: {
+				auto* depthStencilResolveProps = (VkPhysicalDeviceDepthStencilResolveProperties*)next;
+				depthStencilResolveProps->supportedDepthResolveModes = supportedProps12.supportedDepthResolveModes;
+				depthStencilResolveProps->supportedStencilResolveModes = supportedProps12.supportedStencilResolveModes;
+				depthStencilResolveProps->independentResolveNone = supportedProps12.independentResolveNone;
+				depthStencilResolveProps->independentResolve = supportedProps12.independentResolve;
 				break;
-            }
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES: {
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES: {
+				auto* physicalDeviceDriverProps = (VkPhysicalDeviceDriverProperties*)next;
+				physicalDeviceDriverProps->driverID = supportedProps12.driverID;
+				mvkCopy(physicalDeviceDriverProps->driverName, supportedProps12.driverName, VK_MAX_DRIVER_NAME_SIZE);
+				mvkCopy(physicalDeviceDriverProps->driverInfo, supportedProps12.driverInfo, VK_MAX_DRIVER_INFO_SIZE);
+				physicalDeviceDriverProps->conformanceVersion = supportedProps12.conformanceVersion;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES: {
                 auto* timelineSem4Props = (VkPhysicalDeviceTimelineSemaphoreProperties*)next;
-                timelineSem4Props->maxTimelineSemaphoreValueDifference = std::numeric_limits<uint64_t>::max();
+                timelineSem4Props->maxTimelineSemaphoreValueDifference = supportedProps12.maxTimelineSemaphoreValueDifference;
                 break;
             }
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT: {
-				bool isTier2 = isUsingMetalArgumentBuffers() && (_metalFeatures.argumentBuffersTier >= MTLArgumentBuffersTier2);
-				uint32_t maxSampCnt = getMaxSamplerCount();
-
-				auto* pDescIdxProps = (VkPhysicalDeviceDescriptorIndexingPropertiesEXT*)next;
-				pDescIdxProps->maxUpdateAfterBindDescriptorsInAllPools				= kMVKUndefinedLargeUInt32;
-				pDescIdxProps->shaderUniformBufferArrayNonUniformIndexingNative		= false;
-				pDescIdxProps->shaderSampledImageArrayNonUniformIndexingNative		= _metalFeatures.arrayOfTextures && _metalFeatures.arrayOfSamplers;
-				pDescIdxProps->shaderStorageBufferArrayNonUniformIndexingNative		= false;
-				pDescIdxProps->shaderStorageImageArrayNonUniformIndexingNative		= _metalFeatures.arrayOfTextures;
-				pDescIdxProps->shaderInputAttachmentArrayNonUniformIndexingNative	= _metalFeatures.arrayOfTextures;
-				pDescIdxProps->robustBufferAccessUpdateAfterBind					= _features.robustBufferAccess;
-				pDescIdxProps->quadDivergentImplicitLod								= false;
-				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindSamplers			= isTier2 ? maxSampCnt : _properties.limits.maxPerStageDescriptorSamplers;
-				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindUniformBuffers	= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorUniformBuffers;
-				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindStorageBuffers	= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorStorageBuffers;
-				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindSampledImages	= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorSampledImages;
-				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindStorageImages	= isTier2 ? 500000 : _properties.limits.maxPerStageDescriptorStorageImages;
-				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindInputAttachments	= _properties.limits.maxPerStageDescriptorInputAttachments;
-				pDescIdxProps->maxPerStageUpdateAfterBindResources					= isTier2 ? 500000 : _properties.limits.maxPerStageResources;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindSamplers				= isTier2 ? maxSampCnt : _properties.limits.maxDescriptorSetSamplers;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindUniformBuffers		= isTier2 ? 500000 : _properties.limits.maxDescriptorSetUniformBuffers;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic	= isTier2 ? 500000 : _properties.limits.maxDescriptorSetUniformBuffersDynamic;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindStorageBuffers		= isTier2 ? 500000 : _properties.limits.maxDescriptorSetStorageBuffers;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic	= isTier2 ? 500000 : _properties.limits.maxDescriptorSetStorageBuffersDynamic;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindSampledImages			= isTier2 ? 500000 : _properties.limits.maxDescriptorSetSampledImages;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindStorageImages			= isTier2 ? 500000 : _properties.limits.maxDescriptorSetStorageImages;
-				pDescIdxProps->maxDescriptorSetUpdateAfterBindInputAttachments		= _properties.limits.maxDescriptorSetInputAttachments;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES: {
+				auto* pDescIdxProps = (VkPhysicalDeviceDescriptorIndexingProperties*)next;
+				pDescIdxProps->maxUpdateAfterBindDescriptorsInAllPools				= supportedProps12.maxUpdateAfterBindDescriptorsInAllPools;
+				pDescIdxProps->shaderUniformBufferArrayNonUniformIndexingNative		= supportedProps12.shaderUniformBufferArrayNonUniformIndexingNative;
+				pDescIdxProps->shaderSampledImageArrayNonUniformIndexingNative		= supportedProps12.shaderSampledImageArrayNonUniformIndexingNative;
+				pDescIdxProps->shaderStorageBufferArrayNonUniformIndexingNative		= supportedProps12.shaderStorageBufferArrayNonUniformIndexingNative;
+				pDescIdxProps->shaderStorageImageArrayNonUniformIndexingNative		= supportedProps12.shaderStorageImageArrayNonUniformIndexingNative;
+				pDescIdxProps->shaderInputAttachmentArrayNonUniformIndexingNative	= supportedProps12.shaderInputAttachmentArrayNonUniformIndexingNative;
+				pDescIdxProps->robustBufferAccessUpdateAfterBind					= supportedProps12.robustBufferAccessUpdateAfterBind;
+				pDescIdxProps->quadDivergentImplicitLod								= supportedProps12.quadDivergentImplicitLod;
+				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindSamplers			= supportedProps12.maxPerStageDescriptorUpdateAfterBindSamplers;
+				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindUniformBuffers	= supportedProps12.maxPerStageDescriptorUpdateAfterBindUniformBuffers;
+				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindStorageBuffers	= supportedProps12.maxPerStageDescriptorUpdateAfterBindStorageBuffers;
+				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindSampledImages	= supportedProps12.maxPerStageDescriptorUpdateAfterBindSampledImages;
+				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindStorageImages	= supportedProps12.maxPerStageDescriptorUpdateAfterBindStorageImages;
+				pDescIdxProps->maxPerStageDescriptorUpdateAfterBindInputAttachments	= supportedProps12.maxPerStageDescriptorUpdateAfterBindInputAttachments;
+				pDescIdxProps->maxPerStageUpdateAfterBindResources					= supportedProps12.maxPerStageUpdateAfterBindResources;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindSamplers				= supportedProps12.maxDescriptorSetUpdateAfterBindSamplers;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindUniformBuffers		= supportedProps12.maxDescriptorSetUpdateAfterBindUniformBuffers;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindUniformBuffersDynamic	= supportedProps12.maxDescriptorSetUpdateAfterBindUniformBuffersDynamic;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindStorageBuffers		= supportedProps12.maxDescriptorSetUpdateAfterBindStorageBuffers;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindStorageBuffersDynamic	= supportedProps12.maxDescriptorSetUpdateAfterBindStorageBuffersDynamic;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindSampledImages			= supportedProps12.maxDescriptorSetUpdateAfterBindSampledImages;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindStorageImages			= supportedProps12.maxDescriptorSetUpdateAfterBindStorageImages;
+				pDescIdxProps->maxDescriptorSetUpdateAfterBindInputAttachments		= supportedProps12.maxDescriptorSetUpdateAfterBindInputAttachments;
 				break;
 			}
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT: {
-				auto* inlineUniformBlockProps = (VkPhysicalDeviceInlineUniformBlockPropertiesEXT*)next;
+            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES: {
+				auto* inlineUniformBlockProps = (VkPhysicalDeviceInlineUniformBlockProperties*)next;
 				inlineUniformBlockProps->maxInlineUniformBlockSize = _metalFeatures.dynamicMTLBufferSize;
                 inlineUniformBlockProps->maxPerStageDescriptorInlineUniformBlocks = _metalFeatures.dynamicMTLBufferSize ? _metalFeatures.maxPerStageDynamicMTLBufferCount - 1 : 0;    // Less one for push constants
                 inlineUniformBlockProps->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks = inlineUniformBlockProps->maxPerStageDescriptorInlineUniformBlocks;
@@ -450,34 +595,31 @@
                 inlineUniformBlockProps->maxDescriptorSetUpdateAfterBindInlineUniformBlocks = (inlineUniformBlockProps->maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks * 4);
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT: {
-				auto* robustness2Props = (VkPhysicalDeviceRobustness2PropertiesEXT*)next;
-				// This isn't implemented yet, but when it is, I expect that we'll wind up
-				// doing it manually.
-				robustness2Props->robustStorageBufferAccessSizeAlignment = 1;
-				robustness2Props->robustUniformBufferAccessSizeAlignment = 1;
-				break;
-			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT: {
-				auto* subgroupSizeProps = (VkPhysicalDeviceSubgroupSizeControlPropertiesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES: {
+				auto* subgroupSizeProps = (VkPhysicalDeviceSubgroupSizeControlProperties*)next;
 				subgroupSizeProps->minSubgroupSize = _metalFeatures.minSubgroupSize;
 				subgroupSizeProps->maxSubgroupSize = _metalFeatures.maxSubgroupSize;
 				subgroupSizeProps->maxComputeWorkgroupSubgroups = _properties.limits.maxComputeWorkGroupInvocations / _metalFeatures.minSubgroupSize;
 				subgroupSizeProps->requiredSubgroupSizeStages = 0;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT: {
-				auto* texelBuffAlignProps = (VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT*)next;
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES: {
 				// Save the 'next' pointer; we'll unintentionally overwrite it
 				// on the next line. Put it back when we're done.
-				void* savedNext = texelBuffAlignProps->pNext;
+				auto* texelBuffAlignProps = (VkPhysicalDeviceTexelBufferAlignmentProperties*)next;
+				void* pNext = texelBuffAlignProps->pNext;
 				*texelBuffAlignProps = _texelBuffAlignProperties;
-				texelBuffAlignProps->pNext = savedNext;
+				texelBuffAlignProps->pNext = pNext;
 				break;
 			}
-			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: {
-				auto* divisorProps = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT*)next;
-				divisorProps->maxVertexAttribDivisor = kMVKUndefinedLargeUInt32;
+            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_PROPERTIES_KHR: {
+                auto* barycentricProperties = (VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR*)next;
+                barycentricProperties->triStripVertexOrderIndependentOfProvokingVertex = false;
+                break;
+            }
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: {
+				auto* pushDescProps = (VkPhysicalDevicePushDescriptorPropertiesKHR*)next;
+				pushDescProps->maxPushDescriptors = _properties.limits.maxPerStageResources;
 				break;
 			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR: {
@@ -485,6 +627,11 @@
 				portabilityProps->minVertexInputBindingStrideAlignment = (uint32_t)_metalFeatures.vertexStrideAlignment;
 				break;
 			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: {
+				auto* divisorProps = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT*)next;
+				divisorProps->maxVertexAttribDivisor = kMVKUndefinedLargeUInt32;
+				break;
+			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT: {
 				auto* sampLocnProps = (VkPhysicalDeviceSampleLocationsPropertiesEXT*)next;
 				sampLocnProps->sampleLocationSampleCounts = _metalFeatures.supportedSampleCounts;
@@ -495,25 +642,25 @@
 				sampLocnProps->variableSampleLocations = VK_FALSE;
 				break;
 			}
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_PROPERTIES_KHR: {
-                auto* barycentricProperties = (VkPhysicalDeviceFragmentShaderBarycentricPropertiesKHR*)next;
-                barycentricProperties->triStripVertexOrderIndependentOfProvokingVertex = false;
-                break;
-            }
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT: {
+				// This isn't implemented yet, but when it is, it is expected that we'll wind up doing it manually.
+				auto* robustness2Props = (VkPhysicalDeviceRobustness2PropertiesEXT*)next;
+				robustness2Props->robustStorageBufferAccessSizeAlignment = 1;
+				robustness2Props->robustUniformBufferAccessSizeAlignment = 1;
+				break;
+			}
 			default:
 				break;
 		}
 	}
 }
 
-// Populates the device ID properties structure
-void MVKPhysicalDevice::populate(VkPhysicalDeviceIDProperties* pDevIdProps) {
-
+void MVKPhysicalDevice::populateDeviceIDProperties(VkPhysicalDeviceVulkan11Properties* pVk11Props) {
 	uint8_t* uuid;
 	size_t uuidComponentOffset;
 
 	//  ---- Device ID ----------------------------------------------
-	uuid = pDevIdProps->deviceUUID;
+	uuid = pVk11Props->deviceUUID;
 	uuidComponentOffset = 0;
 	mvkClear(uuid, VK_UUID_SIZE);
 
@@ -532,9 +679,8 @@
 	*(uint64_t*)&uuid[uuidComponentOffset] = NSSwapHostLongLongToBig(regID);
 	uuidComponentOffset += sizeof(regID);
 
-
 	// ---- Driver ID ----------------------------------------------
-	uuid = pDevIdProps->driverUUID;
+	uuid = pVk11Props->driverUUID;
 	uuidComponentOffset = 0;
 	mvkClear(uuid, VK_UUID_SIZE);
 
@@ -555,9 +701,34 @@
 	uuidComponentOffset += sizeof(gpuCap);
 
 	// ---- LUID ignored for Metal devices ------------------------
-	mvkClear(pDevIdProps->deviceLUID, VK_LUID_SIZE);
-	pDevIdProps->deviceNodeMask = 0;
-	pDevIdProps->deviceLUIDValid = VK_FALSE;
+	mvkClear(pVk11Props->deviceLUID, VK_LUID_SIZE);
+	pVk11Props->deviceNodeMask = 0;
+	pVk11Props->deviceLUIDValid = VK_FALSE;
+}
+
+void MVKPhysicalDevice::populateSubgroupProperties(VkPhysicalDeviceVulkan11Properties* pVk11Props) {
+	pVk11Props->subgroupSize = _metalFeatures.maxSubgroupSize;
+	pVk11Props->subgroupSupportedStages = VK_SHADER_STAGE_COMPUTE_BIT;
+	if (_features.tessellationShader) {
+		pVk11Props->subgroupSupportedStages |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+	}
+	if (mvkOSVersionIsAtLeast(10.15, 13.0)) {
+		pVk11Props->subgroupSupportedStages |= VK_SHADER_STAGE_FRAGMENT_BIT;
+	}
+	pVk11Props->subgroupSupportedOperations = VK_SUBGROUP_FEATURE_BASIC_BIT;
+	if (_metalFeatures.simdPermute || _metalFeatures.quadPermute) {
+		pVk11Props->subgroupSupportedOperations |= (VK_SUBGROUP_FEATURE_VOTE_BIT |
+													VK_SUBGROUP_FEATURE_BALLOT_BIT |
+													VK_SUBGROUP_FEATURE_SHUFFLE_BIT |
+													VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT);
+	}
+	if (_metalFeatures.simdReduction) {
+		pVk11Props->subgroupSupportedOperations |= VK_SUBGROUP_FEATURE_ARITHMETIC_BIT;
+	}
+	if (_metalFeatures.quadPermute) {
+		pVk11Props->subgroupSupportedOperations |= VK_SUBGROUP_FEATURE_QUAD_BIT;
+	}
+	pVk11Props->subgroupQuadOperationsInAllStages = _metalFeatures.quadPermute;
 }
 
 void MVKPhysicalDevice::getFormatProperties(VkFormat format, VkFormatProperties* pFormatProperties) {
@@ -4290,6 +4461,71 @@
 							   &pdFeats2.features.robustBufferAccess, 55);
 				break;
 			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES: {
+				auto* requestedFeatures = (VkPhysicalDeviceVulkan11Features*)next;
+				enableFeatures(&_enabled16BitStorageFeatures.storageBuffer16BitAccess,
+							   &requestedFeatures->storageBuffer16BitAccess,
+							   &pd16BitStorageFeatures.storageBuffer16BitAccess, 4);
+				enableFeatures(&_enabledMultiviewFeatures.multiview,
+							   &requestedFeatures->multiview,
+							   &pdMultiviewFeatures.multiview, 3);
+				enableFeatures(&_enabledVariablePointerFeatures.variablePointersStorageBuffer,
+							   &requestedFeatures->variablePointersStorageBuffer,
+							   &pdVariablePointerFeatures.variablePointersStorageBuffer, 2);
+				enableFeatures(&_enabledProtectedMemoryFeatures.protectedMemory,
+							   &requestedFeatures->protectedMemory,
+							   &pdProtectedMemoryFeatures.protectedMemory, 1);
+				enableFeatures(&_enabledSamplerYcbcrConversionFeatures.samplerYcbcrConversion,
+							   &requestedFeatures->samplerYcbcrConversion,
+							   &pdSamplerYcbcrConversionFeatures.samplerYcbcrConversion, 1);
+				enableFeatures(&_enabledShaderDrawParametersFeatures.shaderDrawParameters,
+							   &requestedFeatures->shaderDrawParameters,
+							   &pdShaderDrawParametersFeatures.shaderDrawParameters, 1);
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES: {
+				auto* requestedFeatures = (VkPhysicalDeviceVulkan12Features*)next;
+				enableFeatures(&_enabled8BitStorageFeatures.storageBuffer8BitAccess,
+							   &requestedFeatures->storageBuffer8BitAccess,
+							   &pd8BitStorageFeatures.storageBuffer8BitAccess, 3);
+//				enableFeatures(&_enabledShaderAtomicInt64Features.shaderBufferInt64Atomics,		//VK_KHR_shader_atomic_int64
+//							   &requestedFeatures->shaderBufferInt64Atomics,
+//							   &pdShaderAtomicInt64Features.shaderBufferInt64Atomics, 2);
+				enableFeatures(&_enabledShaderFloat16Int8Features.shaderFloat16,
+							   &requestedFeatures->shaderFloat16,
+							   &pdShaderFloat16Int8Features.shaderFloat16, 2);
+				enableFeatures(&_enabledDescriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing,
+							   &requestedFeatures->shaderInputAttachmentArrayDynamicIndexing,
+							   &pdDescriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing, 20);
+				enableFeatures(&_enabledScalarBlockLayoutFeatures.scalarBlockLayout,
+							   &requestedFeatures->scalarBlockLayout,
+							   &pdScalarBlockLayoutFeatures.scalarBlockLayout, 1);
+				enableFeatures(&_enabledImagelessFramebufferFeatures.imagelessFramebuffer,
+							   &requestedFeatures->imagelessFramebuffer,
+							   &pdImagelessFramebufferFeatures.imagelessFramebuffer, 1);
+				enableFeatures(&_enabledUniformBufferStandardLayoutFeatures.uniformBufferStandardLayout,
+							   &requestedFeatures->uniformBufferStandardLayout,
+							   &pdUniformBufferStandardLayoutFeatures.uniformBufferStandardLayout, 1);
+				enableFeatures(&_enabledShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes,
+							   &requestedFeatures->shaderSubgroupExtendedTypes,
+							   &pdShaderSubgroupExtendedTypesFeatures.shaderSubgroupExtendedTypes, 1);
+				enableFeatures(&_enabledSeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts,
+							   &requestedFeatures->separateDepthStencilLayouts,
+							   &pdSeparateDepthStencilLayoutsFeatures.separateDepthStencilLayouts, 1);
+				enableFeatures(&_enabledHostQueryResetFeatures.hostQueryReset,
+							   &requestedFeatures->hostQueryReset,
+							   &pdHostQueryResetFeatures.hostQueryReset, 1);
+				enableFeatures(&_enabledTimelineSemaphoreFeatures.timelineSemaphore,
+							   &requestedFeatures->timelineSemaphore,
+							   &pdTimelineSemaphoreFeatures.timelineSemaphore, 1);
+				enableFeatures(&_enabledBufferDeviceAddressFeatures.bufferDeviceAddress,
+							   &requestedFeatures->bufferDeviceAddress,
+							   &pdBufferDeviceAddressFeatures.bufferDeviceAddress, 3);
+//				enableFeatures(&_enabledVulkanMemoryModelFeatures.vulkanMemoryModel,		// VK_KHR_vulkan_memory_model
+//							   &requestedFeatures->vulkanMemoryModel,
+//							   &pdVulkanMemoryModelFeatures.vulkanMemoryModel, 3);
+				break;
+			}
 
 #define MVK_DEVICE_FEATURE(structName, enumName, flagCount) \
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_##enumName##_FEATURES: { \