Merge branch 'master' of https://github.com/billhollings/MoltenVK into xcode12
diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md
index 51a63e3..f1fc211 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -274,6 +274,7 @@
 - `VK_KHR_bind_memory2`
 - `VK_KHR_create_renderpass2`
 - `VK_KHR_dedicated_allocation`
+- `VK_KHR_depth_stencil_resolve`
 - `VK_KHR_descriptor_update_template`
 - `VK_KHR_device_group`
 - `VK_KHR_device_group_creation`
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 9eb11bf..03b2cc5 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -27,6 +27,7 @@
 - Add support for extensions:
 	- `VK_KHR_portability_subset`
 	- `VK_KHR_create_renderpass2`
+	- `VK_KHR_depth_stencil_resolve`
 	- `VK_KHR_external_fence` (non-functional groundwork for future extensions,
 	  including support for GCD and Mach semaphores)
 	- `VK_KHR_external_fence_capabilities` (non-functional groundwork for future
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index 3de75fc..4c1567c 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -124,6 +124,16 @@
 		2FEA0AB424902F9F00EEF3AD /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; };
 		45003E73214AD4E500E989CB /* MVKExtensions.def in Headers */ = {isa = PBXBuildFile; fileRef = 45003E6F214AD4C900E989CB /* MVKExtensions.def */; };
 		45003E74214AD4E600E989CB /* MVKExtensions.def in Headers */ = {isa = PBXBuildFile; fileRef = 45003E6F214AD4C900E989CB /* MVKExtensions.def */; };
+		453638322508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h in Headers */ = {isa = PBXBuildFile; fileRef = 4536382D2508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h */; };
+		453638342508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h in Headers */ = {isa = PBXBuildFile; fileRef = 4536382D2508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h */; };
+		453638352508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */; };
+		453638362508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */; };
+		453638372508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */; };
+		453638382508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 453638302508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m */; };
+		4536383A2508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = 453638302508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m */; };
+		4536383B2508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h in Headers */ = {isa = PBXBuildFile; fileRef = 453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */; };
+		4536383C2508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h in Headers */ = {isa = PBXBuildFile; fileRef = 453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */; };
+		4536383D2508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h in Headers */ = {isa = PBXBuildFile; fileRef = 453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */; };
 		4553AEFB2251617100E8EBCD /* MVKBlockObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 4553AEF62251617100E8EBCD /* MVKBlockObserver.m */; };
 		4553AEFC2251617100E8EBCD /* MVKBlockObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 4553AEF62251617100E8EBCD /* MVKBlockObserver.m */; };
 		4553AEFD2251617100E8EBCD /* MVKBlockObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4553AEFA2251617100E8EBCD /* MVKBlockObserver.h */; };
@@ -451,6 +461,10 @@
 /* Begin PBXFileReference section */
 		2FEA0ABA24902F9F00EEF3AD /* libMoltenVK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMoltenVK.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		45003E6F214AD4C900E989CB /* MVKExtensions.def */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = MVKExtensions.def; sourceTree = "<group>"; };
+		4536382D2508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h"; sourceTree = "<group>"; };
+		4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m"; sourceTree = "<group>"; };
+		453638302508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m"; sourceTree = "<group>"; };
+		453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h"; sourceTree = "<group>"; };
 		4553AEF62251617100E8EBCD /* MVKBlockObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MVKBlockObserver.m; sourceTree = "<group>"; };
 		4553AEFA2251617100E8EBCD /* MVKBlockObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKBlockObserver.h; sourceTree = "<group>"; };
 		45557A4D21C9EFF3008868BD /* MVKCodec.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MVKCodec.cpp; sourceTree = "<group>"; };
@@ -758,8 +772,12 @@
 			children = (
 				A9E53DD12100B197002781DD /* CAMetalLayer+MoltenVK.h */,
 				A9E53DD62100B197002781DD /* CAMetalLayer+MoltenVK.m */,
+				453638312508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h */,
+				4536382F2508A4C6000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m */,
 				A9E53DEE2100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h */,
 				A9E53DF22100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m */,
+				4536382D2508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h */,
+				453638302508A4C6000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m */,
 				A9E53DFE21064F84002781DD /* MTLRenderPipelineDescriptor+MoltenVK.h */,
 				A9E53DFA21064F84002781DD /* MTLRenderPipelineDescriptor+MoltenVK.m */,
 				A9E53DD32100B197002781DD /* MTLSamplerDescriptor+MoltenVK.h */,
@@ -871,6 +889,7 @@
 				2FEA0A7524902F9F00EEF3AD /* MVKCmdDebug.h in Headers */,
 				2FEA0A7624902F9F00EEF3AD /* MVKWatermarkTextureContent.h in Headers */,
 				2FEA0A7724902F9F00EEF3AD /* MVKFoundation.h in Headers */,
+				4536383C2508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h in Headers */,
 				2FEA0A7824902F9F00EEF3AD /* MVKDeviceMemory.h in Headers */,
 				2FEA0A7924902F9F00EEF3AD /* MVKMTLResourceBindings.h in Headers */,
 				2FEA0A7A24902F9F00EEF3AD /* MVKExtensions.def in Headers */,
@@ -906,6 +925,7 @@
 				45557A5421C9EFF3008868BD /* MVKCodec.h in Headers */,
 				A94FB8041C7DFB4800632CA3 /* MVKRenderPass.h in Headers */,
 				A9F042A61FB4CF83009FCCB8 /* MVKLogging.h in Headers */,
+				453638322508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h in Headers */,
 				A94FB8001C7DFB4800632CA3 /* MVKQueue.h in Headers */,
 				A94FB7EC1C7DFB4800632CA3 /* MVKFramebuffer.h in Headers */,
 				83A4AD2C21BD75570006C935 /* MVKVectorAllocator.h in Headers */,
@@ -949,6 +969,7 @@
 				A9CEAAD5227378D400FAF779 /* mvk_datatypes.hpp in Headers */,
 				A90C8DEA1F45354D009CB32C /* MVKCommandEncodingPool.h in Headers */,
 				A94FB8081C7DFB4800632CA3 /* MVKResource.h in Headers */,
+				4536383B2508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h in Headers */,
 				A9E53DDD2100B197002781DD /* MTLTextureDescriptor+MoltenVK.h in Headers */,
 				A9653FBA24129C84005999D7 /* MVKPixelFormats.h in Headers */,
 				A981496B1FB6A998005F00B4 /* MVKStrings.h in Headers */,
@@ -978,6 +999,7 @@
 				45557A5521C9EFF3008868BD /* MVKCodec.h in Headers */,
 				A94FB8051C7DFB4800632CA3 /* MVKRenderPass.h in Headers */,
 				A9F042A71FB4CF83009FCCB8 /* MVKLogging.h in Headers */,
+				453638342508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h in Headers */,
 				A94FB8011C7DFB4800632CA3 /* MVKQueue.h in Headers */,
 				A94FB7ED1C7DFB4800632CA3 /* MVKFramebuffer.h in Headers */,
 				83A4AD2D21BD75570006C935 /* MVKVectorAllocator.h in Headers */,
@@ -1021,6 +1043,7 @@
 				A9CEAAD6227378D400FAF779 /* mvk_datatypes.hpp in Headers */,
 				A90C8DEB1F45354D009CB32C /* MVKCommandEncodingPool.h in Headers */,
 				A94FB8091C7DFB4800632CA3 /* MVKResource.h in Headers */,
+				4536383D2508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h in Headers */,
 				A9E53DDE2100B197002781DD /* MTLTextureDescriptor+MoltenVK.h in Headers */,
 				A9653FBB24129C84005999D7 /* MVKPixelFormats.h in Headers */,
 				A981496C1FB6A998005F00B4 /* MVKStrings.h in Headers */,
@@ -1375,6 +1398,7 @@
 				2FEA0A9B24902F9F00EEF3AD /* MVKFoundation.cpp in Sources */,
 				2FEA0A9C24902F9F00EEF3AD /* MVKPixelFormats.mm in Sources */,
 				2FEA0A9D24902F9F00EEF3AD /* MVKDevice.mm in Sources */,
+				453638362508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m in Sources */,
 				2FEA0A9E24902F9F00EEF3AD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */,
 				2FEA0A9F24902F9F00EEF3AD /* MVKDescriptor.mm in Sources */,
 				2FEA0AA024902F9F00EEF3AD /* MVKPipeline.mm in Sources */,
@@ -1417,6 +1441,7 @@
 				A981494D1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */,
 				A9E53DE52100B197002781DD /* NSString+MoltenVK.mm in Sources */,
 				A94FB8321C7DFB4800632CA3 /* vulkan.mm in Sources */,
+				453638352508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m in Sources */,
 				A94FB8121C7DFB4800632CA3 /* MVKSurface.mm in Sources */,
 				A94FB7FE1C7DFB4800632CA3 /* MVKQueryPool.mm in Sources */,
 				A94FB7F61C7DFB4800632CA3 /* MVKInstance.mm in Sources */,
