Add support for VK_EXT_debug_marker extension.

Move MVKVulkanAPIObject to its own .h/mm files.
Add MVKCmdDebug.h/mm files.
Change extension on MVKExtensions.cpp and MVKBaseObject.cpp to .mm.
Remove unused command use in MVKQueue submit() and waitIdle() functions.
MVKCommandPool constructor use default isPooling value in MVKCommandTypePool constructors.
MVKSwapchainImage pass image index in constructor.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index d363c23..a6b1a33 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -18,8 +18,10 @@
 
 Released TBD
 
-- Support the `VK_EXT_debug_report` extension.
-- Support the `VK_NV_glsl_shader` extension.
+- Add support for extensions:
+	- `VK_EXT_debug_report`
+	- `VK_EXT_debug_marker`
+	- `VK_NV_glsl_shader`
 - Change log indication of error in logs from `[***MoltenVK ERROR***]` to 
   `[mvk-error]`, for consistency with other log level indications. 
 - Tessellation fixes:
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index 74f1b38..2923d63 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -25,8 +25,8 @@
 		A9096E5F1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */; };
 		A909F65F213B190700FCD6BE /* MVKExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = A909F65A213B190600FCD6BE /* MVKExtensions.h */; };
 		A909F660213B190700FCD6BE /* MVKExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = A909F65A213B190600FCD6BE /* MVKExtensions.h */; };
-		A909F661213B190700FCD6BE /* MVKExtensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A909F65E213B190700FCD6BE /* MVKExtensions.cpp */; };
-		A909F662213B190700FCD6BE /* MVKExtensions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A909F65E213B190700FCD6BE /* MVKExtensions.cpp */; };
+		A909F661213B190700FCD6BE /* MVKExtensions.mm in Sources */ = {isa = PBXBuildFile; fileRef = A909F65E213B190700FCD6BE /* MVKExtensions.mm */; };
+		A909F662213B190700FCD6BE /* MVKExtensions.mm in Sources */ = {isa = PBXBuildFile; fileRef = A909F65E213B190700FCD6BE /* MVKExtensions.mm */; };
 		A90C8DEA1F45354D009CB32C /* MVKCommandEncodingPool.h in Headers */ = {isa = PBXBuildFile; fileRef = A90C8DE81F45354D009CB32C /* MVKCommandEncodingPool.h */; };
 		A90C8DEB1F45354D009CB32C /* MVKCommandEncodingPool.h in Headers */ = {isa = PBXBuildFile; fileRef = A90C8DE81F45354D009CB32C /* MVKCommandEncodingPool.h */; };
 		A90C8DEC1F45354D009CB32C /* MVKCommandEncodingPool.mm in Sources */ = {isa = PBXBuildFile; fileRef = A90C8DE91F45354D009CB32C /* MVKCommandEncodingPool.mm */; };
@@ -157,8 +157,8 @@
 		A95B7D6A1D3EE486003183D3 /* MVKCommandEncoderState.h in Headers */ = {isa = PBXBuildFile; fileRef = A95B7D671D3EE486003183D3 /* MVKCommandEncoderState.h */; };
 		A95B7D6B1D3EE486003183D3 /* MVKCommandEncoderState.mm in Sources */ = {isa = PBXBuildFile; fileRef = A95B7D681D3EE486003183D3 /* MVKCommandEncoderState.mm */; };
 		A95B7D6C1D3EE486003183D3 /* MVKCommandEncoderState.mm in Sources */ = {isa = PBXBuildFile; fileRef = A95B7D681D3EE486003183D3 /* MVKCommandEncoderState.mm */; };
-		A981494D1FB6A3F7005F00B4 /* MVKBaseObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A98149411FB6A3F7005F00B4 /* MVKBaseObject.cpp */; };
-		A981494E1FB6A3F7005F00B4 /* MVKBaseObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A98149411FB6A3F7005F00B4 /* MVKBaseObject.cpp */; };
+		A981494D1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */; };
+		A981494E1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */; };
 		A981494F1FB6A3F7005F00B4 /* MVKBaseObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */; };
 		A98149501FB6A3F7005F00B4 /* MVKBaseObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */; };
 		A98149511FB6A3F7005F00B4 /* MVKEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = A98149431FB6A3F7005F00B4 /* MVKEnvironment.h */; };
@@ -179,6 +179,14 @@
 		A98149641FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h in Headers */ = {isa = PBXBuildFile; fileRef = A981494C1FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h */; };
 		A981496B1FB6A998005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A981496A1FB6A998005F00B4 /* MVKStrings.h */; };
 		A981496C1FB6A998005F00B4 /* MVKStrings.h in Headers */ = {isa = PBXBuildFile; fileRef = A981496A1FB6A998005F00B4 /* MVKStrings.h */; };
+		A99C90EE229455B300A061DA /* MVKCmdDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = A99C90EC229455B200A061DA /* MVKCmdDebug.h */; };
+		A99C90EF229455B300A061DA /* MVKCmdDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = A99C90EC229455B200A061DA /* MVKCmdDebug.h */; };
+		A99C90F0229455B300A061DA /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; };
+		A99C90F1229455B300A061DA /* MVKCmdDebug.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C90ED229455B300A061DA /* MVKCmdDebug.mm */; };
+		A99C91022295FAC600A061DA /* MVKVulkanAPIObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C91002295FAC500A061DA /* MVKVulkanAPIObject.mm */; };
+		A99C91032295FAC600A061DA /* MVKVulkanAPIObject.mm in Sources */ = {isa = PBXBuildFile; fileRef = A99C91002295FAC500A061DA /* MVKVulkanAPIObject.mm */; };
+		A99C91042295FAC600A061DA /* MVKVulkanAPIObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A99C91012295FAC500A061DA /* MVKVulkanAPIObject.h */; };
+		A99C91052295FAC600A061DA /* MVKVulkanAPIObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A99C91012295FAC500A061DA /* MVKVulkanAPIObject.h */; };
 		A9B51BD7225E986A00AC74D2 /* MVKOSExtensions.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9B51BD2225E986A00AC74D2 /* MVKOSExtensions.mm */; };
 		A9B51BD8225E986A00AC74D2 /* MVKOSExtensions.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9B51BD2225E986A00AC74D2 /* MVKOSExtensions.mm */; };
 		A9B51BD9225E986A00AC74D2 /* MVKOSExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = A9B51BD6225E986A00AC74D2 /* MVKOSExtensions.h */; };
