Merge pull request #608 from billhollings/master

Add support for VK_EXT_debug_marker extension.
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(