@@ -1451,6 +1476,7 @@
 				A94FB7BE1C7DFB4800632CA3 /* MVKCmdPipeline.mm in Sources */,
 				A94FB81E1C7DFB4800632CA3 /* MVKLayers.mm in Sources */,
 				A94FB7EE1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */,
+				453638382508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */,
 				A9C96DD21DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */,
 				A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */,
 				A9096E5E1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */,
@@ -1474,6 +1500,7 @@
 				A981494E1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */,
 				A9E53DE62100B197002781DD /* NSString+MoltenVK.mm in Sources */,
 				A94FB8331C7DFB4800632CA3 /* vulkan.mm in Sources */,
+				453638372508A4C7000EFFD3 /* MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m in Sources */,
 				A94FB8131C7DFB4800632CA3 /* MVKSurface.mm in Sources */,
 				A94FB7FF1C7DFB4800632CA3 /* MVKQueryPool.mm in Sources */,
 				A94FB7F71C7DFB4800632CA3 /* MVKInstance.mm in Sources */,
@@ -1508,6 +1535,7 @@
 				A94FB7BF1C7DFB4800632CA3 /* MVKCmdPipeline.mm in Sources */,
 				A94FB81F1C7DFB4800632CA3 /* MVKLayers.mm in Sources */,
 				A94FB7EF1C7DFB4800632CA3 /* MVKFramebuffer.mm in Sources */,
+				4536383A2508A4C7000EFFD3 /* MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m in Sources */,
 				A9C96DD31DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */,
 				A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */,
 				A9096E5F1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */,
diff --git a/MoltenVK/MoltenVK/API/mvk_datatypes.h b/MoltenVK/MoltenVK/API/mvk_datatypes.h
index 3f9e601..12628c5 100644
--- a/MoltenVK/MoltenVK/API/mvk_datatypes.h
+++ b/MoltenVK/MoltenVK/API/mvk_datatypes.h
@@ -362,6 +362,14 @@
 /** Returns the Metal MTLStoreAction corresponding to the specified Vulkan VkAttachmentStoreOp. */
 MTLStoreAction mvkMTLStoreActionFromVkAttachmentStoreOp(VkAttachmentStoreOp vkStoreOp, bool hasResolveAttachment);
 
+/** Returns the Metal MTLMultisampleDepthResolveFilter corresponding to the specified Vulkan VkResolveModeFlagBits. */
+MTLMultisampleDepthResolveFilter mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBits(VkResolveModeFlagBits vkResolveMode);
+
+#if MVK_MACOS_OR_IOS
+/** Returns the Metal MTLMultisampleStencilResolveFilter corresponding to the specified Vulkan VkResolveModeFlagBits. */
+MTLMultisampleStencilResolveFilter mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBits(VkResolveModeFlagBits vkResolveMode);
+#endif
+
 /** Returns the Metal MTLViewport corresponding to the specified Vulkan VkViewport. */
 MTLViewport mvkMTLViewportFromVkViewport(VkViewport vkViewport);
 
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 3b67b4d..d6025c4 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -618,6 +618,8 @@
 	VkBool32 renderWithoutAttachments;          /**< If true, we don't have to create a dummy attachment for a render pass if there isn't one. */
 	VkBool32 deferredStoreActions;				/**< If true, render pass store actions can be specified after the render encoder is created. */
 	VkBool32 sharedLinearTextures;				/**< If true, linear textures and texture buffers can be created from buffers in Shared storage. */
+	VkBool32 depthResolve;						/**< If true, resolving depth textures with filters other than Sample0 is supported. */
+	VkBool32 stencilResolve;					/**< If true, resolving stencil textures with filters other than Sample0 is supported. */
 } MVKPhysicalDeviceMetalFeatures;
 
 /** MoltenVK performance of a particular type of activity. */
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 575ee3f..614f064 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -210,6 +210,23 @@
 	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;
+				break;
+			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES: {
 				auto* physicalDeviceDriverProps = (VkPhysicalDeviceDriverPropertiesKHR*)next;
 				strcpy(physicalDeviceDriverProps->driverName, "MoltenVK");
@@ -1027,6 +1044,7 @@
 		_metalFeatures.depthSampleCompare = true;
 		_metalFeatures.arrayOfTextures = true;
 		_metalFeatures.arrayOfSamplers = true;
+		_metalFeatures.depthResolve = true;
 	}
 
 	if ( mvkOSVersionIsAtLeast(13.0) ) {
@@ -1079,6 +1097,7 @@
 		_metalFeatures.mtlBufferAlignment = 16;     // Min float4 alignment for typical vertex buffers. MTLBuffer may go down to 4 bytes for other data.
 		_metalFeatures.maxTextureDimension = (16 * KIBI);
 		_metalFeatures.depthSampleCompare = true;
+		_metalFeatures.depthResolve = true;
 	}
 
 	if (supportsMTLFeatureSet(iOS_GPUFamily3_v2)) {
@@ -1097,6 +1116,7 @@
 		_metalFeatures.layeredRendering = true;
 		_metalFeatures.stencilFeedback = true;
 		_metalFeatures.indirectTessellationDrawing = true;
+		_metalFeatures.stencilResolve = true;
 	}
 
 	if ( mvkOSVersionIsAtLeast(13.0) ) {
@@ -1153,6 +1173,8 @@
 	if (supportsMTLFeatureSet(macOS_GPUFamily2_v1)) {
 		_metalFeatures.multisampleLayeredRendering = _metalFeatures.layeredRendering;
 		_metalFeatures.stencilFeedback = true;
+		_metalFeatures.depthResolve = true;
+		_metalFeatures.stencilResolve = true;
 	}
 
 	if ( mvkOSVersionIsAtLeast(10.15) ) {
@@ -2239,6 +2261,11 @@
 	MVKExtensionList* pWritableExtns = (MVKExtensionList*)&_supportedExtensions;
 	pWritableExtns->disableAllButEnabledDeviceExtensions();
 
+#if MVK_IOS_OR_TVOS
+	if (!_metalFeatures.depthResolve) {
+		pWritableExtns->vk_KHR_depth_stencil_resolve.enabled = false;
+	}
+#endif
 	if (!_metalFeatures.rasterOrderGroups) {
 		pWritableExtns->vk_EXT_fragment_shader_interlock.enabled = false;
 	}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index a4eae12..b88450f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -366,6 +366,7 @@
 	bool _isLinear;
 	bool _is3DCompressed;
 	bool _isAliasable;
+	bool _hasExtendedUsage;
 };
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index f748d0a..173655d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -123,7 +123,7 @@
     mtlTexDesc.mipmapLevelCount = _image->_mipLevels;
     mtlTexDesc.sampleCount = mvkSampleCountFromVkSampleCountFlagBits(_image->_samples);
     mtlTexDesc.arrayLength = _image->_arrayLayers;
-    mtlTexDesc.usageMVK = _image->getPixelFormats()->getMTLTextureUsage(_image->_usage, mtlPixFmt, minUsage, _image->_isLinear);
+    mtlTexDesc.usageMVK = _image->getPixelFormats()->getMTLTextureUsage(_image->_usage, mtlPixFmt, minUsage, _image->_isLinear, _image->_hasExtendedUsage);
     mtlTexDesc.storageModeMVK = _image->getMTLStorageMode();
     mtlTexDesc.cpuCacheMode = _image->getMTLCPUCacheMode();
 
@@ -604,10 +604,11 @@
 
 VkResult MVKImage::getMemoryRequirements(const void* pInfo, VkMemoryRequirements2* pMemoryRequirements) {
     uint8_t planeIndex = 0;
-	for (auto* next = (VkBaseOutStructure*)pMemoryRequirements->pNext; next; next = next->pNext) {
+	const auto* pImageInfo = (const VkImageMemoryRequirementsInfo2*)pInfo;
+	for (const auto* next = (const VkBaseInStructure*)pImageInfo->pNext; next; next = next->pNext) {
 		switch (next->sType) {
 		case VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO: {
-			auto* planeReqs = (VkImagePlaneMemoryRequirementsInfo*)next;
+			const auto* planeReqs = (const VkImagePlaneMemoryRequirementsInfo*)next;
             planeIndex = MVKImage::getPlaneFromVkImageAspectFlags(planeReqs->planeAspect);
 			break;
 		}
@@ -615,7 +616,9 @@
 			break;
 		}
 	}
-    return getMemoryRequirements(&pMemoryRequirements->memoryRequirements, planeIndex);
+    VkResult rslt = getMemoryRequirements(&pMemoryRequirements->memoryRequirements, planeIndex);
+    if (rslt != VK_SUCCESS) { return rslt; }
+    return _memoryBindings[planeIndex]->getMemoryRequirements(pInfo, pMemoryRequirements);
 }
 
 VkResult MVKImage::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset, uint8_t planeIndex) {
@@ -758,10 +761,12 @@
 
     if (_ioSurface && stgMode == MTLStorageModePrivate) { stgMode = MTLStorageModeShared; }
 
+#if MVK_MACOS
 	// For macOS prior to 10.15.5, textures cannot use Shared storage mode, so change to Managed storage mode.
     if (stgMode == MTLStorageModeShared && !_device->_pMetalFeatures->sharedLinearTextures) {
         stgMode = MTLStorageModeManaged;
     }
+#endif
     return stgMode;
 }
 
@@ -799,6 +804,7 @@
 	MVKPixelFormats* pixFmts = getPixelFormats();
     _vkFormat = pCreateInfo->format;
 	_usage = pCreateInfo->usage;
+	_hasExtendedUsage = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_EXTENDED_USAGE_BIT);
 
 	_is3DCompressed = (getImageType() == VK_IMAGE_TYPE_3D) && (pixFmts->getFormatType(pCreateInfo->format) == kMVKFormatCompressed) && !_device->_pMetalFeatures->native3DCompressedTextures;
 	_isDepthStencilAttachment = (mvkAreAllFlagsEnabled(pCreateInfo->usage, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ||
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
index 1df4621..e87063e 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.h
@@ -77,6 +77,61 @@
 	kMVKMTLFmtCapsMultiPlanar = kMVKMTLFmtCapsChromaSubsampling,
 } MVKMTLFmtCaps;
 
+inline MVKMTLFmtCaps operator|(MVKMTLFmtCaps leftCaps, MVKMTLFmtCaps rightCaps) {
+	return static_cast<MVKMTLFmtCaps>(static_cast<uint32_t>(leftCaps) | rightCaps);
+}
+
+inline MVKMTLFmtCaps& operator|=(MVKMTLFmtCaps& leftCaps, MVKMTLFmtCaps rightCaps) {
+	return (leftCaps = leftCaps | rightCaps);
+}
+
+
+#pragma mark -
+#pragma mark Metal view classes
+
+enum class MVKMTLViewClass : uint8_t {
+	None,
+	Color8,
+	Color16,
+	Color32,
+	Color64,
+	Color128,
+	PVRTC_RGB_2BPP,
+	PVRTC_RGB_4BPP,
+	PVRTC_RGBA_2BPP,
+	PVRTC_RGBA_4BPP,
+	EAC_R11,
+	EAC_RG11,
+	EAC_RGBA8,
+	ETC2_RGB8,
+	ETC2_RGB8A1,
+	ASTC_4x4,
+	ASTC_5x4,
+	ASTC_5x5,
+	ASTC_6x5,
+	ASTC_6x6,
+	ASTC_8x5,
+	ASTC_8x6,
+	ASTC_8x8,
+	ASTC_10x5,
+	ASTC_10x6,
+	ASTC_10x8,
+	ASTC_10x10,
+	ASTC_12x10,
+	ASTC_12x12,
+	BC1_RGBA,
+	BC2_RGBA,
+	BC3_RGBA,
+	BC4_R,
+	BC5_RG,
+	BC6H_RGB,
+	BC7_RGBA,
+	Depth24_Stencil8,
+	Depth32_Stencil8,
+	BGRA10_XR,
+	BGR10_XR
+};
+
 
 #pragma mark -
 #pragma mark Format descriptors
@@ -114,6 +169,7 @@
 	};
 	VkFormat vkFormat;
 	MVKMTLFmtCaps mtlFmtCaps;