@@ -299,7 +307,7 @@
 		A9096E5C1F81E16300DFBEA6 /* MVKCmdDispatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVKCmdDispatch.h; sourceTree = "<group>"; };
 		A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCmdDispatch.mm; sourceTree = "<group>"; };
 		A909F65A213B190600FCD6BE /* MVKExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKExtensions.h; sourceTree = "<group>"; };
-		A909F65E213B190700FCD6BE /* MVKExtensions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MVKExtensions.cpp; sourceTree = "<group>"; };
+		A909F65E213B190700FCD6BE /* MVKExtensions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKExtensions.mm; sourceTree = "<group>"; };
 		A90C8DE81F45354D009CB32C /* MVKCommandEncodingPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKCommandEncodingPool.h; sourceTree = "<group>"; };
 		A90C8DE91F45354D009CB32C /* MVKCommandEncodingPool.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCommandEncodingPool.mm; sourceTree = "<group>"; };
 		A93E832E2121C5D3001FEBD4 /* MVKGPUCapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKGPUCapture.h; sourceTree = "<group>"; };
@@ -365,7 +373,7 @@
 		A95870F71C90D29F009EB096 /* MVKCommandResourceFactory.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCommandResourceFactory.mm; sourceTree = "<group>"; };
 		A95B7D671D3EE486003183D3 /* MVKCommandEncoderState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKCommandEncoderState.h; sourceTree = "<group>"; };
 		A95B7D681D3EE486003183D3 /* MVKCommandEncoderState.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCommandEncoderState.mm; sourceTree = "<group>"; };
-		A98149411FB6A3F7005F00B4 /* MVKBaseObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MVKBaseObject.cpp; sourceTree = "<group>"; };
+		A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKBaseObject.mm; sourceTree = "<group>"; };
 		A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKBaseObject.h; sourceTree = "<group>"; };
 		A98149431FB6A3F7005F00B4 /* MVKEnvironment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKEnvironment.h; sourceTree = "<group>"; };
 		A98149441FB6A3F7005F00B4 /* MVKFoundation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKFoundation.h; sourceTree = "<group>"; };
@@ -376,6 +384,10 @@
 		A981494B1FB6A3F7005F00B4 /* MVKWatermarkShaderSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKWatermarkShaderSource.h; sourceTree = "<group>"; };
 		A981494C1FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKWatermarkTextureContent.h; sourceTree = "<group>"; };
 		A981496A1FB6A998005F00B4 /* MVKStrings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKStrings.h; sourceTree = "<group>"; };
+		A99C90EC229455B200A061DA /* MVKCmdDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKCmdDebug.h; sourceTree = "<group>"; };
+		A99C90ED229455B300A061DA /* MVKCmdDebug.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKCmdDebug.mm; sourceTree = "<group>"; };
+		A99C91002295FAC500A061DA /* MVKVulkanAPIObject.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKVulkanAPIObject.mm; sourceTree = "<group>"; };
+		A99C91012295FAC500A061DA /* MVKVulkanAPIObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKVulkanAPIObject.h; sourceTree = "<group>"; };
 		A9AD67C72054DD6C00ED3C08 /* vulkan */ = {isa = PBXFileReference; lastKnownFileType = folder; path = vulkan; sourceTree = "<group>"; };
 		A9B51BD2225E986A00AC74D2 /* MVKOSExtensions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKOSExtensions.mm; sourceTree = "<group>"; };
 		A9B51BD6225E986A00AC74D2 /* MVKOSExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKOSExtensions.h; sourceTree = "<group>"; };
@@ -432,6 +444,8 @@
 		A94FB76B1C7DFB4800632CA3 /* Commands */ = {
 			isa = PBXGroup;
 			children = (
+				A99C90EC229455B200A061DA /* MVKCmdDebug.h */,
+				A99C90ED229455B300A061DA /* MVKCmdDebug.mm */,
 				A9096E5C1F81E16300DFBEA6 /* MVKCmdDispatch.h */,
 				A9096E5D1F81E16300DFBEA6 /* MVKCmdDispatch.mm */,
 				A94FB7741C7DFB4800632CA3 /* MVKCmdDraw.h */,
@@ -499,6 +513,8 @@
 				A94FB79C1C7DFB4800632CA3 /* MVKSwapchain.mm */,
 				A94FB79D1C7DFB4800632CA3 /* MVKSync.h */,
 				A94FB79E1C7DFB4800632CA3 /* MVKSync.mm */,
+				A99C91012295FAC500A061DA /* MVKVulkanAPIObject.h */,
+				A99C91002295FAC500A061DA /* MVKVulkanAPIObject.mm */,
 			);
 			path = GPUObjects;
 			sourceTree = "<group>";
@@ -508,7 +524,7 @@
 			children = (
 				45003E6F214AD4C900E989CB /* MVKExtensions.def */,
 				A909F65A213B190600FCD6BE /* MVKExtensions.h */,
-				A909F65E213B190700FCD6BE /* MVKExtensions.cpp */,
+				A909F65E213B190700FCD6BE /* MVKExtensions.mm */,
 				A94FB7A01C7DFB4800632CA3 /* MVKLayers.h */,
 				A94FB7A11C7DFB4800632CA3 /* MVKLayers.mm */,
 			);
@@ -536,7 +552,7 @@
 				45557A5721CD83C3008868BD /* MVKDXTnCodec.def */,
 				83A4AD2521BD75570006C935 /* MVKVector.h */,
 				83A4AD2921BD75570006C935 /* MVKVectorAllocator.h */,
-				A98149411FB6A3F7005F00B4 /* MVKBaseObject.cpp */,
+				A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */,
 				A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */,
 				A98149431FB6A3F7005F00B4 /* MVKEnvironment.h */,
 				A98149451FB6A3F7005F00B4 /* MVKFoundation.cpp */,
@@ -661,6 +677,7 @@
 				83A4AD2A21BD75570006C935 /* MVKVector.h in Headers */,
 				A94FB7D41C7DFB4800632CA3 /* MVKCommandPool.h in Headers */,
 				A94FB80C1C7DFB4800632CA3 /* MVKShaderModule.h in Headers */,
+				A99C91042295FAC600A061DA /* MVKVulkanAPIObject.h in Headers */,
 				A94FB7C01C7DFB4800632CA3 /* MVKCmdQueries.h in Headers */,
 				A94FB7CC1C7DFB4800632CA3 /* MVKCommand.h in Headers */,
 				A981494F1FB6A3F7005F00B4 /* MVKBaseObject.h in Headers */,
@@ -681,6 +698,7 @@
 				A94FB7C81C7DFB4800632CA3 /* MVKCmdDraw.h in Headers */,
 				A94FB7D01C7DFB4800632CA3 /* MVKCommandBuffer.h in Headers */,
 				A9E53DF32100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h in Headers */,
+				A99C90EE229455B300A061DA /* MVKCmdDebug.h in Headers */,
 				A98149631FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h in Headers */,
 				A98149531FB6A3F7005F00B4 /* MVKFoundation.h in Headers */,
 				A94FB7E81C7DFB4800632CA3 /* MVKDeviceMemory.h in Headers */,
@@ -727,6 +745,7 @@
 				83A4AD2B21BD75570006C935 /* MVKVector.h in Headers */,
 				A94FB7D51C7DFB4800632CA3 /* MVKCommandPool.h in Headers */,
 				A94FB80D1C7DFB4800632CA3 /* MVKShaderModule.h in Headers */,
+				A99C91052295FAC600A061DA /* MVKVulkanAPIObject.h in Headers */,
 				A94FB7C11C7DFB4800632CA3 /* MVKCmdQueries.h in Headers */,
 				A94FB7CD1C7DFB4800632CA3 /* MVKCommand.h in Headers */,
 				A98149501FB6A3F7005F00B4 /* MVKBaseObject.h in Headers */,
@@ -747,6 +766,7 @@
 				A94FB7C91C7DFB4800632CA3 /* MVKCmdDraw.h in Headers */,
 				A94FB7D11C7DFB4800632CA3 /* MVKCommandBuffer.h in Headers */,
 				A9E53DF42100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.h in Headers */,
+				A99C90EF229455B300A061DA /* MVKCmdDebug.h in Headers */,
 				A98149641FB6A3F7005F00B4 /* MVKWatermarkTextureContent.h in Headers */,
 				A98149541FB6A3F7005F00B4 /* MVKFoundation.h in Headers */,
 				A94FB7E91C7DFB4800632CA3 /* MVKDeviceMemory.h in Headers */,
@@ -961,11 +981,12 @@
 				A9E53DFF21064F84002781DD /* MTLRenderPipelineDescriptor+MoltenVK.m in Sources */,
 				A94FB80A1C7DFB4800632CA3 /* MVKResource.mm in Sources */,
 				A94FB7E21C7DFB4800632CA3 /* MVKDescriptorSet.mm in Sources */,
+				A99C91022295FAC600A061DA /* MVKVulkanAPIObject.mm in Sources */,
 				A9E53DE72100B197002781DD /* MTLTextureDescriptor+MoltenVK.m in Sources */,
 				A95870FA1C90D29F009EB096 /* MVKCommandResourceFactory.mm in Sources */,
 				A90C8DEC1F45354D009CB32C /* MVKCommandEncodingPool.mm in Sources */,
 				A981495F1FB6A3F7005F00B4 /* MVKWatermark.mm in Sources */,
-				A981494D1FB6A3F7005F00B4 /* MVKBaseObject.cpp in Sources */,
+				A981494D1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */,
 				A9E53DE52100B197002781DD /* NSString+MoltenVK.mm in Sources */,
 				A94FB8321C7DFB4800632CA3 /* vulkan.mm in Sources */,
 				A94FB8121C7DFB4800632CA3 /* MVKSurface.mm in Sources */,
@@ -979,7 +1000,7 @@
 				A94FB7C61C7DFB4800632CA3 /* MVKCmdRenderPass.mm in Sources */,
 				A94FB7DE1C7DFB4800632CA3 /* MVKBuffer.mm in Sources */,
 				A94FB82A1C7DFB4800632CA3 /* mvk_datatypes.mm in Sources */,
-				A909F661213B190700FCD6BE /* MVKExtensions.cpp in Sources */,
+				A909F661213B190700FCD6BE /* MVKExtensions.mm in Sources */,
 				A98149551FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */,
 				A94FB7E61C7DFB4800632CA3 /* MVKDevice.mm in Sources */,
 				A9E53DF52100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */,
@@ -1004,6 +1025,7 @@
 				A9C96DD21DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */,
 				A9E53DE92100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */,
 				A9096E5E1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */,
+				A99C90F0229455B300A061DA /* MVKCmdDebug.mm in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -1015,11 +1037,12 @@
 				A9E53E0021064F84002781DD /* MTLRenderPipelineDescriptor+MoltenVK.m in Sources */,
 				A94FB80B1C7DFB4800632CA3 /* MVKResource.mm in Sources */,
 				A94FB7E31C7DFB4800632CA3 /* MVKDescriptorSet.mm in Sources */,
+				A99C91032295FAC600A061DA /* MVKVulkanAPIObject.mm in Sources */,
 				A9E53DE82100B197002781DD /* MTLTextureDescriptor+MoltenVK.m in Sources */,
 				A95870FB1C90D29F009EB096 /* MVKCommandResourceFactory.mm in Sources */,
 				A90C8DED1F45354D009CB32C /* MVKCommandEncodingPool.mm in Sources */,
 				A98149601FB6A3F7005F00B4 /* MVKWatermark.mm in Sources */,
-				A981494E1FB6A3F7005F00B4 /* MVKBaseObject.cpp in Sources */,
+				A981494E1FB6A3F7005F00B4 /* MVKBaseObject.mm in Sources */,
 				A9E53DE62100B197002781DD /* NSString+MoltenVK.mm in Sources */,
 				A94FB8331C7DFB4800632CA3 /* vulkan.mm in Sources */,
 				A94FB8131C7DFB4800632CA3 /* MVKSurface.mm in Sources */,
@@ -1033,7 +1056,7 @@
 				A94FB7C71C7DFB4800632CA3 /* MVKCmdRenderPass.mm in Sources */,
 				A94FB7DF1C7DFB4800632CA3 /* MVKBuffer.mm in Sources */,
 				A94FB82B1C7DFB4800632CA3 /* mvk_datatypes.mm in Sources */,
-				A909F662213B190700FCD6BE /* MVKExtensions.cpp in Sources */,
+				A909F662213B190700FCD6BE /* MVKExtensions.mm in Sources */,
 				A98149561FB6A3F7005F00B4 /* MVKFoundation.cpp in Sources */,
 				A94FB7E71C7DFB4800632CA3 /* MVKDevice.mm in Sources */,
 				A9E53DF62100B302002781DD /* MTLRenderPassDescriptor+MoltenVK.m in Sources */,
@@ -1058,6 +1081,7 @@
 				A9C96DD31DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */,
 				A9E53DEA2100B197002781DD /* CAMetalLayer+MoltenVK.m in Sources */,
 				A9096E5F1F81E16300DFBEA6 /* MVKCmdDispatch.mm in Sources */,
+				A99C90F1229455B300A061DA /* MVKCmdDebug.mm in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.h b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.h
new file mode 100644
index 0000000..a0028cf
--- /dev/null
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.h
@@ -0,0 +1,91 @@
+/*
+ * MVKCmdDebug.h
+ *
+ * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "MVKCommand.h"
+
+#import <Foundation/NSString.h>
+
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarker
+
+/**Abstract Vulkan class to support debug markers. */
+class MVKCmdDebugMarker : public MVKCommand {
+
+public:
+	void setContent(const char* pMarkerName, const float color[4]);
+
+    MVKCmdDebugMarker(MVKCommandTypePool<MVKCmdDebugMarker>* pool);
+
+	~MVKCmdDebugMarker() override;
+
+protected:
+	NSString* _markerName = nil;
+};
+
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarkerBegin
+
+/** Vulkan command to begin a marker region into the command buffer. */
+class MVKCmdDebugMarkerBegin : public MVKCmdDebugMarker {
+
+public:
+	void encode(MVKCommandEncoder* cmdEncoder) override;
+
+	MVKCmdDebugMarkerBegin(MVKCommandTypePool<MVKCmdDebugMarkerBegin>* pool);
+};
+
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarkerEnd
+
+/** Vulkan command to end an open marker region in the command buffer. */
+class MVKCmdDebugMarkerEnd : public MVKCommand {
+
+public:
+	void encode(MVKCommandEncoder* cmdEncoder) override;
+
+	MVKCmdDebugMarkerEnd(MVKCommandTypePool<MVKCmdDebugMarkerEnd>* pool);
+};
+
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarkerInsert
+
+	/** Vulkan command to insert a debug marker into the command encoder. */
+	class MVKCmdDebugMarkerInsert : public MVKCmdDebugMarker {
+
+	public:
+		void encode(MVKCommandEncoder* cmdEncoder) override;
+
+		MVKCmdDebugMarkerInsert(MVKCommandTypePool<MVKCmdDebugMarkerInsert>* pool);
+	};
+
+
+#pragma mark -
+#pragma mark Command creation functions
+
+void mvkCmdDebugMarkerBegin(MVKCommandBuffer* cmdBuff, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+
+void mvkCmdDebugMarkerEnd(MVKCommandBuffer* cmdBuff);
+
+void mvkCmdDebugMarkerInsert(MVKCommandBuffer* cmdBuff, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
+
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
new file mode 100644
index 0000000..45ee566
--- /dev/null
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
@@ -0,0 +1,97 @@
+/*
+ * MVKCmdDebug.mm
+ *
+ * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MVKCmdDebug.h"
+#include "MVKCommandBuffer.h"
+#include "MVKCommandPool.h"
+
+#include "MVKLogging.h"
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarker
+
+void MVKCmdDebugMarker::setContent(const char* pMarkerName, const float color[4]) {
+	[_markerName release];
+	_markerName = [@(pMarkerName) retain];
+}
+
+MVKCmdDebugMarker::MVKCmdDebugMarker(MVKCommandTypePool<MVKCmdDebugMarker>* pool)
+	: MVKCommand::MVKCommand((MVKCommandTypePool<MVKCommand>*)pool) {}
+
+MVKCmdDebugMarker::~MVKCmdDebugMarker() {
+	[_markerName release];
+}
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarkerBegin
+
+// Vulkan debug groups are more general than Metal's.
+// Always push on command buffer instead of the encoder.
+void MVKCmdDebugMarkerBegin::encode(MVKCommandEncoder* cmdEncoder) {
+	[cmdEncoder->_mtlCmdBuffer pushDebugGroup: _markerName];
+}
+
+MVKCmdDebugMarkerBegin::MVKCmdDebugMarkerBegin(MVKCommandTypePool<MVKCmdDebugMarkerBegin>* pool)
+	: MVKCmdDebugMarker::MVKCmdDebugMarker((MVKCommandTypePool<MVKCmdDebugMarker>*)pool) {}
+
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarkerEnd
+
+// Vulkan debug groups are more general than Metal's.
+// Always pop from command buffer instead of the encoder.
+void MVKCmdDebugMarkerEnd::encode(MVKCommandEncoder* cmdEncoder) {
+	[cmdEncoder->_mtlCmdBuffer popDebugGroup];
+}
+
+MVKCmdDebugMarkerEnd::MVKCmdDebugMarkerEnd(MVKCommandTypePool<MVKCmdDebugMarkerEnd>* pool)
+	: MVKCommand::MVKCommand((MVKCommandTypePool<MVKCommand>*)pool) {}
+
+
+#pragma mark -
+#pragma mark MVKCmdDebugMarkerInsert
+
+void MVKCmdDebugMarkerInsert::encode(MVKCommandEncoder* cmdEncoder) {
+	[cmdEncoder->getMTLEncoder() insertDebugSignpost: _markerName];
+}
+
+MVKCmdDebugMarkerInsert::MVKCmdDebugMarkerInsert(MVKCommandTypePool<MVKCmdDebugMarkerInsert>* pool)
+	: MVKCmdDebugMarker::MVKCmdDebugMarker((MVKCommandTypePool<MVKCmdDebugMarker>*)pool) {}
+
+
+#pragma mark -
+#pragma mark Command creation functions
+
+void mvkCmdDebugMarkerBegin(MVKCommandBuffer* cmdBuff, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) {
+	MVKCmdDebugMarkerBegin* cmd = cmdBuff->_commandPool->_cmdDebugMarkerBeginPool.acquireObject();
+	cmd->setContent(pMarkerInfo->pMarkerName, pMarkerInfo->color);
+	cmdBuff->addCommand(cmd);
+}
+
+void mvkCmdDebugMarkerEnd(MVKCommandBuffer* cmdBuff) {
+	MVKCmdDebugMarkerEnd* cmd = cmdBuff->_commandPool->_cmdDebugMarkerEndPool.acquireObject();
+	cmdBuff->addCommand(cmd);
+}
+
+void mvkCmdDebugMarkerInsert(MVKCommandBuffer* cmdBuff, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) {
+	MVKCmdDebugMarkerInsert* cmd = cmdBuff->_commandPool->_cmdDebugMarkerInsertPool.acquireObject();
+	cmd->setContent(pMarkerInfo->pMarkerName, pMarkerInfo->color);
+	cmdBuff->addCommand(cmd);
+}
+
+
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
index 3aecbb6..9213901 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
@@ -1,5 +1,5 @@
 /*
- * MVKMVKCmdDispatch.h
+ * MVKCmdDispatch.h
  *
  * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
  *
@@ -24,8 +24,6 @@
 
 #import <Metal/Metal.h>
 
-class MVKDevice;
-
 
 #pragma mark -
 #pragma mark MVKCmdDispatch
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
index dc03d47..d52d36a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
@@ -1,5 +1,5 @@
 /*
- * MVKMVKCmdDispatch.mm
+ * MVKCmdDispatch.mm
  *
  * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
  *
@@ -25,7 +25,7 @@
 
 
 #pragma mark -
-#pragma mark MVKCmdDraw
+#pragma mark MVKCmdDispatch
 
 void MVKCmdDispatch::setContent(uint32_t x, uint32_t y, uint32_t z) {
     _mtlThreadgroupCount.width = x;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h
index da4567f..fcea48d 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h
@@ -24,8 +24,6 @@
 
 #import <Metal/Metal.h>
 
-class MVKDevice;
-
 
 #pragma mark -
 #pragma mark MVKCmdBindVertexBuffers
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
index c26ecbf..2658c0f 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
@@ -24,7 +24,6 @@
 
 #import <Metal/Metal.h>
 
-class MVKCommandBuffer;
 class MVKRenderPass;
 class MVKFramebuffer;
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
index 31fd02a..3e3b007 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
@@ -26,7 +26,6 @@
 
 #import <Metal/Metal.h>
 
-class MVKCommandBuffer;
 class MVKImage;
 class MVKBuffer;
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index 2149cca..8991223 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -322,7 +322,7 @@
             mtlColorAttDesc.level = bltRend.dstLevel;
             mtlColorAttDesc.slice = bltRend.dstSlice;
             id<MTLRenderCommandEncoder> mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: _mtlRenderPassDescriptor];
-            mtlRendEnc.label = mvkMTLRenderCommandEncoderLabel(_commandUse);
+			setLabelIfNotNil(mtlRendEnc, mvkMTLRenderCommandEncoderLabel(_commandUse));
 
             [mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"];
             [mtlRendEnc setRenderPipelineState: cmdEncPool->getCmdBlitImageMTLRenderPipelineState(_blitKey)];
@@ -517,7 +517,7 @@
         mtlColorAttDesc.resolveLevel = rslvSlice.level;
         mtlColorAttDesc.resolveSlice = rslvSlice.slice;
         id<MTLRenderCommandEncoder> mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: _mtlRenderPassDescriptor];
-        mtlRendEnc.label = mvkMTLRenderCommandEncoderLabel(kMVKCommandUseResolveImage);
+		setLabelIfNotNil(mtlRendEnc, mvkMTLRenderCommandEncoderLabel(kMVKCommandUseResolveImage));
 
         [mtlRendEnc pushDebugGroup: @"vkCmdResolveImage"];
         [mtlRendEnc popDebugGroup];
@@ -1060,7 +1060,7 @@
 				mtlRPSADesc.slice = layer;
 
                 id<MTLRenderCommandEncoder> mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPDesc];
-                mtlRendEnc.label = mtlRendEncName;
+				setLabelIfNotNil(mtlRendEnc, mtlRendEncName);
                 [mtlRendEnc endEncoding];
             }
         }
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index c2d0356..ba86b4b 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -149,6 +149,7 @@
 	friend class MVKCommandPool;
 
 	MVKBaseObject* getBaseObject() override { return this; };
+	void propogateDebugName() override {}
 	void init(const VkCommandBufferAllocateInfo* pAllocateInfo);
 	bool canExecute();
 	bool canPrefill();
@@ -312,7 +313,7 @@
 	void endMetalRenderEncoding();
 
 	/** 
-	 * The current Metal compute encoder for the specified use,
+	 * Returns trhe current Metal compute encoder for the specified use,
 	 * which determines the label assigned to the returned encoder.
 	 *
 	 * If the current encoder is not a compute encoder, this function ends current before 
@@ -321,7 +322,7 @@
 	id<MTLComputeCommandEncoder> getMTLComputeEncoder(MVKCommandUse cmdUse);
 
 	/**
-	 * The current Metal BLIT encoder for the specified use,
+	 * Returns the current Metal BLIT encoder for the specified use,
      * which determines the label assigned to the returned encoder.
 	 *
 	 * If the current encoder is not a BLIT encoder, this function ends 
@@ -329,6 +330,12 @@
 	 */
 	id<MTLBlitCommandEncoder> getMTLBlitEncoder(MVKCommandUse cmdUse);
 
+	/**
+	 * Returns the current Metal encoder, which may be any of the Metal render,
+	 * comupte, or Blit encoders, or nil if no encoding is currently occurring.
+	 */
+	id<MTLCommandEncoder> getMTLEncoder();
+
 	/** Returns the push constants associated with the specified shader stage. */
 	MVKPushConstantsCommandEncoderState* getPushConstants(VkShaderStageFlagBits shaderStage);
 
@@ -460,9 +467,6 @@
 #pragma mark -
 #pragma mark Support functions
 
-/** Returns a name, suitable for use as a MTLCommandBuffer label, based on the MVKCommandUse. */
-NSString* mvkMTLCommandBufferLabel(MVKCommandUse cmdUse);
-
 /** Returns a name, suitable for use as a MTLRenderCommandEncoder label, based on the MVKCommandUse. */
 NSString* mvkMTLRenderCommandEncoderLabel(MVKCommandUse cmdUse);
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index 09356b4..7548626 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -238,6 +238,8 @@
 
 	_mtlCmdBuffer = mtlCmdBuff;		// not retained
 
+	setLabelIfNotNil(_mtlCmdBuffer, _cmdBuffer->_debugName);
+
     MVKCommand* cmd = _cmdBuffer->_head;
 	while (cmd) {
         if (cmd->canEncode()) { cmd->encode(this); }
@@ -307,7 +309,7 @@
 	}
 
     _mtlRenderEncoder = [_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPDesc];     // not retained
-    _mtlRenderEncoder.label = getMTLRenderCommandEncoderName();
+	setLabelIfNotNil(_mtlRenderEncoder, getMTLRenderCommandEncoderName());
 
     if ( !_isRenderingEntireAttachment ) { clearRenderArea(); }
 
@@ -330,8 +332,11 @@
 
 // Returns a name for use as a MTLRenderCommandEncoder label
 NSString* MVKCommandEncoder::getMTLRenderCommandEncoderName() {
-    MVKCommandUse cmdUse = (_renderSubpassIndex == 0) ? kMVKCommandUseBeginRenderPass : kMVKCommandUseNextSubpass;
-    return mvkMTLRenderCommandEncoderLabel(cmdUse);
+	NSString* rpName = _renderPass ? _renderPass->getDebugName() : nil;
+	if (rpName) { return rpName; }
+
+	MVKCommandUse cmdUse = (_renderSubpassIndex == 0) ? kMVKCommandUseBeginRenderPass : kMVKCommandUseNextSubpass;
+	return mvkMTLRenderCommandEncoderLabel(cmdUse);
 }
 
 void MVKCommandEncoder::bindPipeline(VkPipelineBindPoint pipelineBindPoint, MVKPipeline* pipeline) {
@@ -438,7 +443,7 @@
 	}
 	if (_mtlComputeEncoderUse != cmdUse) {
 		_mtlComputeEncoderUse = cmdUse;
-		_mtlComputeEncoder.label = mvkMTLComputeCommandEncoderLabel(cmdUse);
+		setLabelIfNotNil(_mtlComputeEncoder, mvkMTLComputeCommandEncoderLabel(cmdUse));
 	}
 	return _mtlComputeEncoder;
 }
@@ -450,10 +455,18 @@
 	}
     if (_mtlBlitEncoderUse != cmdUse) {
         _mtlBlitEncoderUse = cmdUse;
-        _mtlBlitEncoder.label = mvkMTLBlitCommandEncoderLabel(cmdUse);
+		setLabelIfNotNil(_mtlBlitEncoder, mvkMTLBlitCommandEncoderLabel(cmdUse));
     }
 	return _mtlBlitEncoder;
 }
+
+id<MTLCommandEncoder> MVKCommandEncoder::getMTLEncoder(){
+	if (_mtlRenderEncoder) { return _mtlRenderEncoder; }
+	if (_mtlComputeEncoder) { return _mtlComputeEncoder; }
+	if (_mtlBlitEncoder) { return _mtlBlitEncoder; }
+	return nil;
+}
+
 MVKPushConstantsCommandEncoderState* MVKCommandEncoder::getPushConstants(VkShaderStageFlagBits shaderStage) {
 	switch (shaderStage) {
 		case VK_SHADER_STAGE_VERTEX_BIT:					return &_vertexPushConstants;
@@ -602,16 +615,6 @@
 #pragma mark -
 #pragma mark Support functions
 
-NSString* mvkMTLCommandBufferLabel(MVKCommandUse cmdUse) {
-    switch (cmdUse) {
-        case kMVKCommandUseQueueSubmit:     return @"vkQueueSubmit CommandBuffer";
-        case kMVKCommandUseQueuePresent:    return @"vkQueuePresentKHR CommandBuffer";
-        case kMVKCommandUseQueueWaitIdle:   return @"vkQueueWaitIdle CommandBuffer";
-        case kMVKCommandUseDeviceWaitIdle:  return @"vkDeviceWaitIdle CommandBuffer";
-        default:                            return @"Unknown Use CommandBuffer";
-    }
-}
-
 NSString* mvkMTLRenderCommandEncoderLabel(MVKCommandUse cmdUse) {
     switch (cmdUse) {
         case kMVKCommandUseBeginRenderPass:         return @"vkCmdBeginRenderPass RenderEncoder";
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
index 253580f..f110814 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
@@ -28,6 +28,7 @@
 #include "MVKCmdDraw.h"
 #include "MVKCmdTransfer.h"
 #include "MVKCmdQueries.h"
+#include "MVKCmdDebug.h"
 #include "MVKMTLBufferAllocation.h"
 #include <unordered_set>
 
@@ -139,6 +140,12 @@
 
     MVKCommandTypePool<MVKCmdPushDescriptorSetWithTemplate> _cmdPushSetWithTemplatePool;
 
+	MVKCommandTypePool<MVKCmdDebugMarkerBegin> _cmdDebugMarkerBeginPool;
+
+	MVKCommandTypePool<MVKCmdDebugMarkerEnd> _cmdDebugMarkerEndPool;
+
+	MVKCommandTypePool<MVKCmdDebugMarkerInsert> _cmdDebugMarkerInsertPool;
+
 
 #pragma mark Command resources
 
@@ -173,6 +180,7 @@
 	~MVKCommandPool() override;
 
 protected:
+	void propogateDebugName() override {}
 	MVKDeviceObjectPool<MVKCommandBuffer> _commandBufferPool;
 	std::unordered_set<MVKCommandBuffer*> _allocatedCommandBuffers;
 	MVKCommandEncodingPool _commandEncodingPool;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
index 273da53..3bdb118 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
@@ -134,47 +134,50 @@
 	_commandBufferPool(device),
 	_commandEncodingPool(this),
 	_queueFamilyIndex(pCreateInfo->queueFamilyIndex),
-	_cmdPipelineBarrierPool(this, true),
-	_cmdBindPipelinePool(this, true),
-	_cmdBeginRenderPassPool(this, true),
-	_cmdNextSubpassPool(this, true),
-	_cmdExecuteCommandsPool(this, true),
-	_cmdEndRenderPassPool(this, true),
-	_cmdBindDescriptorSetsPool(this, true),
-	_cmdSetViewportPool(this, true),
-	_cmdSetScissorPool(this, true),
-    _cmdSetLineWidthPool(this, true),
-    _cmdSetDepthBiasPool(this, true),
-    _cmdSetBlendConstantsPool(this, true),
-    _cmdSetDepthBoundsPool(this, true),
-    _cmdSetStencilCompareMaskPool(this, true),
-    _cmdSetStencilWriteMaskPool(this, true),
-    _cmdSetStencilReferencePool(this, true),
-	_cmdBindVertexBuffersPool(this, true),
-	_cmdBindIndexBufferPool(this, true),
-	_cmdDrawPool(this, true),
-	_cmdDrawIndexedPool(this, true),
-	_cmdDrawIndirectPool(this, true),
-	_cmdDrawIndexedIndirectPool(this, true),
-	_cmdCopyImagePool(this, true),
-	_cmdBlitImagePool(this, true),
-    _cmdResolveImagePool(this, true),
-    _cmdFillBufferPool(this, true),
-    _cmdUpdateBufferPool(this, true),
-	_cmdCopyBufferPool(this, true),
-    _cmdBufferImageCopyPool(this, true),
-	_cmdClearAttachmentsPool(this, true),
-	_cmdClearImagePool(this, true),
-    _cmdBeginQueryPool(this, true),
-    _cmdEndQueryPool(this, true),
-	_cmdWriteTimestampPool(this, true),
-    _cmdResetQueryPoolPool(this, true),
-    _cmdCopyQueryPoolResultsPool(this, true),
-	_cmdPushConstantsPool(this, true),
-    _cmdDispatchPool(this, true),
-    _cmdDispatchIndirectPool(this, true),
-    _cmdPushDescriptorSetPool(this, true),
-    _cmdPushSetWithTemplatePool(this, true)
+	_cmdPipelineBarrierPool(this),
+	_cmdBindPipelinePool(this),
+	_cmdBeginRenderPassPool(this),
+	_cmdNextSubpassPool(this),
+	_cmdExecuteCommandsPool(this),
+	_cmdEndRenderPassPool(this),
+	_cmdBindDescriptorSetsPool(this),
+	_cmdSetViewportPool(this),
+	_cmdSetScissorPool(this),
+    _cmdSetLineWidthPool(this),
+    _cmdSetDepthBiasPool(this),
+    _cmdSetBlendConstantsPool(this),
+    _cmdSetDepthBoundsPool(this),
+    _cmdSetStencilCompareMaskPool(this),
+    _cmdSetStencilWriteMaskPool(this),
+    _cmdSetStencilReferencePool(this),
+	_cmdBindVertexBuffersPool(this),
+	_cmdBindIndexBufferPool(this),
+	_cmdDrawPool(this),
+	_cmdDrawIndexedPool(this),
+	_cmdDrawIndirectPool(this),
+	_cmdDrawIndexedIndirectPool(this),
+	_cmdCopyImagePool(this),
+	_cmdBlitImagePool(this),
+    _cmdResolveImagePool(this),
+    _cmdFillBufferPool(this),
+    _cmdUpdateBufferPool(this),
+	_cmdCopyBufferPool(this),
+    _cmdBufferImageCopyPool(this),
+	_cmdClearAttachmentsPool(this),
+	_cmdClearImagePool(this),
+    _cmdBeginQueryPool(this),
+    _cmdEndQueryPool(this),
+	_cmdWriteTimestampPool(this),
+    _cmdResetQueryPoolPool(this),
+    _cmdCopyQueryPoolResultsPool(this),
+	_cmdPushConstantsPool(this),
+    _cmdDispatchPool(this),
+    _cmdDispatchIndirectPool(this),
+    _cmdPushDescriptorSetPool(this),
+    _cmdPushSetWithTemplatePool(this),
+	_cmdDebugMarkerBeginPool(this),
+	_cmdDebugMarkerEndPool(this),
+	_cmdDebugMarkerInsertPool(this)
 {}
 
 MVKCommandPool::~MVKCommandPool() {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
index f88a504..eeae73d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
@@ -81,6 +81,7 @@
 protected:
 	using MVKResource::needsHostReadSync;
 
+	void propogateDebugName() override;
 	bool needsHostReadSync(VkPipelineStageFlags srcStageMask,
 						   VkPipelineStageFlags dstStageMask,
 						   VkBufferMemoryBarrier* pBufferMemoryBarrier);
@@ -112,6 +113,8 @@
     ~MVKBufferView() override;
 
 protected:
+	void propogateDebugName() override;
+
     MVKBuffer* _buffer;
 	id<MTLTexture> _mtlTexture;
 	MTLPixelFormat _mtlPixelFormat;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
index 858d30f..db650c9 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
@@ -28,6 +28,16 @@
 #pragma mark -
 #pragma mark MVKBuffer
 
+void MVKBuffer::propogateDebugName() {
+	if (_deviceMemory &&
+		_deviceMemory->isDedicatedAllocation() &&
+		_deviceMemory->_debugName.length == 0) {
+
+		_deviceMemory->setDebugName(_debugName.UTF8String);
+	}
+}
+
+
 #pragma mark Resource memory
 
 VkResult MVKBuffer::getMemoryRequirements(VkMemoryRequirements* pMemoryRequirements) {
@@ -70,6 +80,8 @@
 
 	MVKResource::bindDeviceMemory(mvkMem, memOffset);
 
+	propogateDebugName();
+
 	return _deviceMemory ? _deviceMemory->addBuffer(this) : VK_SUCCESS;
 }
 
@@ -130,6 +142,10 @@
 #pragma mark -
 #pragma mark MVKBufferView
 
+void MVKBufferView::propogateDebugName() {
+	setLabelIfNotNil(_mtlTexture, _debugName);
+}
+
 #pragma mark Metal
 
 id<MTLTexture> MVKBufferView::getMTLTexture() {
@@ -161,6 +177,7 @@
 		_mtlTexture = [_buffer->getMTLBuffer() newTextureWithDescriptor: mtlTexDesc
 																 offset: _mtlBufferOffset
 															bytesPerRow: _mtlBytesPerRow];
+		propogateDebugName();
     }
     return _mtlTexture;
 }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index 3570f52..7ee1e67 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -175,6 +175,7 @@
 	friend class MVKPipelineLayout;
 	friend class MVKDescriptorSet;
 
+	void propogateDebugName() override {}
 	MVKVectorInline<MVKDescriptorSetLayoutBinding, 8> _bindings;
 	std::unordered_map<uint32_t, uint32_t> _bindingToIndex;
 	MVKShaderResourceBinding _mtlResourceCounts;
@@ -314,6 +315,7 @@
 	friend class MVKDescriptorSetLayout;
 	friend class MVKDescriptorPool;
 
+	void propogateDebugName() override {}
 	void setLayout(MVKDescriptorSetLayout* layout);
     MVKDescriptorBinding* getBinding(uint32_t binding);
 
@@ -353,6 +355,7 @@
 	~MVKDescriptorPool() override;
 
 protected:
+	void propogateDebugName() override {}
 	MVKDescriptorSetPool* getDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout);
 
 	uint32_t _maxSets;
@@ -387,7 +390,9 @@
 	/** Destructor. */
 	~MVKDescriptorUpdateTemplate() override = default;
 
-private:
+protected:
+	void propogateDebugName() override {}
+
 	VkDescriptorUpdateTemplateTypeKHR _type;
 	std::vector<VkDescriptorUpdateTemplateEntryKHR> _entries;
 };
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index 5680d9a..017a339 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -19,7 +19,7 @@
 #pragma once
 
 #include "MVKFoundation.h"
-#include "MVKBaseObject.h"
+#include "MVKVulkanAPIObject.h"
 #include "MVKLayers.h"
 #include "MVKObjectPool.h"
 #include "mvk_datatypes.hpp"
@@ -309,6 +309,7 @@
 protected:
 	friend class MVKDevice;
 
+	void propogateDebugName() override {}
 	MTLFeatureSet getMaximalMTLFeatureSet();
     void initMetalFeatures();
 	void initFeatures();
@@ -407,6 +408,7 @@
 
 	MVKSwapchainImage* createSwapchainImage(const VkImageCreateInfo* pCreateInfo,
 											MVKSwapchain* swapchain,
+											uint32_t swapchainIndex,
 											const VkAllocationCallbacks* pAllocator);
 	void destroySwapchainImage(MVKSwapchainImage* mvkImg,
 							   const VkAllocationCallbacks* pAllocator);
@@ -636,6 +638,7 @@
     }
 
 protected:
+	void propogateDebugName() override  {}
 	MVKResource* addResource(MVKResource* rez);
 	MVKResource* removeResource(MVKResource* rez);
     void initPerformanceTracking();
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index d2ea1af..d06651d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -1597,7 +1597,7 @@
 VkResult MVKDevice::waitIdle() {
 	for (auto& queues : _queuesByQueueFamilyIndex) {
 		for (MVKQueue* q : queues) {
-			q->waitIdle(kMVKCommandUseDeviceWaitIdle);
+			q->waitIdle();
 		}
 	}
 	return VK_SUCCESS;
@@ -1718,8 +1718,9 @@
 
 MVKSwapchainImage* MVKDevice::createSwapchainImage(const VkImageCreateInfo* pCreateInfo,
 												   MVKSwapchain* swapchain,
+												   uint32_t swapchainIndex,
 												   const VkAllocationCallbacks* pAllocator) {
-	return (MVKSwapchainImage*)addResource(new MVKSwapchainImage(this, pCreateInfo, swapchain));
+	return (MVKSwapchainImage*)addResource(new MVKSwapchainImage(this, pCreateInfo, swapchain, swapchainIndex));
 }
 
 void MVKDevice::destroySwapchainImage(MVKSwapchainImage* mvkImg,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
index da0a2dd..3551b93 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
@@ -114,6 +114,7 @@
 	friend MVKBuffer;
 	friend MVKImage;
 
+	void propogateDebugName() override;
 	VkDeviceSize adjustMemorySize(VkDeviceSize size, VkDeviceSize offset);
 	VkResult addBuffer(MVKBuffer* mvkBuff);
 	void removeBuffer(MVKBuffer* mvkBuff);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
index e3be6ba..b179646 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
@@ -28,8 +28,11 @@
 
 using namespace std;
 
+
 #pragma mark MVKDeviceMemory
 
+void MVKDeviceMemory::propogateDebugName() { setLabelIfNotNil(_mtlBuffer, _debugName); }
+
 VkResult MVKDeviceMemory::map(VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData) {
 
 	if ( !isMemoryHostAccessible() ) {
@@ -118,8 +121,7 @@
 	}
 
 	// In the dedicated case, we already saved the buffer we're going to use.
-	if (!_isDedicated)
-		_buffers.push_back(mvkBuff);
+	if (!_isDedicated) { _buffers.push_back(mvkBuff); }
 
 	return VK_SUCCESS;
 }
@@ -168,6 +170,8 @@
 	}
 	_pMemory = isMemoryHostAccessible() ? _mtlBuffer.contents : nullptr;
 
+	propogateDebugName();
+
 	return true;
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
index 1f91afc..19701c1 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
@@ -49,7 +49,9 @@
 	MVKFramebuffer(MVKDevice* device, const VkFramebufferCreateInfo* pCreateInfo);
 
 protected:
-    VkExtent2D _extent;
+	void propogateDebugName() override {}
+
+	VkExtent2D _extent;
 	uint32_t _layerCount;
 	std::vector<MVKImageView*> _attachments;
 };
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index c9b457f..3fdd466 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -212,6 +212,7 @@
 	friend class MVKImageView;
 	using MVKResource::needsHostReadSync;
 
+	void propogateDebugName() override;
 	MVKImageSubresource* getSubresource(uint32_t mipLevel, uint32_t arrayLayer);
 	void validateConfig(const VkImageCreateInfo* pCreateInfo);
 	VkSampleCountFlagBits validateSamples(const VkImageCreateInfo* pCreateInfo);
@@ -300,6 +301,7 @@
 	~MVKImageView() override;
 
 protected:
+	void propogateDebugName() override;
 	id<MTLTexture> newMTLTexture();
 	void initMTLTextureViewSupport();
     MTLPixelFormat getSwizzledMTLPixelFormat(VkFormat format,
@@ -339,6 +341,7 @@
 	~MVKSampler() override;
 
 protected:
+	void propogateDebugName() override {}
 	MTLSamplerDescriptor* getMTLSamplerDescriptor(const VkSamplerCreateInfo* pCreateInfo);
 
 	id<MTLSamplerState> _mtlSamplerState;
@@ -365,6 +368,9 @@
 
 public:
 
+	/** Returns the encompassing swapchain. */
+	inline MVKSwapchain* getSwapchain() { return _swapchain; }
+
 	/** Returns the index of this image within the encompassing swapchain. */
 	inline uint32_t getSwapchainIndex() { return _swapchainIndex; }
 
@@ -397,7 +403,8 @@
 	/** Constructs an instance for the specified device and swapchain. */
 	MVKSwapchainImage(MVKDevice* device,
 					  const VkImageCreateInfo* pCreateInfo,
-					  MVKSwapchain* swapchain);
+					  MVKSwapchain* swapchain,
+					  uint32_t swapchainIndex);
 
 	~MVKSwapchainImage() override;
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index b14f33c..5faf759 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -34,6 +34,8 @@
 
 #pragma mark MVKImage
 
+void MVKImage::propogateDebugName() { setLabelIfNotNil(_mtlTexture, _debugName); }
+
 VkImageType MVKImage::getImageType() { return mvkVkImageTypeFromMTLTextureType(_mtlTextureType); }
 
 VkFormat MVKImage::getVkFormat() { return mvkVkFormatFromMTLPixelFormat(_mtlPixelFormat); }
@@ -270,6 +272,8 @@
 		if (_mtlTexture) { return _mtlTexture; }
 
 		_mtlTexture = newMTLTexture();   // retained
+
+		propogateDebugName();
 	}
 	return _mtlTexture;
 }