+	MVKMTLViewClass mtlViewClass;
 	const char* name;
 
 	inline bool isSupported() const { return (mtlPixelFormat != MTLPixelFormatInvalid) && (mtlFmtCaps != kMVKMTLFmtCapsNone); };
@@ -254,10 +310,13 @@
 	VkFormatProperties& getVkFormatProperties(VkFormat vkFormat);
 
 	/** Returns the Metal format capabilities supported by the specified Vulkan format, without substitution. */
-	MVKMTLFmtCaps getCapabilities(VkFormat vkFormat);
+	MVKMTLFmtCaps getCapabilities(VkFormat vkFormat, bool isExtended = false);
 
 	/** Returns the Metal format capabilities supported by the specified Metal format. */
-	MVKMTLFmtCaps getCapabilities(MTLPixelFormat mtlFormat);
+	MVKMTLFmtCaps getCapabilities(MTLPixelFormat mtlFormat, bool isExtended = false);
+
+	/** Returns the Metal view class of the specified Metal format. */
+	MVKMTLViewClass getViewClass(MTLPixelFormat mtlFormat);
 
 	/** Returns the name of the specified Vulkan format. */
 	const char* getName(VkFormat vkFormat);
@@ -283,12 +342,15 @@
 	/**
 	 * Returns the Metal texture usage from the Vulkan image usage and Metal format, ensuring that at least the
 	 * usages in minUsage are included, even if they wouldn't naturally be included based on the other two parameters.
-     *  isLinear further restricts the allowed usage to those that are valid for linear textures.
+     * isLinear further restricts the allowed usage to those that are valid for linear textures.
+     * isExtended expands the allowed usage to those that are valid for all formats which
+     * can be used in a view created from the specified format.
 	 */
 	MTLTextureUsage getMTLTextureUsage(VkImageUsageFlags vkImageUsageFlags,
 									   MTLPixelFormat mtlFormat,
 									   MTLTextureUsage minUsage = MTLTextureUsageUnknown,
-                                       bool isLinear = false);
+                                       bool isLinear = false,
+                                       bool isExtended = false);
 
 	/** Enumerates all formats that support the given features, calling a specified function for each one. */
 	void enumerateSupportedFormats(VkFormatProperties properties, bool any, std::function<bool(VkFormat)> func);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
index 9fb584e..d27b4b2 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPixelFormats.mm
@@ -320,12 +320,23 @@
 	return getVkFormatDesc(vkFormat).properties;
 }
 
-MVKMTLFmtCaps MVKPixelFormats::getCapabilities(VkFormat vkFormat) {
-	return getMTLPixelFormatDesc(getVkFormatDesc(vkFormat).mtlPixelFormat).mtlFmtCaps;
+MVKMTLFmtCaps MVKPixelFormats::getCapabilities(VkFormat vkFormat, bool isExtended) {
+	return getCapabilities(getVkFormatDesc(vkFormat).mtlPixelFormat, isExtended);
 }
 
-MVKMTLFmtCaps MVKPixelFormats::getCapabilities(MTLPixelFormat mtlFormat) {
-	return getMTLPixelFormatDesc(mtlFormat).mtlFmtCaps;
+MVKMTLFmtCaps MVKPixelFormats::getCapabilities(MTLPixelFormat mtlFormat, bool isExtended) {
+    MVKMTLFormatDesc& mtlDesc = getMTLPixelFormatDesc(mtlFormat);
+    MVKMTLFmtCaps caps = mtlDesc.mtlFmtCaps;
+    if (!isExtended || mtlDesc.mtlViewClass == MVKMTLViewClass::None) { return caps; }
+    // Now get caps of all formats in the view class.
+    for (auto& otherDesc : _mtlPixelFormatDescriptions) {
+        if (otherDesc.mtlViewClass == mtlDesc.mtlViewClass) { caps |= otherDesc.mtlFmtCaps; }
+    }
+    return caps;
+}
+
+MVKMTLViewClass MVKPixelFormats::getViewClass(MTLPixelFormat mtlFormat) {
+    return getMTLPixelFormatDesc(mtlFormat).mtlViewClass;
 }
 
 const char* MVKPixelFormats::getName(VkFormat vkFormat) {
@@ -453,13 +464,14 @@
 MTLTextureUsage MVKPixelFormats::getMTLTextureUsage(VkImageUsageFlags vkImageUsageFlags,
 													MTLPixelFormat mtlFormat,
 													MTLTextureUsage minUsage,
-                                                    bool isLinear) {
+                                                    bool isLinear,
+                                                    bool isExtended) {
 	bool isDepthFmt = isDepthFormat(mtlFormat);
 	bool isStencilFmt = isStencilFormat(mtlFormat);
 	bool isCombinedDepthStencilFmt = isDepthFmt && isStencilFmt;
 	bool isColorFormat = !(isDepthFmt || isStencilFmt);
 	bool supportsStencilViews = _physicalDevice ? _physicalDevice->getMetalFeatures()->stencilViews : false;
-	MVKMTLFmtCaps mtlFmtCaps = getCapabilities(mtlFormat);
+	MVKMTLFmtCaps mtlFmtCaps = getCapabilities(mtlFormat, isExtended);
 
 	MTLTextureUsage mtlUsage = minUsage;
 
@@ -479,7 +491,7 @@
 	}
 #if MVK_MACOS
     // Clearing a linear image may use shader writes.
-    if (mvkIsAnyFlagEnabled(vkImageUsageFlags, VK_IMAGE_USAGE_TRANSFER_DST_BIT) &&
+    if (mvkIsAnyFlagEnabled(vkImageUsageFlags, (VK_IMAGE_USAGE_TRANSFER_DST_BIT)) &&
         mvkIsAnyFlagEnabled(mtlFmtCaps, kMVKMTLFmtCapsWrite) && isLinear) {
 
 		mvkEnableFlags(mtlUsage, MTLTextureUsageShaderWrite);
@@ -856,11 +868,11 @@
 	// When adding to this list, be sure to ensure _vkFormatCount is large enough for the format count
 }
 
-#define addMTLPixelFormatDesc(MTL_FMT, IOS_CAPS, MACOS_CAPS)  \
+#define addMTLPixelFormatDesc(MTL_FMT, VIEW_CLASS, IOS_CAPS, MACOS_CAPS)  \
 	MVKAssert(fmtIdx < _mtlPixelFormatCount, "Attempting to describe %d MTLPixelFormats, but only have space for %d. Increase the value of _mtlPixelFormatCount", fmtIdx + 1, _mtlPixelFormatCount);  \
 	_mtlPixelFormatDescriptions[fmtIdx++] = { .mtlPixelFormat = MTLPixelFormat ##MTL_FMT, VK_FORMAT_UNDEFINED,  \
 											  mvkSelectPlatformValue<MVKMTLFmtCaps>(kMVKMTLFmtCaps ##MACOS_CAPS, kMVKMTLFmtCaps ##IOS_CAPS),  \
-											  "MTLPixelFormat" #MTL_FMT }
+											  MVKMTLViewClass:: VIEW_CLASS, "MTLPixelFormat" #MTL_FMT }
 
 void MVKPixelFormats::initMTLPixelFormatCapabilities() {
 
@@ -871,159 +883,157 @@
 	// When adding to this list, be sure to ensure _mtlPixelFormatCount is large enough for the format count
 
 	// MTLPixelFormatInvalid must come first.
-	addMTLPixelFormatDesc( Invalid, None, None );
+	addMTLPixelFormatDesc( Invalid, None, None, None );
 
 	// Ordinary 8-bit pixel formats
-	addMTLPixelFormatDesc( A8Unorm, RF, RF );
-	addMTLPixelFormatDesc( R8Unorm, All, All );
-	addMTLPixelFormatDesc( R8Unorm_sRGB, RFCMRB, None );
-	addMTLPixelFormatDesc( R8Snorm, RFWCMB, All );
-	addMTLPixelFormatDesc( R8Uint, RWCM, RWCM );
-	addMTLPixelFormatDesc( R8Sint, RWCM, RWCM );
+	addMTLPixelFormatDesc( A8Unorm, Color8, RF, RF );
+	addMTLPixelFormatDesc( R8Unorm, Color8, All, All );
+	addMTLPixelFormatDesc( R8Unorm_sRGB, Color8, RFCMRB, None );
+	addMTLPixelFormatDesc( R8Snorm, Color8, RFWCMB, All );
+	addMTLPixelFormatDesc( R8Uint, Color8, RWCM, RWCM );
+	addMTLPixelFormatDesc( R8Sint, Color8, RWCM, RWCM );
 
 	// Ordinary 16-bit pixel formats
-	addMTLPixelFormatDesc( R16Unorm, RFWCMB, All );
-	addMTLPixelFormatDesc( R16Snorm, RFWCMB, All );
-	addMTLPixelFormatDesc( R16Uint, RWCM, RWCM );
-	addMTLPixelFormatDesc( R16Sint, RWCM, RWCM );
-	addMTLPixelFormatDesc( R16Float, All, All );
+	addMTLPixelFormatDesc( R16Unorm, Color16, RFWCMB, All );
+	addMTLPixelFormatDesc( R16Snorm, Color16, RFWCMB, All );
+	addMTLPixelFormatDesc( R16Uint, Color16, RWCM, RWCM );
+	addMTLPixelFormatDesc( R16Sint, Color16, RWCM, RWCM );
+	addMTLPixelFormatDesc( R16Float, Color16, All, All );
 
-	addMTLPixelFormatDesc( RG8Unorm, All, All );
-	addMTLPixelFormatDesc( RG8Unorm_sRGB, RFCMRB, None );
-	addMTLPixelFormatDesc( RG8Snorm, RFWCMB, All );
-	addMTLPixelFormatDesc( RG8Uint, RWCM, RWCM );
-	addMTLPixelFormatDesc( RG8Sint, RWCM, RWCM );
+	addMTLPixelFormatDesc( RG8Unorm, Color16, All, All );
+	addMTLPixelFormatDesc( RG8Unorm_sRGB, Color16, RFCMRB, None );
+	addMTLPixelFormatDesc( RG8Snorm, Color16, RFWCMB, All );
+	addMTLPixelFormatDesc( RG8Uint, Color16, RWCM, RWCM );
+	addMTLPixelFormatDesc( RG8Sint, Color16, RWCM, RWCM );
 
 	// Packed 16-bit pixel formats
-	addMTLPixelFormatDesc( B5G6R5Unorm, RFCMRB, None );
-	addMTLPixelFormatDesc( A1BGR5Unorm, RFCMRB, None );
-	addMTLPixelFormatDesc( ABGR4Unorm, RFCMRB, None );
-	addMTLPixelFormatDesc( BGR5A1Unorm, RFCMRB, None );
+	addMTLPixelFormatDesc( B5G6R5Unorm, Color16, RFCMRB, None );
+	addMTLPixelFormatDesc( A1BGR5Unorm, Color16, RFCMRB, None );
+	addMTLPixelFormatDesc( ABGR4Unorm, Color16, RFCMRB, None );
+	addMTLPixelFormatDesc( BGR5A1Unorm, Color16, RFCMRB, None );
 
 	// Ordinary 32-bit pixel formats
-	addMTLPixelFormatDesc( R32Uint, RC, RWCM );
-	addMTLPixelFormatDesc( R32Sint, RC, RWCM );
-	addMTLPixelFormatDesc( R32Float, RCMB, All );
+	addMTLPixelFormatDesc( R32Uint, Color32, RC, RWCM );
+	addMTLPixelFormatDesc( R32Sint, Color32, RC, RWCM );
+	addMTLPixelFormatDesc( R32Float, Color32, RCMB, All );
 
-	addMTLPixelFormatDesc( RG16Unorm, RFWCMB, All );
-	addMTLPixelFormatDesc( RG16Snorm, RFWCMB, All );
-	addMTLPixelFormatDesc( RG16Uint, RWCM, RWCM );
-	addMTLPixelFormatDesc( RG16Sint, RWCM, RWCM );
-	addMTLPixelFormatDesc( RG16Float, All, All );
+	addMTLPixelFormatDesc( RG16Unorm, Color32, RFWCMB, All );
+	addMTLPixelFormatDesc( RG16Snorm, Color32, RFWCMB, All );
+	addMTLPixelFormatDesc( RG16Uint, Color32, RWCM, RWCM );
+	addMTLPixelFormatDesc( RG16Sint, Color32, RWCM, RWCM );
+	addMTLPixelFormatDesc( RG16Float, Color32, All, All );
 
-	addMTLPixelFormatDesc( RGBA8Unorm, All, All );
-	addMTLPixelFormatDesc( RGBA8Unorm_sRGB, RFCMRB, RFCMRB );
-	addMTLPixelFormatDesc( RGBA8Snorm, RFWCMB, All );
-	addMTLPixelFormatDesc( RGBA8Uint, RWCM, RWCM );
-	addMTLPixelFormatDesc( RGBA8Sint, RWCM, RWCM );
+	addMTLPixelFormatDesc( RGBA8Unorm, Color32, All, All );
+	addMTLPixelFormatDesc( RGBA8Unorm_sRGB, Color32, RFCMRB, RFCMRB );
+	addMTLPixelFormatDesc( RGBA8Snorm, Color32, RFWCMB, All );
+	addMTLPixelFormatDesc( RGBA8Uint, Color32, RWCM, RWCM );
+	addMTLPixelFormatDesc( RGBA8Sint, Color32, RWCM, RWCM );
 
-	addMTLPixelFormatDesc( BGRA8Unorm, All, All );
-	addMTLPixelFormatDesc( BGRA8Unorm_sRGB, RFCMRB, RFCMRB );
+	addMTLPixelFormatDesc( BGRA8Unorm, Color32, All, All );
+	addMTLPixelFormatDesc( BGRA8Unorm_sRGB, Color32, RFCMRB, RFCMRB );
 
 	// Packed 32-bit pixel formats
-	addMTLPixelFormatDesc( RGB10A2Unorm, RFCMRB, All );
-	addMTLPixelFormatDesc( RGB10A2Uint, RCM, RWCM );
-	addMTLPixelFormatDesc( RG11B10Float, RFCMRB, All );
-	addMTLPixelFormatDesc( RGB9E5Float, RFCMRB, RF );
+	addMTLPixelFormatDesc( RGB10A2Unorm, Color32, RFCMRB, All );
+	addMTLPixelFormatDesc( RGB10A2Uint, Color32, RCM, RWCM );
+	addMTLPixelFormatDesc( RG11B10Float, Color32, RFCMRB, All );
+	addMTLPixelFormatDesc( RGB9E5Float, Color32, RFCMRB, RF );
 
 	// Ordinary 64-bit pixel formats
-	addMTLPixelFormatDesc( RG32Uint, RC, RWCM );
-	addMTLPixelFormatDesc( RG32Sint, RC, RWCM );
-	addMTLPixelFormatDesc( RG32Float, RCB, All );
+	addMTLPixelFormatDesc( RG32Uint, Color64, RC, RWCM );
+	addMTLPixelFormatDesc( RG32Sint, Color64, RC, RWCM );
+	addMTLPixelFormatDesc( RG32Float, Color64, RCB, All );
 
-	addMTLPixelFormatDesc( RGBA16Unorm, RFWCMB, All );
-	addMTLPixelFormatDesc( RGBA16Snorm, RFWCMB, All );
-	addMTLPixelFormatDesc( RGBA16Uint, RWCM, RWCM );
-	addMTLPixelFormatDesc( RGBA16Sint, RWCM, RWCM );
-	addMTLPixelFormatDesc( RGBA16Float, All, All );
+	addMTLPixelFormatDesc( RGBA16Unorm, Color64, RFWCMB, All );
+	addMTLPixelFormatDesc( RGBA16Snorm, Color64, RFWCMB, All );
+	addMTLPixelFormatDesc( RGBA16Uint, Color64, RWCM, RWCM );
+	addMTLPixelFormatDesc( RGBA16Sint, Color64, RWCM, RWCM );
+	addMTLPixelFormatDesc( RGBA16Float, Color64, All, All );
 
 	// Ordinary 128-bit pixel formats
-	addMTLPixelFormatDesc( RGBA32Uint, RC, RWCM );
-	addMTLPixelFormatDesc( RGBA32Sint, RC, RWCM );
-	addMTLPixelFormatDesc( RGBA32Float, RC, All );
+	addMTLPixelFormatDesc( RGBA32Uint, Color128, RC, RWCM );
+	addMTLPixelFormatDesc( RGBA32Sint, Color128, RC, RWCM );
+	addMTLPixelFormatDesc( RGBA32Float, Color128, RC, All );
 
 	// Compressed pixel formats
-	addMTLPixelFormatDesc( PVRTC_RGBA_2BPP, RF, None );
-	addMTLPixelFormatDesc( PVRTC_RGBA_4BPP, RF, None );
-	addMTLPixelFormatDesc( PVRTC_RGBA_2BPP_sRGB, RF, None );
-	addMTLPixelFormatDesc( PVRTC_RGBA_4BPP_sRGB, RF, None );
+	addMTLPixelFormatDesc( PVRTC_RGBA_2BPP, PVRTC_RGBA_2BPP, RF, None );
+	addMTLPixelFormatDesc( PVRTC_RGBA_4BPP, PVRTC_RGBA_4BPP, RF, None );
+	addMTLPixelFormatDesc( PVRTC_RGBA_2BPP_sRGB, PVRTC_RGBA_2BPP, RF, None );
+	addMTLPixelFormatDesc( PVRTC_RGBA_4BPP_sRGB, PVRTC_RGBA_4BPP, RF, None );
 
-	addMTLPixelFormatDesc( ETC2_RGB8, RF, None );
-	addMTLPixelFormatDesc( ETC2_RGB8_sRGB, RF, None );
-	addMTLPixelFormatDesc( ETC2_RGB8A1, RF, None );
-	addMTLPixelFormatDesc( ETC2_RGB8A1_sRGB, RF, None );
-	addMTLPixelFormatDesc( EAC_RGBA8, RF, None );
-	addMTLPixelFormatDesc( EAC_RGBA8_sRGB, RF, None );
-	addMTLPixelFormatDesc( EAC_R11Unorm, RF, None );
-	addMTLPixelFormatDesc( EAC_R11Snorm, RF, None );
-	addMTLPixelFormatDesc( EAC_RG11Unorm, RF, None );
-	addMTLPixelFormatDesc( EAC_RG11Snorm, RF, None );
+	addMTLPixelFormatDesc( ETC2_RGB8, ETC2_RGB8, RF, None );
+	addMTLPixelFormatDesc( ETC2_RGB8_sRGB, ETC2_RGB8, RF, None );
+	addMTLPixelFormatDesc( ETC2_RGB8A1, ETC2_RGB8A1, RF, None );
+	addMTLPixelFormatDesc( ETC2_RGB8A1_sRGB, ETC2_RGB8A1, RF, None );
+	addMTLPixelFormatDesc( EAC_RGBA8, EAC_RGBA8, RF, None );
+	addMTLPixelFormatDesc( EAC_RGBA8_sRGB, EAC_RGBA8, RF, None );
+	addMTLPixelFormatDesc( EAC_R11Unorm, EAC_R11, RF, None );
+	addMTLPixelFormatDesc( EAC_R11Snorm, EAC_R11, RF, None );
+	addMTLPixelFormatDesc( EAC_RG11Unorm, EAC_RG11, RF, None );
+	addMTLPixelFormatDesc( EAC_RG11Snorm, EAC_RG11, RF, None );
 
-	addMTLPixelFormatDesc( ASTC_4x4_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_4x4_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_5x4_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_5x4_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_5x5_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_5x5_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_6x5_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_6x5_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_6x6_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_6x6_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_8x5_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_8x5_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_8x6_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_8x6_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_8x8_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_8x8_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_10x5_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_10x5_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_10x6_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_10x6_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_10x8_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_10x8_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_10x10_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_10x10_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_12x10_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_12x10_sRGB, None, None );
-	addMTLPixelFormatDesc( ASTC_12x12_LDR, None, None );
-	addMTLPixelFormatDesc( ASTC_12x12_sRGB, None, None );
+	addMTLPixelFormatDesc( ASTC_4x4_LDR, ASTC_4x4, None, None );
+	addMTLPixelFormatDesc( ASTC_4x4_sRGB, ASTC_4x4, None, None );
+	addMTLPixelFormatDesc( ASTC_5x4_LDR, ASTC_5x4, None, None );
+	addMTLPixelFormatDesc( ASTC_5x4_sRGB, ASTC_5x4, None, None );
+	addMTLPixelFormatDesc( ASTC_5x5_LDR, ASTC_5x5, None, None );
+	addMTLPixelFormatDesc( ASTC_5x5_sRGB, ASTC_5x5, None, None );
+	addMTLPixelFormatDesc( ASTC_6x5_LDR, ASTC_6x5, None, None );
+	addMTLPixelFormatDesc( ASTC_6x5_sRGB, ASTC_6x5, None, None );
+	addMTLPixelFormatDesc( ASTC_6x6_LDR, ASTC_6x6, None, None );
+	addMTLPixelFormatDesc( ASTC_6x6_sRGB, ASTC_6x6, None, None );
+	addMTLPixelFormatDesc( ASTC_8x5_LDR, ASTC_8x5, None, None );
+	addMTLPixelFormatDesc( ASTC_8x5_sRGB, ASTC_8x5, None, None );
+	addMTLPixelFormatDesc( ASTC_8x6_LDR, ASTC_8x6, None, None );
+	addMTLPixelFormatDesc( ASTC_8x6_sRGB, ASTC_8x6, None, None );
+	addMTLPixelFormatDesc( ASTC_8x8_LDR, ASTC_8x8, None, None );
+	addMTLPixelFormatDesc( ASTC_8x8_sRGB, ASTC_8x8, None, None );
+	addMTLPixelFormatDesc( ASTC_10x5_LDR, ASTC_10x5, None, None );
+	addMTLPixelFormatDesc( ASTC_10x5_sRGB, ASTC_10x5, None, None );
+	addMTLPixelFormatDesc( ASTC_10x6_LDR, ASTC_10x6, None, None );
+	addMTLPixelFormatDesc( ASTC_10x6_sRGB, ASTC_10x6, None, None );
+	addMTLPixelFormatDesc( ASTC_10x8_LDR, ASTC_10x8, None, None );
+	addMTLPixelFormatDesc( ASTC_10x8_sRGB, ASTC_10x8, None, None );
+	addMTLPixelFormatDesc( ASTC_10x10_LDR, ASTC_10x10, None, None );
+	addMTLPixelFormatDesc( ASTC_10x10_sRGB, ASTC_10x10, None, None );
+	addMTLPixelFormatDesc( ASTC_12x10_LDR, ASTC_12x10, None, None );
+	addMTLPixelFormatDesc( ASTC_12x10_sRGB, ASTC_12x10, None, None );
+	addMTLPixelFormatDesc( ASTC_12x12_LDR, ASTC_12x12, None, None );
+	addMTLPixelFormatDesc( ASTC_12x12_sRGB, ASTC_12x12, None, None );
 
-	addMTLPixelFormatDesc( BC1_RGBA, None, RF );
-	addMTLPixelFormatDesc( BC1_RGBA_sRGB, None, RF );
-	addMTLPixelFormatDesc( BC1_RGBA, None, RF );
-	addMTLPixelFormatDesc( BC1_RGBA_sRGB, None, RF );
-	addMTLPixelFormatDesc( BC2_RGBA, None, RF );
-	addMTLPixelFormatDesc( BC2_RGBA_sRGB, None, RF );
-	addMTLPixelFormatDesc( BC3_RGBA, None, RF );
-	addMTLPixelFormatDesc( BC3_RGBA_sRGB, None, RF );
-	addMTLPixelFormatDesc( BC4_RUnorm, None, RF );
-	addMTLPixelFormatDesc( BC4_RSnorm, None, RF );
-	addMTLPixelFormatDesc( BC5_RGUnorm, None, RF );
-	addMTLPixelFormatDesc( BC5_RGSnorm, None, RF );
-	addMTLPixelFormatDesc( BC6H_RGBUfloat, None, RF );
-	addMTLPixelFormatDesc( BC6H_RGBFloat, None, RF );
-	addMTLPixelFormatDesc( BC7_RGBAUnorm, None, RF );
-	addMTLPixelFormatDesc( BC7_RGBAUnorm_sRGB, None, RF );
+	addMTLPixelFormatDesc( BC1_RGBA, BC1_RGBA, None, RF );
+	addMTLPixelFormatDesc( BC1_RGBA_sRGB, BC1_RGBA, None, RF );
+	addMTLPixelFormatDesc( BC2_RGBA, BC2_RGBA, None, RF );
+	addMTLPixelFormatDesc( BC2_RGBA_sRGB, BC2_RGBA, None, RF );
+	addMTLPixelFormatDesc( BC3_RGBA, BC3_RGBA, None, RF );
+	addMTLPixelFormatDesc( BC3_RGBA_sRGB, BC3_RGBA, None, RF );
+	addMTLPixelFormatDesc( BC4_RUnorm, BC4_R, None, RF );
+	addMTLPixelFormatDesc( BC4_RSnorm, BC4_R, None, RF );
+	addMTLPixelFormatDesc( BC5_RGUnorm, BC5_RG, None, RF );
+	addMTLPixelFormatDesc( BC5_RGSnorm, BC5_RG, None, RF );
+	addMTLPixelFormatDesc( BC6H_RGBUfloat, BC6H_RGB, None, RF );
+	addMTLPixelFormatDesc( BC6H_RGBFloat, BC6H_RGB, None, RF );
+	addMTLPixelFormatDesc( BC7_RGBAUnorm, BC7_RGBA, None, RF );
+	addMTLPixelFormatDesc( BC7_RGBAUnorm_sRGB, BC7_RGBA, None, RF );
 
 	// YUV pixel formats
-	addMTLPixelFormatDesc( GBGR422, RF, RF );
-	addMTLPixelFormatDesc( BGRG422, RF, RF );
+	addMTLPixelFormatDesc( GBGR422, None, RF, RF );
+	addMTLPixelFormatDesc( BGRG422, None, RF, RF );
 
 	// Extended range and wide color pixel formats
-	addMTLPixelFormatDesc( BGRA10_XR, None, None );
-	addMTLPixelFormatDesc( BGRA10_XR_sRGB, None, None );
-	addMTLPixelFormatDesc( BGR10_XR, None, None );
-	addMTLPixelFormatDesc( BGR10_XR_sRGB, None, None );
-	addMTLPixelFormatDesc( BGR10A2Unorm, None, None );
+	addMTLPixelFormatDesc( BGRA10_XR, BGRA10_XR, None, None );
+	addMTLPixelFormatDesc( BGRA10_XR_sRGB, BGRA10_XR, None, None );
+	addMTLPixelFormatDesc( BGR10_XR, BGR10_XR, None, None );
+	addMTLPixelFormatDesc( BGR10_XR_sRGB, BGR10_XR, None, None );
+	addMTLPixelFormatDesc( BGR10A2Unorm, Color32, None, None );
 
 	// Depth and stencil pixel formats
-	addMTLPixelFormatDesc( Depth16Unorm, None, None );
-	addMTLPixelFormatDesc( Depth32Float, DRM, DRFMR );
-	addMTLPixelFormatDesc( Stencil8, DRM, DRM );
-	addMTLPixelFormatDesc( Depth24Unorm_Stencil8, None, None );
-	addMTLPixelFormatDesc( Depth32Float_Stencil8, DRM, DRFMR );
-	addMTLPixelFormatDesc( X24_Stencil8, None, DRM );
-	addMTLPixelFormatDesc( X32_Stencil8, DRM, DRM );
+	addMTLPixelFormatDesc( Depth16Unorm, None, None, None );
+	addMTLPixelFormatDesc( Depth32Float, None, DRM, DRFMR );
+	addMTLPixelFormatDesc( Stencil8, None, DRM, DRMR );
+	addMTLPixelFormatDesc( Depth24Unorm_Stencil8, Depth24_Stencil8, None, None );
+	addMTLPixelFormatDesc( Depth32Float_Stencil8, Depth32_Stencil8, DRM, DRFMR );
+	addMTLPixelFormatDesc( X24_Stencil8, Depth24_Stencil8, None, DRMR );
+	addMTLPixelFormatDesc( X32_Stencil8, Depth32_Stencil8, DRM, DRMR );
 
 	// When adding to this list, be sure to ensure _mtlPixelFormatCount is large enough for the format count
 }
@@ -1032,7 +1042,7 @@
 	MVKAssert(fmtIdx < _mtlVertexFormatCount, "Attempting to describe %d MTLVertexFormats, but only have space for %d. Increase the value of _mtlVertexFormatCount", fmtIdx + 1, _mtlVertexFormatCount);  \
 	_mtlVertexFormatDescriptions[fmtIdx++] = { .mtlVertexFormat = MTLVertexFormat ##MTL_VTX_FMT, VK_FORMAT_UNDEFINED,  \
                                                mvkSelectPlatformValue<MVKMTLFmtCaps>(kMVKMTLFmtCaps ##MACOS_CAPS, kMVKMTLFmtCaps ##IOS_CAPS),  \
-                                               "MTLVertexFormat" #MTL_VTX_FMT }
+                                               MVKMTLViewClass::None, "MTLVertexFormat" #MTL_VTX_FMT }
 
 void MVKPixelFormats::initMTLVertexFormatCapabilities() {
 
@@ -1330,6 +1340,7 @@
 
 	addFeatSetMTLPixFmtCaps( tvOS_GPUFamily2_v1, Depth32Float, DRMR );
 	addFeatSetMTLPixFmtCaps( tvOS_GPUFamily2_v1, Depth32Float_Stencil8, DRMR );
+	addFeatSetMTLPixFmtCaps( tvOS_GPUFamily2_v1, Stencil8, DRMR );
 
 	addFeatSetMTLPixFmtCaps(tvOS_GPUFamily2_v1, BGRA10_XR, All );
 	addFeatSetMTLPixFmtCaps(tvOS_GPUFamily2_v1, BGRA10_XR_sRGB, All );
@@ -1468,6 +1479,7 @@
 
 	addFeatSetMTLPixFmtCaps( iOS_GPUFamily3_v1, Depth32Float, DRMR );
 	addFeatSetMTLPixFmtCaps( iOS_GPUFamily3_v1, Depth32Float_Stencil8, DRMR );
+	addFeatSetMTLPixFmtCaps( iOS_GPUFamily3_v1, Stencil8, DRMR );
 
 	addFeatSetMTLPixFmtCaps( iOS_GPUFamily3_v2, BGRA10_XR, All );
 	addFeatSetMTLPixFmtCaps( iOS_GPUFamily3_v2, BGRA10_XR_sRGB, All );
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
index ca51bd0..7706fd3 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
@@ -143,6 +143,9 @@
 	MVKSmallVector<VkAttachmentReference2, kMVKDefaultAttachmentCount> _resolveAttachments;
 	MVKSmallVector<uint32_t, kMVKDefaultAttachmentCount> _preserveAttachments;
 	VkAttachmentReference2 _depthStencilAttachment;
+	VkAttachmentReference2 _depthStencilResolveAttachment;
+	VkResolveModeFlagBits _depthResolveMode = VK_RESOLVE_MODE_NONE;
+	VkResolveModeFlagBits _stencilResolveMode = VK_RESOLVE_MODE_NONE;
 	id<MTLTexture> _mtlDummyTex = nil;
 	VkSampleCountFlagBits _defaultSampleCount = VK_SAMPLE_COUNT_1_BIT;
 };
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm
index 464f1d0..52d21bc 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm
@@ -21,6 +21,10 @@
 #include "MVKCommandBuffer.h"
 #include "MVKFoundation.h"
 #include "mvk_datatypes.hpp"
+#include "MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h"
+#if MVK_MACOS_OR_IOS
+#include "MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h"
+#endif
 #include <cassert>
 
 using namespace std;
@@ -224,17 +228,31 @@
 
 	// Populate the Metal depth and stencil attachments
 	uint32_t dsRPAttIdx = _depthStencilAttachment.attachment;
+	uint32_t dsRslvRPAttIdx = _depthStencilResolveAttachment.attachment;
 	if (dsRPAttIdx != VK_ATTACHMENT_UNUSED) {
 		MVKRenderPassAttachment* dsMVKRPAtt = &_renderPass->_attachments[dsRPAttIdx];
 		MVKImageView* dsImage = framebuffer->getAttachment(dsRPAttIdx);
+		MVKImageView* dsRslvImage = nullptr;
 		MTLPixelFormat mtlDSFormat = dsImage->getMTLPixelFormat(0);
 
+		if (dsRslvRPAttIdx != VK_ATTACHMENT_UNUSED) {
+			dsRslvImage = framebuffer->getAttachment(dsRslvRPAttIdx);
+		}
+
 		if (pixFmts->isDepthFormat(mtlDSFormat)) {
 			MTLRenderPassDepthAttachmentDescriptor* mtlDepthAttDesc = mtlRPDesc.depthAttachment;
+			bool hasResolveAttachment = (dsRslvRPAttIdx != VK_ATTACHMENT_UNUSED && _depthResolveMode != VK_RESOLVE_MODE_NONE);
+			if (hasResolveAttachment) {
+				dsRslvImage->populateMTLRenderPassAttachmentDescriptorResolve(mtlDepthAttDesc);
+				mtlDepthAttDesc.depthResolveFilterMVK = mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBits(_depthResolveMode);
+				if (isMultiview()) {
+					mtlDepthAttDesc.resolveSlice += getFirstViewIndexInMetalPass(passIdx);
+				}
+			}
 			dsImage->populateMTLRenderPassAttachmentDescriptor(mtlDepthAttDesc);
 			if (dsMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlDepthAttDesc, this,
                                                                       isRenderingEntireAttachment,
-                                                                      false, false,
+                                                                      hasResolveAttachment, false,
                                                                       loadOverride)) {
                 mtlDepthAttDesc.clearDepth = pixFmts->getMTLClearDepthValue(clearValues[dsRPAttIdx]);
 			}
@@ -244,10 +262,20 @@
 		}
 		if (pixFmts->isStencilFormat(mtlDSFormat)) {
 			MTLRenderPassStencilAttachmentDescriptor* mtlStencilAttDesc = mtlRPDesc.stencilAttachment;
+			bool hasResolveAttachment = (dsRslvRPAttIdx != VK_ATTACHMENT_UNUSED && _stencilResolveMode != VK_RESOLVE_MODE_NONE);
+			if (hasResolveAttachment) {
+				dsRslvImage->populateMTLRenderPassAttachmentDescriptorResolve(mtlStencilAttDesc);
+#if MVK_MACOS_OR_IOS
+				mtlStencilAttDesc.stencilResolveFilterMVK = mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBits(_stencilResolveMode);
+#endif
+				if (isMultiview()) {
+					mtlStencilAttDesc.resolveSlice += getFirstViewIndexInMetalPass(passIdx);
+				}
+			}
 			dsImage->populateMTLRenderPassAttachmentDescriptor(mtlStencilAttDesc);
 			if (dsMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlStencilAttDesc, this,
                                                                       isRenderingEntireAttachment,
-                                                                      false, true,
+                                                                      hasResolveAttachment, true,
                                                                       loadOverride)) {
 				mtlStencilAttDesc.clearStencil = pixFmts->getMTLClearStencilValue(clearValues[dsRPAttIdx]);
 			}
@@ -337,8 +365,11 @@
     }
     uint32_t dsRPAttIdx = _depthStencilAttachment.attachment;
     if (dsRPAttIdx != VK_ATTACHMENT_UNUSED) {
-        _renderPass->_attachments[dsRPAttIdx].encodeStoreAction(cmdEncoder, this, isRenderingEntireAttachment, false, 0, false, storeOverride);
-        _renderPass->_attachments[dsRPAttIdx].encodeStoreAction(cmdEncoder, this, isRenderingEntireAttachment, false, 0, true, storeOverride);
+        bool hasResolveAttachment = _depthStencilResolveAttachment.attachment != VK_ATTACHMENT_UNUSED;
+        bool hasDepthResolveAttachment = hasResolveAttachment && _depthResolveMode != VK_RESOLVE_MODE_NONE;
+        bool hasStencilResolveAttachment = hasResolveAttachment && _stencilResolveMode != VK_RESOLVE_MODE_NONE;
+        _renderPass->_attachments[dsRPAttIdx].encodeStoreAction(cmdEncoder, this, isRenderingEntireAttachment, hasDepthResolveAttachment, 0, false, storeOverride);
+        _renderPass->_attachments[dsRPAttIdx].encodeStoreAction(cmdEncoder, this, isRenderingEntireAttachment, hasStencilResolveAttachment, 0, true, storeOverride);
     }
 }
 
@@ -416,6 +447,7 @@
 		}
 	}
 	if (_depthStencilAttachment.attachment == rpAttIdx) { mvkEnableFlags(caps, kMVKMTLFmtCapsDSAtt); }
+	if (_depthStencilResolveAttachment.attachment == rpAttIdx) { mvkEnableFlags(caps, kMVKMTLFmtCapsResolve); }
 
 	return caps;
 }
@@ -464,6 +496,8 @@
 		_depthStencilAttachment.attachment = VK_ATTACHMENT_UNUSED;
 	}
 
+	_depthStencilResolveAttachment.attachment = VK_ATTACHMENT_UNUSED;
+
 	_preserveAttachments.reserve(pCreateInfo->preserveAttachmentCount);
 	for (uint32_t i = 0; i < pCreateInfo->preserveAttachmentCount; i++) {
 		_preserveAttachments.push_back(pCreateInfo->pPreserveAttachments[i]);
@@ -472,6 +506,18 @@
 
 MVKRenderSubpass::MVKRenderSubpass(MVKRenderPass* renderPass,
 								   const VkSubpassDescription2* pCreateInfo) {
+
+	VkSubpassDescriptionDepthStencilResolve* pDSResolveInfo = nullptr;
+	for (auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
+		switch (next->sType) {
+		case VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE:
+			pDSResolveInfo = (VkSubpassDescriptionDepthStencilResolve*)next;
+			break;
+		default:
+			break;
+		}
+	}
+
 	_renderPass = renderPass;
 	_subpassIndex = (uint32_t)_renderPass->_subpasses.size();
 	_viewMask = pCreateInfo->viewMask;
@@ -500,6 +546,14 @@
 		_depthStencilAttachment.attachment = VK_ATTACHMENT_UNUSED;
 	}
 
+	if (pDSResolveInfo && pDSResolveInfo->pDepthStencilResolveAttachment) {
+		_depthStencilResolveAttachment = *pDSResolveInfo->pDepthStencilResolveAttachment;
+		_depthResolveMode = pDSResolveInfo->depthResolveMode;
+		_stencilResolveMode = pDSResolveInfo->stencilResolveMode;
+	} else {
+		_depthStencilResolveAttachment.attachment = VK_ATTACHMENT_UNUSED;
+	}
+
 	_preserveAttachments.reserve(pCreateInfo->preserveAttachmentCount);
 	for (uint32_t i = 0; i < pCreateInfo->preserveAttachmentCount; i++) {
 		_preserveAttachments.push_back(pCreateInfo->pPreserveAttachments[i]);
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index 805c7ac..cd54eb6 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -44,6 +44,7 @@
 MVK_EXTENSION(KHR_bind_memory2, KHR_BIND_MEMORY_2, DEVICE)
 MVK_EXTENSION(KHR_create_renderpass2, KHR_CREATE_RENDERPASS_2, DEVICE)
 MVK_EXTENSION(KHR_dedicated_allocation, KHR_DEDICATED_ALLOCATION, DEVICE)
+MVK_EXTENSION(KHR_depth_stencil_resolve, KHR_DEPTH_STENCIL_RESOLVE, DEVICE)
 MVK_EXTENSION(KHR_descriptor_update_template, KHR_DESCRIPTOR_UPDATE_TEMPLATE, DEVICE)
 MVK_EXTENSION(KHR_device_group, KHR_DEVICE_GROUP, DEVICE)
 MVK_EXTENSION(KHR_device_group_creation, KHR_DEVICE_GROUP_CREATION, INSTANCE)
diff --git a/MoltenVK/MoltenVK/OS/MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h b/MoltenVK/MoltenVK/OS/MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h
new file mode 100644
index 0000000..4ea08a5
--- /dev/null
+++ b/MoltenVK/MoltenVK/OS/MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h
@@ -0,0 +1,34 @@
+/*
+ * MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h
+ *
+ * Copyright (c) 2020 Chip Davis for CodeWeavers
+ *
+ * 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.
+ */
+
+#pragma once
+
+#import <Metal/Metal.h>
+
+/** Extensions to MTLRenderPassDepthAttachmentDescriptor to support MoltenVK. */
+@interface MTLRenderPassDepthAttachmentDescriptor (MoltenVK)
+
+/**
+ * Replacement for the depthResolveFilter property.
+ *
+ * This property allows support under all OS versions. Delegates to the depthResolveFilter
+ * property if it is available. Otherwise, returns MTLMultisampleDepthResolveFilterSample0 when read and does nothing when set.
+ */
+@property(nonatomic, readwrite) MTLMultisampleDepthResolveFilter depthResolveFilterMVK;
+
+@end
diff --git a/MoltenVK/MoltenVK/OS/MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m b/MoltenVK/MoltenVK/OS/MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m
new file mode 100644
index 0000000..eb249da
--- /dev/null
+++ b/MoltenVK/MoltenVK/OS/MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m
@@ -0,0 +1,42 @@
+/*
+ * MTLRenderPassDepthAttachmentDescriptor+MoltenVK.m
+ *
+ * Copyright (c) 2020 Chip Davis for CodeWeavers
+ *
+ * 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.
+ */
+
+
+#include "MTLRenderPassDepthAttachmentDescriptor+MoltenVK.h"
+#include "MVKEnvironment.h"
+
+@implementation MTLRenderPassDepthAttachmentDescriptor (MoltenVK)
+
+-(MTLMultisampleDepthResolveFilter) depthResolveFilterMVK {
+
+	if ( [self respondsToSelector: @selector(depthResolveFilter)] ) {
+		return self.depthResolveFilter;
+	}
+	return MTLMultisampleDepthResolveFilterSample0;
+
+}
+
+-(void) setDepthResolveFilterMVK: (MTLMultisampleDepthResolveFilter) filter {
+
+	if ( [self respondsToSelector: @selector(setDepthResolveFilter:)] ) {
+		self.depthResolveFilter = filter;
+	}
+
+}
+
+@end
diff --git a/MoltenVK/MoltenVK/OS/MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h b/MoltenVK/MoltenVK/OS/MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h
new file mode 100644
index 0000000..d2fb4fe
--- /dev/null
+++ b/MoltenVK/MoltenVK/OS/MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h
@@ -0,0 +1,34 @@
+/*
+ * MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h
+ *
+ * Copyright (c) 2020 Chip Davis for CodeWeavers
+ *
+ * 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.
+ */
+
+#pragma once
+
+#import <Metal/Metal.h>
+
+/** Extensions to MTLRenderPassStencilAttachmentDescriptor to support MoltenVK. */
+@interface MTLRenderPassStencilAttachmentDescriptor (MoltenVK)
+
+/**
+ * Replacement for the stencilResolveFilter property.
+ *
+ * This property allows support under all OS versions. Delegates to the stencilResolveFilter
+ * property if it is available. Otherwise, returns MTLMultisampleStencilResolveFilterSample0 when read and does nothing when set.
+ */
+@property(nonatomic, readwrite) MTLMultisampleStencilResolveFilter stencilResolveFilterMVK;
+
+@end
diff --git a/MoltenVK/MoltenVK/OS/MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m b/MoltenVK/MoltenVK/OS/MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m
new file mode 100644
index 0000000..4cbda58
--- /dev/null
+++ b/MoltenVK/MoltenVK/OS/MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m
@@ -0,0 +1,42 @@
+/*
+ * MTLRenderPassStencilAttachmentDescriptor+MoltenVK.m
+ *
+ * Copyright (c) 2020 Chip Davis for CodeWeavers
+ *
+ * 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.
+ */
+
+
+#include "MTLRenderPassStencilAttachmentDescriptor+MoltenVK.h"
+#include "MVKEnvironment.h"
+
+@implementation MTLRenderPassStencilAttachmentDescriptor (MoltenVK)
+
+-(MTLMultisampleStencilResolveFilter) stencilResolveFilterMVK {
+
+	if ( [self respondsToSelector: @selector(stencilResolveFilter)] ) {
+		return self.stencilResolveFilter;
+	}
+	return MTLMultisampleStencilResolveFilterSample0;
+
+}
+
+-(void) setStencilResolveFilterMVK: (MTLMultisampleStencilResolveFilter) filter {
+
+	if ( [self respondsToSelector: @selector(setStencilResolveFilter:)] ) {
+		self.stencilResolveFilter = filter;
+	}
+
+}
+
+@end
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp
index be0c519..7a2a5d9 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.hpp
@@ -62,6 +62,14 @@
 MTLStoreAction mvkMTLStoreActionFromVkAttachmentStoreOpInObj(VkAttachmentStoreOp vkStoreOp, bool hasResolveAttachment, MVKBaseObject* mvkObj);
 #define mvkMTLStoreActionFromVkAttachmentStoreOp(vkStoreOp, hasResolveAttachment) mvkMTLStoreActionFromVkAttachmentStoreOpInObj(vkStoreOp, hasResolveAttachment, this)
 
+MTLMultisampleDepthResolveFilter mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBitsInObj(VkResolveModeFlagBits vkResolveMode, MVKBaseObject* mvkObj);
+#define mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBits(vkResolveMode) mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBitsInObj(vkResolveMode, this)
+
+#if MVK_MACOS_OR_IOS
+MTLMultisampleStencilResolveFilter mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBitsInObj(VkResolveModeFlagBits vkResolveMode, MVKBaseObject* mvkObj);
+#define mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBits(vkResolveMode) mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBitsInObj(vkResolveMode, this)
+#endif
+
 MVKShaderStage mvkShaderStageFromVkShaderStageFlagBitsInObj(VkShaderStageFlagBits vkStage, MVKBaseObject* mvkObj);
 #define mvkShaderStageFromVkShaderStageFlagBits(vkStage) mvkShaderStageFromVkShaderStageFlagBitsInObj(vkStage, this)
 
diff --git a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
index 57d6de1..8bee86e 100644
--- a/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
+++ b/MoltenVK/MoltenVK/Vulkan/mvk_datatypes.mm
@@ -509,6 +509,40 @@
 	}
 }
 
+#undef mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBits
+MVK_PUBLIC_SYMBOL MTLMultisampleDepthResolveFilter mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBits(VkResolveModeFlagBits vkResolveMode) {
+	return mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBitsInObj(vkResolveMode, nullptr);
+}
+
+MTLMultisampleDepthResolveFilter mvkMTLMultisampleDepthResolveFilterFromVkResolveModeFlagBitsInObj(VkResolveModeFlagBits vkResolveMode, MVKBaseObject* mvkObj) {
+	switch (vkResolveMode) {
+		case VK_RESOLVE_MODE_SAMPLE_ZERO_BIT:	return MTLMultisampleDepthResolveFilterSample0;
+		case VK_RESOLVE_MODE_MIN_BIT:			return MTLMultisampleDepthResolveFilterMin;
+		case VK_RESOLVE_MODE_MAX_BIT:			return MTLMultisampleDepthResolveFilterMax;
+
+		default:
+			MVKBaseObject::reportError(mvkObj, VK_ERROR_FORMAT_NOT_SUPPORTED, "VkResolveModeFlagBits value %d is not supported.", vkResolveMode);
+			return MTLMultisampleDepthResolveFilterSample0;
+	}
+}
+
+#if MVK_MACOS_OR_IOS
+#undef mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBits
+MVK_PUBLIC_SYMBOL MTLMultisampleStencilResolveFilter mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBits(VkResolveModeFlagBits vkResolveMode) {
+	return mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBitsInObj(vkResolveMode, nullptr);
+}
+
+MTLMultisampleStencilResolveFilter mvkMTLMultisampleStencilResolveFilterFromVkResolveModeFlagBitsInObj(VkResolveModeFlagBits vkResolveMode, MVKBaseObject* mvkObj) {
+	switch (vkResolveMode) {
+		case VK_RESOLVE_MODE_SAMPLE_ZERO_BIT:	return MTLMultisampleStencilResolveFilterSample0;
+
+		default:
+			MVKBaseObject::reportError(mvkObj, VK_ERROR_FORMAT_NOT_SUPPORTED, "VkResolveModeFlagBits value %d is not supported.", vkResolveMode);
+			return MTLMultisampleStencilResolveFilterSample0;
+	}
+}
+#endif
+
 MVK_PUBLIC_SYMBOL MTLViewport mvkMTLViewportFromVkViewport(VkViewport vkViewport) {
 	MTLViewport mtlViewport;
 	mtlViewport.originX	= vkViewport.x;