@@ -758,6 +762,8 @@
 #pragma mark -
 #pragma mark MVKImageView
 
+void MVKImageView::propogateDebugName() { setLabelIfNotNil(_mtlTexture, _debugName); }
+
 void MVKImageView::populateMTLRenderPassAttachmentDescriptor(MTLRenderPassAttachmentDescriptor* mtlAttDesc) {
     mtlAttDesc.texture = getMTLTexture();           // Use image view, necessary if image view format differs from image format
     mtlAttDesc.level = _useMTLTextureView ? 0 : _subresourceRange.baseMipLevel;
@@ -795,6 +801,8 @@
 			if (_mtlTexture) { return _mtlTexture; }
 
 			_mtlTexture = newMTLTexture(); // retained
+
+			propogateDebugName();
 		}
 		return _mtlTexture;
 	} else {
@@ -1208,8 +1216,12 @@
     // and make myself available only once the command buffer has completed.
     // Otherwise, immediately present the drawable and make myself available.
     if (mtlCmdBuff) {
-        [mtlCmdBuff presentDrawable: mtlDrawable];
-        resetMetalSurface();
+		NSString* scName = _swapchain->getDebugName();
+		if (scName) { [mtlCmdBuff pushDebugGroup: scName]; }
+		[mtlCmdBuff presentDrawable: mtlDrawable];
+		if (scName) { [mtlCmdBuff popDebugGroup]; }
+
+		resetMetalSurface();
         if (_device->_pMetalFeatures->events && !_availabilitySignalers.empty()) {
             // Signal the semaphore device-side.
             _availabilitySignalers.front().first->encodeSignal(mtlCmdBuff);
@@ -1239,9 +1251,10 @@
 
 MVKSwapchainImage::MVKSwapchainImage(MVKDevice* device,
 									 const VkImageCreateInfo* pCreateInfo,
-									 MVKSwapchain* swapchain) : MVKImage(device, pCreateInfo) {
+									 MVKSwapchain* swapchain,
+									 uint32_t swapchainIndex) : MVKImage(device, pCreateInfo) {
 	_swapchain = swapchain;
-	_swapchainIndex = _swapchain->getImageCount();
+	_swapchainIndex = swapchainIndex;
 	_availability.acquisitionID = _swapchain->getNextAcquisitionID();
 	_availability.isAvailable = true;
 	_preSignaled = make_pair(nullptr, nullptr);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
index 22551cd..29111e8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
@@ -20,7 +20,7 @@
 
 #include "MVKEnvironment.h"
 #include "MVKLayers.h"
-#include "MVKBaseObject.h"
+#include "MVKVulkanAPIObject.h"
 #include "vk_mvk_moltenvk.h"
 #include <vector>
 #include <unordered_map>
@@ -143,6 +143,7 @@
 protected:
 	friend MVKDevice;
 
+	void propogateDebugName() override {}
 	void initProcAddrs();
 	void initCreationDebugReportCallbacks(const VkInstanceCreateInfo* pCreateInfo);
 	VkDebugReportFlagsEXT getVkDebugReportFlagsFromASLLevel(int aslLvl);
@@ -187,6 +188,8 @@
 protected:
 	friend MVKInstance;
 	
+	void propogateDebugName() override {}
+
 	MVKInstance* _mvkInstance;
 	VkDebugReportCallbackCreateInfoEXT _info;
 	bool _isCreationCallback;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index a27d8eb..abd6988 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -476,6 +476,11 @@
 	ADD_DVC_EXT2_ENTRY_POINT(vkGetPhysicalDevicePresentRectanglesKHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
 	ADD_DVC_EXT2_ENTRY_POINT(vkAcquireNextImage2KHR, KHR_SWAPCHAIN, KHR_DEVICE_GROUP);
 	ADD_DVC_EXT_ENTRY_POINT(vkResetQueryPoolEXT, EXT_HOST_QUERY_RESET);
+	ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectTagEXT, EXT_DEBUG_MARKER);
+	ADD_DVC_EXT_ENTRY_POINT(vkDebugMarkerSetObjectNameEXT, EXT_DEBUG_MARKER);
+	ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerBeginEXT, EXT_DEBUG_MARKER);
+	ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerEndEXT, EXT_DEBUG_MARKER);
+	ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerInsertEXT, EXT_DEBUG_MARKER);
 
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index 39f8543..bdaba80 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -91,6 +91,8 @@
 	MVKPipelineLayout(MVKDevice* device, const VkPipelineLayoutCreateInfo* pCreateInfo);
 
 protected:
+	void propogateDebugName() override {}
+
 	MVKVectorInline<MVKDescriptorSetLayout, 8> _descriptorSetLayouts;
 	MVKVectorInline<MVKShaderResourceBinding, 8> _dslMTLResourceIndexOffsets;
 	MVKVectorInline<VkPushConstantRange, 8> _pushConstants;
@@ -141,6 +143,8 @@
 	   																					   _fullImageViewSwizzle(device->_pMVKConfig->fullImageViewSwizzle)	{}
 
 protected:
+	void propogateDebugName() override {}
+
 	MVKPipelineCache* _pipelineCache;
 	MVKShaderImplicitRezBinding _auxBufferIndex;
 	bool _fullImageViewSwizzle;
@@ -335,6 +339,7 @@
 	~MVKPipelineCache() override;
 
 protected:
+	void propogateDebugName() override;
 	MVKShaderLibraryCache* getShaderLibraryCache(MVKShaderModuleKey smKey);
 	void readData(const VkPipelineCacheCreateInfo* pCreateInfo);
 	void writeData(std::ostream& outstream, bool isCounting = false);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 90e7176..e44316b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -429,6 +429,8 @@
 	// Output
 	addFragmentOutputToPipeline(plDesc, reflectData, pCreateInfo);
 
+	setLabelIfNotNil(plDesc, ((MVKPipelineLayout*)pCreateInfo->layout)->getDebugName());
+
 	return plDesc;
 }
 
@@ -578,6 +580,8 @@
 	}
 	plDesc.stageInputDescriptor.indexBufferIndex = kMVKTessCtlIndexBufferIndex;
 
+	setLabelIfNotNil(plDesc, ((MVKPipelineLayout*)pCreateInfo->layout)->getDebugName());
+
 	return plDesc;
 }
 
@@ -1166,8 +1170,12 @@
 	_mtlPipelineState = nil;
 
 	if (shaderFunc.mtlFunction) {
+		MTLComputePipelineDescriptor* plDesc = [[MTLComputePipelineDescriptor new] autorelease];
+		plDesc.computeFunction = shaderFunc.mtlFunction;
+		setLabelIfNotNil(plDesc, ((MVKPipelineLayout*)pCreateInfo->layout)->getDebugName());
+
 		MVKComputePipelineCompiler* plc = new MVKComputePipelineCompiler(this);
-		_mtlPipelineState = plc->newMTLComputePipelineState(shaderFunc.mtlFunction);	// retained
+		_mtlPipelineState = plc->newMTLComputePipelineState(plDesc);	// retained
 		plc->destroy();
 	} else {
 		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader function could not be compiled into pipeline. See previous logged error."));
@@ -1209,6 +1217,13 @@
 #pragma mark -
 #pragma mark MVKPipelineCache
 
+
+void MVKPipelineCache::propogateDebugName() {
+	lock_guard<mutex> lock(_shaderCacheLock);
+
+	for (auto& slPair : _shaderCache) { slPair.second->propogateDebugName(); }
+}
+
 // Return a shader library from the specified shader context sourced from the specified shader module.
 MVKShaderLibrary* MVKPipelineCache::getShaderLibrary(SPIRVToMSLConverterContext* pContext, MVKShaderModule* shaderModule) {
 	lock_guard<mutex> lock(_shaderCacheLock);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h
index cf77c01..72f955b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h
@@ -148,6 +148,7 @@
 	MVKTimestampQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo);
 
 protected:
+	void propogateDebugName() override {}
 	void getResult(uint32_t query, void* pQryData, bool shouldOutput64Bit) override;
 	id<MTLBuffer> getResultBuffer(MVKCommandEncoder* cmdEncoder, uint32_t firstQuery, uint32_t queryCount, NSUInteger& offset) override;
 	void encodeSetResultBuffer(MVKCommandEncoder* cmdEncoder, uint32_t firstQuery, uint32_t queryCount, uint32_t index) override;
@@ -183,6 +184,7 @@
     ~MVKOcclusionQueryPool() override;
 
 protected:
+	void propogateDebugName() override;
     void getResult(uint32_t query, void* pQryData, bool shouldOutput64Bit) override;
 	id<MTLBuffer> getResultBuffer(MVKCommandEncoder* cmdEncoder, uint32_t firstQuery, uint32_t queryCount, NSUInteger& offset) override;
 	void encodeSetResultBuffer(MVKCommandEncoder* cmdEncoder, uint32_t firstQuery, uint32_t queryCount, uint32_t index) override;
@@ -201,6 +203,8 @@
 public:
     MVKPipelineStatisticsQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo);
 
+protected:
+	void propogateDebugName() override {}
 };
 
 
@@ -213,5 +217,7 @@
 public:
 	MVKUnsupportedQueryPool(MVKDevice* device, const VkQueryPoolCreateInfo* pCreateInfo);
 
+protected:
+	void propogateDebugName() override {}
 };
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm
index 0cdfd93..1b5e77b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.mm
@@ -222,6 +222,8 @@
 #pragma mark -
 #pragma mark MVKOcclusionQueryPool
 
+void MVKOcclusionQueryPool::propogateDebugName() { setLabelIfNotNil(_visibilityResultMTLBuffer, _debugName); }
+
 // If a dedicated visibility buffer has been established, use it, otherwise fetch the
 // current global visibility buffer, but don't cache it because it could be replaced later.
 id<MTLBuffer> MVKOcclusionQueryPool::getVisibilityResultMTLBuffer() {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
index e30d5ff..2a365bd 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
@@ -87,14 +87,13 @@
 #pragma mark Queue submissions
 
 	/** Submits the specified command buffers to the queue. */
-	VkResult submit(uint32_t submitCount, const VkSubmitInfo* pSubmits,
-                    VkFence fence, MVKCommandUse cmdBuffUse);
+	VkResult submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
 
 	/** Submits the specified presentation command to the queue. */
 	VkResult submit(const VkPresentInfoKHR* pPresentInfo);
 
 	/** Block the current thread until this queue is idle. */
-	VkResult waitIdle(MVKCommandUse cmdBuffUse);
+	VkResult waitIdle();
 
 	/** Return the name of this queue. */
 	inline const std::string& getName() { return _name; }
@@ -132,6 +131,7 @@
 	friend class MVKQueuePresentSurfaceSubmission;
 
 	MVKBaseObject* getBaseObject() override { return this; };
+	void propogateDebugName() override;
 	void initName();
 	void initExecQueue();
 	void initMTLCommandQueue();
@@ -196,8 +196,7 @@
 	/** Constructs an instance for the queue. */
 	MVKQueueCommandBufferSubmission(MVKQueue* queue,
 									const VkSubmitInfo* pSubmit,
-									VkFence fence,
-                                    MVKCommandUse cmdBuffUse);
+									VkFence fence);
 
 protected:
 	friend MVKCommandBuffer;
@@ -210,7 +209,6 @@
 	MVKVectorInline<MVKCommandBuffer*, 16> _cmdBuffers;
 	MVKVectorInline<MVKSemaphore*, 16> _signalSemaphores;
 	MVKFence* _fence;
-    MVKCommandUse _cmdBuffUse;
 	id<MTLCommandBuffer> _activeMTLCommandBuffer;
 	bool _isSignalingSemaphores;
 };
@@ -229,6 +227,8 @@
 									 const VkPresentInfoKHR* pPresentInfo);
 
 protected:
+	id<MTLCommandBuffer> getMTLCommandBuffer();
+
 	MVKVectorInline<MVKSwapchainImage*, 4> _surfaceImages;
 };
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
index 4efac25..ecffbdb 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
@@ -60,6 +60,8 @@
 #pragma mark -
 #pragma mark MVKQueue
 
+void MVKQueue::propogateDebugName() { setLabelIfNotNil(_mtlQueue, _debugName); }
+
 
 #pragma mark Queue submissions
 
@@ -83,18 +85,17 @@
 	return rslt;
 }
 
-VkResult MVKQueue::submit(uint32_t submitCount, const VkSubmitInfo* pSubmits,
-                          VkFence fence, MVKCommandUse cmdBuffUse) {
+VkResult MVKQueue::submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) {
 
     // Fence-only submission
     if (submitCount == 0 && fence) {
-        return submit(new MVKQueueCommandBufferSubmission(this, nullptr, fence, cmdBuffUse));
+        return submit(new MVKQueueCommandBufferSubmission(this, nullptr, fence));
     }
 
     VkResult rslt = VK_SUCCESS;
     for (uint32_t sIdx = 0; sIdx < submitCount; sIdx++) {
         VkFence fenceOrNil = (sIdx == (submitCount - 1)) ? fence : VK_NULL_HANDLE; // last one gets the fence
-        VkResult subRslt = submit(new MVKQueueCommandBufferSubmission(this, &pSubmits[sIdx], fenceOrNil, cmdBuffUse));
+        VkResult subRslt = submit(new MVKQueueCommandBufferSubmission(this, &pSubmits[sIdx], fenceOrNil));
         if (rslt == VK_SUCCESS) { rslt = subRslt; }
     }
     return rslt;
@@ -105,7 +106,7 @@
 }
 
 // Create an empty submit struct and fence, submit to queue and wait on fence.
-VkResult MVKQueue::waitIdle(MVKCommandUse cmdBuffUse) {
+VkResult MVKQueue::waitIdle() {
 
 	VkFenceCreateInfo vkFenceInfo = {
 		.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
@@ -115,7 +116,7 @@
 
 	MVKFence mvkFence(_device, &vkFenceInfo);
 	VkFence fence = (VkFence)&mvkFence;
-	submit(0, nullptr, fence, cmdBuffUse);
+	submit(0, nullptr, fence);
 	return mvkWaitForFences(_device, 1, &fence, false);
 }
 
@@ -265,7 +266,6 @@
 	if (_activeMTLCommandBuffer) { commitActiveMTLCommandBuffer(); }
 
 	_activeMTLCommandBuffer = mtlCmdBuff;	// not retained
-	_activeMTLCommandBuffer.label = mvkMTLCommandBufferLabel(_cmdBuffUse);
 	[_activeMTLCommandBuffer enqueue];
 }
 
@@ -313,8 +313,7 @@
 
 MVKQueueCommandBufferSubmission::MVKQueueCommandBufferSubmission(MVKQueue* queue,
 																 const VkSubmitInfo* pSubmit,
-																 VkFence fence,
-                                                                 MVKCommandUse cmdBuffUse)
+																 VkFence fence)
         : MVKQueueSubmission(queue,
 							 (pSubmit ? pSubmit->waitSemaphoreCount : 0),
 							 (pSubmit ? pSubmit->pWaitSemaphores : nullptr)) {
@@ -338,7 +337,6 @@
     }
 
 	_fence = (MVKFence*)fence;
-    _cmdBuffUse= cmdBuffUse;
 	_activeMTLCommandBuffer = nil;
 
 //	static std::atomic<uint32_t> _subCount;
@@ -350,18 +348,13 @@
 #pragma mark MVKQueuePresentSurfaceSubmission
 
 void MVKQueuePresentSurfaceSubmission::execute() {
-    id<MTLCommandQueue> mtlQ = _queue->getMTLCommandQueue();
-
 	// If there are semaphores and this device supports MTLEvent, we must present
 	// with a command buffer in order to synchronize with the semaphores.
 	MVKDevice* mvkDev = _queue->getDevice();
 	if (mvkDev->_pMetalFeatures->events && !_waitSemaphores.empty()) {
 		// Create a command buffer, have it wait for the semaphores, then present
 		// surfaces via the command buffer.
-		id<MTLCommandBuffer> mtlCmdBuff = [mtlQ commandBufferWithUnretainedReferences];
-		mtlCmdBuff.label = mvkMTLCommandBufferLabel(kMVKCommandUseQueuePresent);
-		[mtlCmdBuff enqueue];
-
+		id<MTLCommandBuffer> mtlCmdBuff = getMTLCommandBuffer();
 		for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); }
 		for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); }
 
@@ -369,10 +362,7 @@
 	} else if (mvkDev->_pMVKConfig->presentWithCommandBuffer || mvkDev->_pMVKConfig->displayWatermark) {
 		// Create a command buffer, present surfaces via the command buffer,
 		// then wait on the semaphores before committing.
-		id<MTLCommandBuffer> mtlCmdBuff = [mtlQ commandBufferWithUnretainedReferences];
-		mtlCmdBuff.label = mvkMTLCommandBufferLabel(kMVKCommandUseQueuePresent);
-		[mtlCmdBuff enqueue];
-
+		id<MTLCommandBuffer> mtlCmdBuff = getMTLCommandBuffer();
 		for (auto& si : _surfaceImages) { si->presentCAMetalDrawable(mtlCmdBuff); }
 		for (auto& ws : _waitSemaphores) { ws->wait(); }
 
@@ -391,6 +381,13 @@
     this->destroy();
 }
 
+id<MTLCommandBuffer> MVKQueuePresentSurfaceSubmission::getMTLCommandBuffer() {
+	id<MTLCommandBuffer> mtlCmdBuff = [_queue->getMTLCommandQueue() commandBufferWithUnretainedReferences];
+	setLabelIfNotNil(mtlCmdBuff, @"vkQueuePresentKHR CommandBuffer");
+	[mtlCmdBuff enqueue];
+	return mtlCmdBuff;
+}
+
 MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* queue,
 																   const VkPresentInfoKHR* pPresentInfo)
 		: MVKQueueSubmission(queue, pPresentInfo->waitSemaphoreCount, pPresentInfo->pWaitSemaphores) {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
index d61285c..56209da 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
@@ -160,10 +160,11 @@
 	MVKRenderPass(MVKDevice* device, const VkRenderPassCreateInfo* pCreateInfo);
 
 protected:
-
 	friend class MVKRenderSubpass;
 	friend class MVKRenderPassAttachment;
 
+	void propogateDebugName() override {}
+
 	std::vector<MVKRenderSubpass> _subpasses;
 	std::vector<MVKRenderPassAttachment> _attachments;
 	std::vector<VkSubpassDependency> _subpassDependencies;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
index 573cf30..d6fbbbe 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
@@ -29,6 +29,8 @@
 
 class MVKPipelineCache;
 class MVKShaderCacheIterator;
+class MVKShaderLibraryCache;
+class MVKShaderModule;
 
 using namespace mvk;
 
@@ -73,7 +75,10 @@
 
 protected:
 	friend MVKShaderCacheIterator;
+	friend MVKShaderLibraryCache;
+	friend MVKShaderModule;
 
+	void propogateDebugName();
 	void handleCompilationError(NSError* err, const char* opDesc);
     MTLFunctionConstant* getFunctionConstant(NSArray<MTLFunctionConstant*>* mtlFCs, NSUInteger mtlFCID);
 
@@ -113,7 +118,9 @@
 protected:
 	friend MVKShaderCacheIterator;
 	friend MVKPipelineCache;
+	friend MVKShaderModule;
 
+	void propogateDebugName();
 	MVKShaderLibrary* findShaderLibrary(SPIRVToMSLConverterContext* pContext);
 	MVKShaderLibrary* addShaderLibrary(SPIRVToMSLConverterContext* pContext,
 									   const std::string& mslSourceCode,
@@ -121,7 +128,6 @@
 	void merge(MVKShaderLibraryCache* other);
 
 	MVKVulkanAPIDeviceObject* _owner;
-	std::mutex _accessLock;
 	std::vector<std::pair<SPIRVToMSLConverterContext, MVKShaderLibrary*>> _shaderLibraries;
 };
 
@@ -192,6 +198,7 @@
 protected:
 	friend MVKShaderCacheIterator;
 
+	void propogateDebugName() override;
 	MVKGLSLConversionShaderStage getMVKGLSLConversionShaderStage(SPIRVToMSLConverterContext* pContext);
 
 	MVKShaderLibraryCache _shaderLibraryCache;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index 6c30480..94f366f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -31,6 +31,8 @@
 #pragma mark -
 #pragma mark MVKShaderLibrary
 
+void MVKShaderLibrary::propogateDebugName() { setLabelIfNotNil(_mtlLibrary, _owner->getDebugName()); }
+
 // If the size of the workgroup dimension is specialized, extract it from the
 // specialization info, otherwise use the value specified in the SPIR-V shader code.
 static uint32_t getWorkgroupDimensionSize(const SPIRVWorkgroupSizeDimension& wgDim, const VkSpecializationInfo* pSpecInfo) {
@@ -50,11 +52,7 @@
 
     if ( !_mtlLibrary ) { return MVKMTLFunctionNull; }
 
-    // Ensure the function name is compatible with Metal (Metal does not allow main()
-    // as a function name), and retrieve the unspecialized Metal function with that name.
     NSString* mtlFuncName = @(_entryPoint.mtlFunctionName.c_str());
-
-
 	MVKDevice* mvkDev = _owner->getDevice();
     uint64_t startTime = mvkDev->getPerformanceTimestamp();
     id<MTLFunction> mtlFunc = [[_mtlLibrary newFunctionWithName: mtlFuncName] autorelease];
@@ -91,6 +89,7 @@
 				fs->destroy();
             }
         }
+		setLabelIfNotNil(mtlFunc, _owner->getDebugName());
     } else {
         reportError(VK_ERROR_INVALID_SHADER_NV, "Shader module does not contain an entry point named '%s'.", mtlFuncName.UTF8String);
     }
@@ -112,6 +111,7 @@
 	MVKShaderLibraryCompiler* slc = new MVKShaderLibraryCompiler(_owner);
 	_mtlLibrary = slc->newMTLLibrary(@(mslSourceCode.c_str()));	// retained
 	slc->destroy();
+	propogateDebugName();
 
 	_entryPoint = entryPoint;
 	_msl = mslSourceCode;
@@ -132,6 +132,7 @@
         handleCompilationError(err, "Compiled shader module creation");
         [shdrData release];
     }
+	propogateDebugName();
     mvkDev->addActivityPerformance(mvkDev->_performanceStatistics.shaderCompilation.mslLoad, startTime);
 }
 
@@ -165,6 +166,10 @@
 #pragma mark -
 #pragma mark MVKShaderLibraryCache
 
+void MVKShaderLibraryCache::propogateDebugName() {
+	for (auto& slPair : _shaderLibraries) { slPair.second->propogateDebugName(); }
+}
+
 MVKShaderLibrary* MVKShaderLibraryCache::getShaderLibrary(SPIRVToMSLConverterContext* pContext,
 														  MVKShaderModule* shaderModule,
 														  bool* pWasAdded) {
@@ -221,6 +226,13 @@
 #pragma mark -
 #pragma mark MVKShaderModule
 
+void MVKShaderModule::propogateDebugName() {
+	lock_guard<mutex> lock(_accessLock);
+
+	_shaderLibraryCache.propogateDebugName();
+	if (_defaultLibrary) { _defaultLibrary->propogateDebugName(); }
+}
+
 MVKMTLFunction MVKShaderModule::getMTLFunction(SPIRVToMSLConverterContext* pContext,
 											   const VkSpecializationInfo* pSpecializationInfo,
 											   MVKPipelineCache* pipelineCache) {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
index 5c21169..bac1e64 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
@@ -18,7 +18,7 @@
 
 #pragma once
 
-#include "MVKBaseObject.h"
+#include "MVKVulkanAPIObject.h"
 #include "MVKEnvironment.h"
 #include <mutex>
 
@@ -69,6 +69,8 @@
 	~MVKSurface() override;
 
 protected:
+	void propogateDebugName() override {}
+
 	MVKInstance* _mvkInstance;
 	CAMetalLayer* _mtlCAMetalLayer;
 	std::mutex _lock;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
index 3ce0981..affd7a3 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
@@ -96,6 +96,7 @@
 protected:
 	friend class MVKSwapchainImage;
 
+	void propogateDebugName() override;
 	void initCAMetalLayer(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt);
 	void initSurfaceImages(const VkSwapchainCreateInfoKHR* pCreateInfo, uint32_t imgCnt);
     void initFrameIntervalTracking();
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
index 15154cf..47c711b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
@@ -34,6 +34,13 @@
 
 #pragma mark MVKSwapchain
 
+void MVKSwapchain::propogateDebugName() {
+	size_t imgCnt = _surfaceImages.size();
+	for (size_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) {
+		_surfaceImages[imgIdx]->setDebugName([NSString stringWithFormat: @"%@(%lu)", _debugName, imgIdx].UTF8String);
+	}
+}
+
 uint32_t MVKSwapchain::getImageCount() { return (uint32_t)_surfaceImages.size(); }
 
 MVKSwapchainImage* MVKSwapchain::getImage(uint32_t index) { return _surfaceImages[index]; }
@@ -265,7 +272,7 @@
 
 	_surfaceImages.reserve(imgCnt);
     for (uint32_t imgIdx = 0; imgIdx < imgCnt; imgIdx++) {
-        _surfaceImages.push_back(_device->createSwapchainImage(&imgInfo, this, NULL));
+        _surfaceImages.push_back(_device->createSwapchainImage(&imgInfo, this, imgIdx, NULL));
     }
 
     MVKLogInfo("Created %d swapchain images with initial size (%d, %d).", imgCnt, imgExtent.width, imgExtent.height);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
index 1504c92..6904fa8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
@@ -142,6 +142,8 @@
     ~MVKSemaphore() override;
 
 protected:
+	void propogateDebugName() override {}
+
 	MVKSemaphoreImpl _blocker;
 	id<MTLEvent> _mtlEvent;
 	std::atomic<uint64_t> _mtlEventValue;
@@ -192,6 +194,7 @@
 		MVKVulkanAPIDeviceObject(device), _isSignaled(mvkAreFlagsEnabled(pCreateInfo->flags, VK_FENCE_CREATE_SIGNALED_BIT)) {}
 
 protected:
+	void propogateDebugName() override {}
 	void notifySitters();
 
 	std::mutex _lock;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
new file mode 100644
index 0000000..2f20ad9
--- /dev/null
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
@@ -0,0 +1,153 @@
+/*
+ * MVKVulkanAPIObject.h
+ *
+ * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "MVKBaseObject.h"
+#include <vulkan/vk_icd.h>
+#include <string>
+#include <mutex>
+
+#import <Foundation/NSString.h>
+
+class MVKInstance;
+
+
+#pragma mark -
+#pragma mark MVKVulkanAPIObject
+
+/**
+ * Abstract class that represents an opaque Vulkan API handle object.
+ *
+ * API objects can sometimes be destroyed by the client before the GPU is done with them.
+ * To support this, an object of this type will automatically be deleted iff it has been
+ * destroyed by the client, and all references have been released. An object of this type
+ * is therefore allowed to live past its destruction by the client, until it is no longer
+ * referenced by other objects.
+ */
+class MVKVulkanAPIObject : public MVKConfigurableObject {
+
+public:
+
+	/** Returns the Vulkan API opaque object controlling this object. */
+	MVKVulkanAPIObject* getVulkanAPIObject() override { return this; };
+
+	/** Returns a reference to this object suitable for use as a Vulkan API handle. */
+	virtual void* getVkHandle() { return this; }
+
+	/** Returns the debug report object type of this object. */
+	virtual VkDebugReportObjectTypeEXT getVkDebugReportObjectType() = 0;
+
+	/** Returns the Vulkan instance. */
+	virtual MVKInstance* getInstance() = 0;
+
+	/**
+	 * Called when this instance has been retained as a reference by another object,
+	 * indicating that this instance will not be deleted until that reference is released.
+	 */
+	void retain();
+
+	/**
+	 * Called when this instance has been released as a reference from another object.
+	 * Once all references have been released, this object is free to be deleted.
+	 * If the destroy() function has already been called on this instance by the time
+	 * this function is called, this instance will be deleted.
+	 */
+	void release();
+
+	/**
+	 * Marks this instance as destroyed. If all previous references to this instance
+	 * have been released, this instance will be deleted, otherwise deletion of this
+	 * instance will automatically be deferred until all references have been released.
+	 */
+	void destroy() override;
+
+	/** Gets the debug object name of this instance. */
+	inline NSString* getDebugName() { return _debugName; }
+
+	/** Sets the debug object name of this instance. */
+	VkResult setDebugName(const char* pObjectName);
+
+	/** Returns the MVKVulkanAPIObject instance referenced by the object of the given type. */
+	static MVKVulkanAPIObject* getMVKVulkanAPIObject(VkDebugReportObjectTypeEXT objType, uint64_t object);
+
+	/** Construct an empty instance. Declared here to support copy constructor. */
+	MVKVulkanAPIObject() {}
+
+	/**
+	 * Construct an instance from a copy. Default copy constructor disallowed due to mutex.
+	 * Copies start with fresh reference counts.
+	 */
+	MVKVulkanAPIObject(const MVKVulkanAPIObject& other) {}
+
+	~MVKVulkanAPIObject() override;
+
+protected:
+	bool decrementRetainCount();
+	bool markDestroyed();
+	virtual void propogateDebugName() = 0;
+
+	NSString* _debugName = nil;
+	std::mutex _refLock;
+	unsigned _refCount = 0;
+	bool _isDestroyed = false;
+};
+
+
+#pragma mark -
+#pragma mark MVKDispatchableVulkanAPIObject
+
+/** Abstract class that represents a dispatchable opaque Vulkan API handle object. */
+class MVKDispatchableVulkanAPIObject : public MVKVulkanAPIObject {
+
+    typedef struct {
+        VK_LOADER_DATA loaderData;
+        MVKDispatchableVulkanAPIObject* mvkObject;
+    } MVKDispatchableObjectICDRef;
+
+public:
+
+    /**
+     * Returns a reference to this object suitable for use as a Vulkan API handle.
+     * This is the compliment of the getDispatchableObject() method.
+     */
+    void* getVkHandle() override { return &_icdRef; }
+
+    /**
+     * Retrieves the MVKDispatchableVulkanAPIObject instance referenced by the dispatchable Vulkan handle.
+     * This is the compliment of the getVkHandle() method.
+     */
+    static inline MVKDispatchableVulkanAPIObject* getDispatchableObject(void* vkHandle) {
+		return vkHandle ? ((MVKDispatchableObjectICDRef*)vkHandle)->mvkObject : nullptr;
+    }
+
+protected:
+    MVKDispatchableObjectICDRef _icdRef = { ICD_LOADER_MAGIC, this };
+
+};
+
+#pragma mark -
+#pragma mark Support functions
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wobjc-method-access"
+/** Generically avoids setting a label to nil, which many objects don't like. */
+static inline void setLabelIfNotNil(id object, NSString* label) { if (label) { [object setLabel: label]; } }
+#pragma clang diagnostic pop
+
+
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
new file mode 100644
index 0000000..8815dab
--- /dev/null
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
@@ -0,0 +1,80 @@
+/*
+ * MVKVulkanAPIObject.mm
+ *
+ * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MVKVulkanAPIObject.h"
+
+using namespace std;
+
+
+#pragma mark -
+#pragma mark MVKVulkanAPIObject
+
+void MVKVulkanAPIObject::retain() {
+	lock_guard<mutex> lock(_refLock);
+
+	_refCount++;
+}
+
+void MVKVulkanAPIObject::release() {
+	if (decrementRetainCount()) { destroy(); }
+}
+
+void MVKVulkanAPIObject::destroy() {
+	if (markDestroyed()) { MVKConfigurableObject::destroy(); }
+}
+
+// Decrements the reference count, and returns whether it's time to destroy this object.
+bool MVKVulkanAPIObject::decrementRetainCount() {
+	lock_guard<mutex> lock(_refLock);
+
+	if (_refCount > 0) { _refCount--; }
+	return (_isDestroyed && _refCount == 0);
+}
+
+// Marks this object as destroyed, and returns whether no references are left outstanding.
+bool MVKVulkanAPIObject::markDestroyed() {
+	lock_guard<mutex> lock(_refLock);
+
+	_isDestroyed = true;
+	return _refCount == 0;
+}
+
+VkResult MVKVulkanAPIObject::setDebugName(const char* pObjectName) {
+	[_debugName release];
+	_debugName = [[NSString stringWithUTF8String: pObjectName] retain];		// retained
+	propogateDebugName();
+	return VK_SUCCESS;
+}
+
+MVKVulkanAPIObject* MVKVulkanAPIObject::getMVKVulkanAPIObject(VkDebugReportObjectTypeEXT objType, uint64_t object) {
+	void* pVkObj = (void*)object;
+	switch (objType) {
+		case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT:
+		case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT:
+		case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT:
+		case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT:
+		case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT:
+			return MVKDispatchableVulkanAPIObject::getDispatchableObject(pVkObj);
+		default:
+			return (MVKVulkanAPIObject*)pVkObj;
+	}
+}
+
+MVKVulkanAPIObject::~MVKVulkanAPIObject() {
+	[_debugName release];
+}
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index 7c5fd14..fc42c73 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -52,6 +52,7 @@
 MVK_EXTENSION(KHR_swapchain, KHR_SWAPCHAIN)
 MVK_EXTENSION(KHR_swapchain_mutable_format, KHR_SWAPCHAIN_MUTABLE_FORMAT)
 MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS)
+MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER)
 MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT)
 MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET)
 MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET)
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.h b/MoltenVK/MoltenVK/Layers/MVKExtensions.h
index ae4379e..e93fa50 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.h
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.h
@@ -48,7 +48,7 @@
 public:
 
 	/** Returns the Vulkan API opaque object controlling this object. */
-	MVKVulkanAPIObject* getVulkanAPIObject() override { return _apiObject->getVulkanAPIObject(); };
+	MVKVulkanAPIObject* getVulkanAPIObject() override { return _apiObject; };
 
 	union {
 		struct {
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.cpp b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
similarity index 99%
rename from MoltenVK/MoltenVK/Layers/MVKExtensions.cpp
rename to MoltenVK/MoltenVK/Layers/MVKExtensions.mm
index d39ccb2..8ca031a 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.cpp
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
@@ -1,5 +1,5 @@
 /*
- * MVKExtensions.cpp
+ * MVKExtensions.mm
  *
  * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
  *
diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
index ccdf4bc..4e11160 100644
--- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
+++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
@@ -19,11 +19,8 @@
 #pragma once
 
 #include "mvk_vulkan.h"
-#include <vulkan/vk_icd.h>
 #include <string>
-#include <mutex>
 
-class MVKInstance;
 class MVKVulkanAPIObject;
 
 
@@ -126,105 +123,3 @@
 protected:
 	VkResult _configurationResult = VK_SUCCESS;
 };
-
-
-#pragma mark -
-#pragma mark MVKVulkanAPIObject
-
-/**
- * Abstract class that represents an opaque Vulkan API handle object.
- *
- * API objects can sometimes be destroyed by the client before the GPU is done with them.
- * To support this, an object of this type will automatically be deleted iff it has been
- * destroyed by the client, and all references have been released. An object of this type
- * is therefore allowed to live past its destruction by the client, until it is no longer
- * referenced by other objects.
- */
-class MVKVulkanAPIObject : public MVKConfigurableObject {
-
-public:
-
-	/** Returns the Vulkan API opaque object controlling this object. */
-	MVKVulkanAPIObject* getVulkanAPIObject() override { return this; };
-
-	/** Returns a reference to this object suitable for use as a Vulkan API handle. */
-	virtual void* getVkHandle() { return this; }
-
-	/** Returns the debug report object type of this object. */
-	virtual VkDebugReportObjectTypeEXT getVkDebugReportObjectType() = 0;
-
-	/** Returns the Vulkan instance. */
-	virtual MVKInstance* getInstance() = 0;
-
-	/**
-	 * Called when this instance has been retained as a reference by another object,
-	 * indicating that this instance will not be deleted until that reference is released.
-	 */
-	void retain();
-
-	/**
-	 * Called when this instance has been released as a reference from another object.
-	 * Once all references have been released, this object is free to be deleted.
-	 * If the destroy() function has already been called on this instance by the time
-	 * this function is called, this instance will be deleted.
-	 */
-	void release();
-
-	/**
-	 * Marks this instance as destroyed. If all previous references to this instance
-	 * have been released, this instance will be deleted, otherwise deletion of this
-	 * instance will automatically be deferred until all references have been released.
-	 */
-	void destroy() override;
-
-	/** Construct an empty instance. Declared here to support copy constructor. */
-	MVKVulkanAPIObject() {}
-
-	/**
-	 * Construct an instance from a copy. Default copy constructor disallowed due to mutex.
-	 * Copies start with fresh reference counts.
-	 */
-	MVKVulkanAPIObject(const MVKVulkanAPIObject& other) {}
-
-protected:
-
-	bool decrementRetainCount();
-	bool markDestroyed();
-
-	std::mutex _refLock;
-	unsigned _refCount = 0;
-	bool _isDestroyed = false;
-};
-
-
-#pragma mark -
-#pragma mark MVKDispatchableVulkanAPIObject
-
-/** Abstract class that represents a dispatchable opaque Vulkan API handle object. */
-class MVKDispatchableVulkanAPIObject : public MVKVulkanAPIObject {
-
-    typedef struct {
-        VK_LOADER_DATA loaderData;
-        MVKDispatchableVulkanAPIObject* mvkObject;
-    } MVKDispatchableObjectICDRef;
-
-public:
-
-    /**
-     * Returns a reference to this object suitable for use as a Vulkan API handle.
-     * This is the compliment of the getDispatchableObject() method.
-     */
-    void* getVkHandle() override { return &_icdRef; }
-
-    /**
-     * Retrieves the MVKDispatchableVulkanAPIObject instance referenced by the dispatchable Vulkan handle.
-     * This is the compliment of the getVkHandle() method.
-     */
-    static inline MVKDispatchableVulkanAPIObject* getDispatchableObject(void* vkHandle) {
-		return vkHandle ? ((MVKDispatchableObjectICDRef*)vkHandle)->mvkObject : nullptr;
-    }
-
-protected:
-    MVKDispatchableObjectICDRef _icdRef = { ICD_LOADER_MAGIC, this };
-
-};
diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
similarity index 85%
rename from MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp
rename to MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
index e8abb0c..39f497e 100644
--- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp
+++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
@@ -1,5 +1,5 @@
 /*
- * MVKBaseObject.cpp
+ * MVKBaseObject.mm
  *
  * Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
  *
@@ -17,11 +17,11 @@
  */
 
 #include "MVKBaseObject.h"
+#include "MVKVulkanAPIObject.h"
 #include "MVKInstance.h"
 #include "MVKFoundation.h"
 #include "MVKOSExtensions.h"
 #include "MVKLogging.h"
-#include <stdlib.h>
 #include <cxxabi.h>
 
 using namespace std;
@@ -166,37 +166,3 @@
 
 	return vkErr;
 }
-
-
-#pragma mark -
-#pragma mark MVKVulkanAPIObject
-
-void MVKVulkanAPIObject::retain() {
-	lock_guard<mutex> lock(_refLock);
-
-	_refCount++;
-}
-
-void MVKVulkanAPIObject::release() {
-	if (decrementRetainCount()) { destroy(); }
-}
-
-void MVKVulkanAPIObject::destroy() {
-	if (markDestroyed()) { MVKConfigurableObject::destroy(); }
-}
-
-// Decrements the reference count, and returns whether it's time to destroy this object.
-bool MVKVulkanAPIObject::decrementRetainCount() {
-	lock_guard<mutex> lock(_refLock);
-
-	if (_refCount > 0) { _refCount--; }
-	return (_isDestroyed && _refCount == 0);
-}
-
-// Marks this object as destroyed, and returns whether no references are left outstanding.
-bool MVKVulkanAPIObject::markDestroyed() {
-	lock_guard<mutex> lock(_refLock);
-
-	_isDestroyed = true;
-	return _refCount == 0;
-}
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 3c5e144..ed6f65b 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -242,7 +242,7 @@
 
 	MVKTraceVulkanCall();
 	MVKQueue* mvkQ = MVKQueue::getMVKQueue(queue);
-	return mvkQ->submit(submitCount, pSubmits, fence, kMVKCommandUseQueueSubmit);
+	return mvkQ->submit(submitCount, pSubmits, fence);
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkQueueWaitIdle(
@@ -250,7 +250,7 @@
 	
 	MVKTraceVulkanCall();
 	MVKQueue* mvkQ = MVKQueue::getMVKQueue(queue);
-	return mvkQ->waitIdle(kMVKCommandUseQueueWaitIdle);
+	return mvkQ->waitIdle();
 }
 
 MVK_PUBLIC_SYMBOL VkResult vkDeviceWaitIdle(
@@ -2134,6 +2134,53 @@
 
 
 #pragma mark -
+#pragma mark VK_EXT_debug_marker extension
+
+MVK_PUBLIC_SYMBOL VkResult vkDebugMarkerSetObjectTagEXT(
+	VkDevice                                    device,
+	const VkDebugMarkerObjectTagInfoEXT*        pTagInfo) {
+
+	MVKTraceVulkanCall();
+	return VK_SUCCESS;
+}
+
+MVK_PUBLIC_SYMBOL VkResult vkDebugMarkerSetObjectNameEXT(
+	VkDevice                                    device,
+	const VkDebugMarkerObjectNameInfoEXT*       pNameInfo) {
+
+	MVKTraceVulkanCall();
+	MVKVulkanAPIObject* mvkObj = MVKVulkanAPIObject::getMVKVulkanAPIObject(pNameInfo->objectType, pNameInfo->object);
+	return mvkObj ? mvkObj->setDebugName(pNameInfo->pObjectName) : VK_SUCCESS;
+}
+
+MVK_PUBLIC_SYMBOL void vkCmdDebugMarkerBeginEXT(
+	VkCommandBuffer                             commandBuffer,
+	const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo) {
+
+	MVKTraceVulkanCall();
+	MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
+	mvkCmdDebugMarkerBegin(cmdBuff, pMarkerInfo);
+}
+
+MVK_PUBLIC_SYMBOL void vkCmdDebugMarkerEndEXT(
+	VkCommandBuffer                             commandBuffer) {
+
+	MVKTraceVulkanCall();
+	MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
+	mvkCmdDebugMarkerEnd(cmdBuff);
+}
+
+MVK_PUBLIC_SYMBOL void vkCmdDebugMarkerInsertEXT(
+	VkCommandBuffer                             commandBuffer,
+	const VkDebugMarkerMarkerInfoEXT*           pMarkerInfo) {
+
+	MVKTraceVulkanCall();
+	MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
+	mvkCmdDebugMarkerInsert(cmdBuff, pMarkerInfo);
+}
+
+
+#pragma mark -
 #pragma mark iOS & macOS surface extensions
 
 MVK_PUBLIC_SYMBOL VkResult vkCreate_PLATFORM_SurfaceMVK(