Merge pull request #624 from billhollings/master

Add support for the VK_EXT_debug_utils extension.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 12f508f..32c6881 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -21,6 +21,7 @@
 - Add support for extensions:
 	- `VK_EXT_debug_report`
 	- `VK_EXT_debug_marker`
+	- `VK_EXT_debug_utils`
 	- `VK_NV_glsl_shader`
 - Change log indication of error in logs from `[***MoltenVK ERROR***]` to 
   `[mvk-error]`, for consistency with other log level indications. 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.h b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.h
index a0028cf..dac03b9 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.h
@@ -89,3 +89,9 @@
 
 void mvkCmdDebugMarkerInsert(MVKCommandBuffer* cmdBuff, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo);
 
+void mvkCmdBeginDebugUtilsLabel(MVKCommandBuffer* cmdBuff, const VkDebugUtilsLabelEXT* pLabelInfo);
+
+void mvkCmdEndDebugUtilsLabel(MVKCommandBuffer* cmdBuff);
+
+void mvkCmdInsertDebugUtilsLabel(MVKCommandBuffer* cmdBuff, const VkDebugUtilsLabelEXT* pLabelInfo);
+
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
index ae28ec3..b7eda12 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
@@ -104,4 +104,20 @@
 	cmdBuff->addCommand(cmd);
 }
 
+void mvkCmdBeginDebugUtilsLabel(MVKCommandBuffer* cmdBuff, const VkDebugUtilsLabelEXT* pLabelInfo) {
+	MVKCmdDebugMarkerBegin* cmd = cmdBuff->_commandPool->_cmdDebugMarkerBeginPool.acquireObject();
+	cmd->setContent(pLabelInfo->pLabelName, pLabelInfo->color);
+	cmdBuff->addCommand(cmd);
+}
+
+void mvkCmdEndDebugUtilsLabel(MVKCommandBuffer* cmdBuff) {
+	MVKCmdDebugMarkerEnd* cmd = cmdBuff->_commandPool->_cmdDebugMarkerEndPool.acquireObject();
+	cmdBuff->addCommand(cmd);
+}
+
+void mvkCmdInsertDebugUtilsLabel(MVKCommandBuffer* cmdBuff, const VkDebugUtilsLabelEXT* pLabelInfo) {
+	MVKCmdDebugMarkerInsert* cmd = cmdBuff->_commandPool->_cmdDebugMarkerInsertPool.acquireObject();
+	cmd->setContent(pLabelInfo->pLabelName, pLabelInfo->color);
+	cmdBuff->addCommand(cmd);
+}
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 2591f74..2a6289a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -54,6 +54,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_COMMAND_BUFFER; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT; }
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
index f110814..45cc0a6 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
@@ -53,6 +53,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_COMMAND_POOL; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
index eeae73d..82815ca 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
@@ -31,6 +31,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_BUFFER; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT; }
 
@@ -97,6 +100,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_BUFFER_VIEW; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
index a7bfd5b..18da46f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
@@ -203,7 +203,7 @@
 	// Multiple rows will automatically align with PoT max texture dimension, but need to align upwards if less than full single row.
 	size_t maxBlocksPerRow = _device->_pMetalFeatures->maxTextureDimension / fmtBlockSize.width;
 	size_t blocksPerRow = min(blockCount, maxBlocksPerRow);
-	_mtlBytesPerRow = mvkAlignByteOffset(blocksPerRow * bytesPerBlock, _device->getVkFormatTexelBufferAlignment(pCreateInfo->format));
+	_mtlBytesPerRow = mvkAlignByteOffset(blocksPerRow * bytesPerBlock, _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this));
 
 	size_t rowCount = blockCount / blocksPerRow;
 	if (blockCount % blocksPerRow) { rowCount++; }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index 7ee1e67..2569f32 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -134,6 +134,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT; }
 
@@ -283,6 +286,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DESCRIPTOR_SET; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT; }
 
@@ -334,6 +340,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DESCRIPTOR_POOL; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT; }
 
@@ -372,6 +381,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index 017a339..b054c9a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -79,6 +79,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_PHYSICAL_DEVICE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT; }
 
@@ -345,6 +348,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DEVICE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT; }
 
@@ -561,10 +567,10 @@
 	 *
 	 * All other pixel formats are returned unchanged.
 	 */
-	MTLPixelFormat getMTLPixelFormatFromVkFormat(VkFormat vkFormat);
+	MTLPixelFormat getMTLPixelFormatFromVkFormat(VkFormat vkFormat, MVKBaseObject* mvkObj);
 
 	/** Returns the memory alignment required for the format when used in a texel buffer. */
-	VkDeviceSize getVkFormatTexelBufferAlignment(VkFormat format);
+	VkDeviceSize getVkFormatTexelBufferAlignment(VkFormat format, MVKBaseObject* mvkObj);
 
     /** 
      * Returns the MTLBuffer used to hold occlusion query results, 
@@ -694,7 +700,7 @@
 	 * are managed for each platform device.
 	 */
 	inline MTLPixelFormat getMTLPixelFormatFromVkFormat(VkFormat vkFormat) {
-		return _device ? _device->getMTLPixelFormatFromVkFormat(vkFormat)
+		return _device ? _device->getMTLPixelFormatFromVkFormat(vkFormat, getBaseObject())
 					   : mvkMTLPixelFormatFromVkFormatInObj(vkFormat, getBaseObject());
 	}
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 99badf0..aadd5fc 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -2027,8 +2027,8 @@
 	return ((_pMetalFeatures->maxPerStageBufferCount - 1) - binding);
 }
 
-MTLPixelFormat MVKDevice::getMTLPixelFormatFromVkFormat(VkFormat vkFormat) {
-	MTLPixelFormat mtlPixFmt = mvkMTLPixelFormatFromVkFormat(vkFormat);
+MTLPixelFormat MVKDevice::getMTLPixelFormatFromVkFormat(VkFormat vkFormat, MVKBaseObject* mvkObj) {
+	MTLPixelFormat mtlPixFmt = mvkMTLPixelFormatFromVkFormatInObj(vkFormat, mvkObj);
 #if MVK_MACOS
 	if (mtlPixFmt == MTLPixelFormatDepth24Unorm_Stencil8 &&
 		!getMTLDevice().isDepth24Stencil8PixelFormatSupported) {
@@ -2038,8 +2038,8 @@
 	return mtlPixFmt;
 }
 
-VkDeviceSize MVKDevice::getVkFormatTexelBufferAlignment(VkFormat format) {
-	VkDeviceSize deviceAlignment = mvkMTLPixelFormatLinearTextureAlignment(getMTLPixelFormatFromVkFormat(format), getMTLDevice());
+VkDeviceSize MVKDevice::getVkFormatTexelBufferAlignment(VkFormat format, MVKBaseObject* mvkObj) {
+	VkDeviceSize deviceAlignment = mvkMTLPixelFormatLinearTextureAlignment(getMTLPixelFormatFromVkFormat(format, mvkObj), getMTLDevice());
 	return deviceAlignment ? deviceAlignment : _pProperties->limits.minTexelBufferOffsetAlignment;
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
index 3551b93..5c13988 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
@@ -35,6 +35,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DEVICE_MEMORY; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
index 19701c1..9fd9ef3 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
@@ -30,6 +30,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_FRAMEBUFFER; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 3fdd466..9c31d20 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -48,6 +48,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_IMAGE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT; }
 
@@ -262,6 +265,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_IMAGE_VIEW; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT; }
 
@@ -330,6 +336,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_SAMPLER; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 5faf759..4cd8aa8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -584,7 +584,7 @@
 	_hasExpectedTexelSize = (mvkMTLPixelFormatBytesPerBlock(_mtlPixelFormat) == mvkVkFormatBytesPerBlock(pCreateInfo->format));
 
 	// Calc _byteCount after _byteAlignment
-	_byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format) : mvkEnsurePowerOfTwo(mvkVkFormatBytesPerBlock(pCreateInfo->format));
+	_byteAlignment = _isLinear ? _device->getVkFormatTexelBufferAlignment(pCreateInfo->format, this) : mvkEnsurePowerOfTwo(mvkVkFormatBytesPerBlock(pCreateInfo->format));
     for (uint32_t mipLvl = 0; mipLvl < _mipLevels; mipLvl++) {
         _byteCount += getBytesPerLayer(mipLvl) * _extent.depth * _arrayLayers;
     }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
index 29111e8..e0a355d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
@@ -31,6 +31,7 @@
 class MVKDevice;
 class MVKSurface;
 class MVKDebugReportCallback;
+class MVKDebugUtilsMessenger;
 
 
 /** Tracks info about entry point function pointer addresses. */
@@ -55,6 +56,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_INSTANCE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT; }
 
@@ -104,10 +108,21 @@
 							const char* pLayerPrefix,
 							const char* pMessage);
 
+
+	MVKDebugUtilsMessenger* createDebugUtilsMessenger(const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
+													  const VkAllocationCallbacks* pAllocator);
+
+	void destroyDebugUtilsMessenger(MVKDebugUtilsMessenger* mvkDUM,
+									const VkAllocationCallbacks* pAllocator);
+
+	void debugUtilsMessage(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+						   VkDebugUtilsMessageTypeFlagsEXT messageTypes,
+						   const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData);
+
 	void debugReportMessage(MVKVulkanAPIObject* mvkAPIObj, int aslLvl, const char* pMessage);
 
-	/** Returns whether debug report callbacks are being used. */
-	bool hasDebugReportCallbacks() { return _hasDebugReportCallbacks; }
+	/** Returns whether debug callbacks are being used. */
+	bool hasDebugCallbacks() { return _hasDebugReportCallbacks || _hasDebugUtilsMessengers; }
 
 	/** Returns the MoltenVK configuration settings. */
 	const MVKConfiguration* getMoltenVKConfiguration() { return &_mvkConfig; }
@@ -145,8 +160,9 @@
 
 	void propogateDebugName() override {}
 	void initProcAddrs();
-	void initCreationDebugReportCallbacks(const VkInstanceCreateInfo* pCreateInfo);
+	void initDebugCallbacks(const VkInstanceCreateInfo* pCreateInfo);
 	VkDebugReportFlagsEXT getVkDebugReportFlagsFromASLLevel(int aslLvl);
+	VkDebugUtilsMessageSeverityFlagBitsEXT getVkDebugUtilsMessageSeverityFlagBitsFromASLLevel(int aslLvl);
 	MVKEntryPoint* getEntryPoint(const char* pName);
 	void initConfig();
     void logVersions();
@@ -157,8 +173,10 @@
 	std::vector<MVKPhysicalDevice*> _physicalDevices;
 	std::unordered_map<std::string, MVKEntryPoint> _entryPoints;
 	std::vector<MVKDebugReportCallback*> _debugReportCallbacks;
-	std::mutex _drcbLock;
+	std::vector<MVKDebugUtilsMessenger*> _debugUtilMessengers;
+	std::mutex _dcbLock;
 	bool _hasDebugReportCallbacks;
+	bool _hasDebugUtilsMessengers;
 	bool _useCreationCallbacks;
 	const char* _debugReportCallbackLayerPrefix;
 };
@@ -172,8 +190,11 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT; }
+
 	/** Returns the debug report object type of this object. */
-	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT; }
+	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT; }
 
 	/** Returns a pointer to the Vulkan instance. */
 	MVKInstance* getInstance() override { return _mvkInstance; }
@@ -181,7 +202,10 @@
 	MVKDebugReportCallback(MVKInstance* mvkInstance,
 						   const VkDebugReportCallbackCreateInfoEXT* pCreateInfo,
 						   bool isCreationCallback) :
-	_mvkInstance(mvkInstance), _info(*pCreateInfo), _isCreationCallback(isCreationCallback) {
+	_mvkInstance(mvkInstance),
+	_info(*pCreateInfo),
+	_isCreationCallback(isCreationCallback) {
+
 		_info.pNext = nullptr;
 	}
 
@@ -195,3 +219,41 @@
 	bool _isCreationCallback;
 };
 
+
+#pragma mark -
+#pragma mark MVKDebugUtilsMessenger
+
+/** Represents a Vulkan Debug Utils callback. */
+class MVKDebugUtilsMessenger : public MVKVulkanAPIObject {
+
+public:
+
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT; }
+
+	/** Returns the debug report object type of this object. */
+	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT; }
+
+	/** Returns a pointer to the Vulkan instance. */
+	MVKInstance* getInstance() override { return _mvkInstance; }
+
+	MVKDebugUtilsMessenger(MVKInstance* mvkInstance,
+						   const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
+						   bool isCreationCallback) :
+	_mvkInstance(mvkInstance),
+	_info(*pCreateInfo),
+	_isCreationCallback(isCreationCallback) {
+
+		_info.pNext = nullptr;
+	}
+
+protected:
+	friend MVKInstance;
+
+	void propogateDebugName() override {}
+
+	MVKInstance* _mvkInstance;
+	VkDebugUtilsMessengerCreateInfoEXT _info;
+	bool _isCreationCallback;
+};
+
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index abd6988..1958eb0 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -82,7 +82,7 @@
 
 MVKDebugReportCallback* MVKInstance::createDebugReportCallback(const VkDebugReportCallbackCreateInfoEXT* pCreateInfo,
 															   const VkAllocationCallbacks* pAllocator) {
-	lock_guard<mutex> lock(_drcbLock);
+	lock_guard<mutex> lock(_dcbLock);
 
 	MVKDebugReportCallback* mvkDRCB = new MVKDebugReportCallback(this, pCreateInfo, _useCreationCallbacks);
 	_debugReportCallbacks.push_back(mvkDRCB);
@@ -92,7 +92,7 @@
 
 void MVKInstance::destroyDebugReportCallback(MVKDebugReportCallback* mvkDRCB,
 								const VkAllocationCallbacks* pAllocator) {
-	lock_guard<mutex> lock(_drcbLock);
+	lock_guard<mutex> lock(_dcbLock);
 
 	mvkRemoveAllOccurances(_debugReportCallbacks, mvkDRCB);
 	_hasDebugReportCallbacks = (_debugReportCallbacks.size() != 0);
@@ -110,25 +110,96 @@
 	// Fail fast to avoid further unnecessary processing and locking.
 	if ( !(_hasDebugReportCallbacks) ) { return; }
 
-	lock_guard<mutex> lock(_drcbLock);
+	lock_guard<mutex> lock(_dcbLock);
 
 	for (auto mvkDRCB : _debugReportCallbacks) {
 		auto& drbcInfo = mvkDRCB->_info;
-		if (drbcInfo.pfnCallback && mvkIsAnyFlagEnabled(drbcInfo.flags, flags) && (mvkDRCB->_isCreationCallback == _useCreationCallbacks)) {
+		if (drbcInfo.pfnCallback &&
+			mvkIsAnyFlagEnabled(drbcInfo.flags, flags) &&
+			(mvkDRCB->_isCreationCallback == _useCreationCallbacks)) {
+
 			drbcInfo.pfnCallback(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage, drbcInfo.pUserData);
 		}
 	}
 }
 
+MVKDebugUtilsMessenger* MVKInstance::createDebugUtilsMessenger(const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
+															   const VkAllocationCallbacks* pAllocator) {
+	lock_guard<mutex> lock(_dcbLock);
+
+	MVKDebugUtilsMessenger* mvkDUM = new MVKDebugUtilsMessenger(this, pCreateInfo, _useCreationCallbacks);
+	_debugUtilMessengers.push_back(mvkDUM);
+	_hasDebugUtilsMessengers = true;
+	return mvkDUM;
+}
+
+void MVKInstance::destroyDebugUtilsMessenger(MVKDebugUtilsMessenger* mvkDUM,
+											 const VkAllocationCallbacks* pAllocator) {
+	lock_guard<mutex> lock(_dcbLock);
+
+	mvkRemoveAllOccurances(_debugUtilMessengers, mvkDUM);
+	_hasDebugUtilsMessengers = (_debugUtilMessengers.size() != 0);
+	mvkDUM->destroy();
+}
+
+void MVKInstance::debugUtilsMessage(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+									VkDebugUtilsMessageTypeFlagsEXT messageTypes,
+									const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) {
+
+	// Fail fast to avoid further unnecessary processing and locking.
+	if ( !(_hasDebugUtilsMessengers) ) { return; }
+
+	lock_guard<mutex> lock(_dcbLock);
+
+	for (auto mvkDUM : _debugUtilMessengers) {
+		auto& dumInfo = mvkDUM->_info;
+		if (dumInfo.pfnUserCallback &&
+			mvkIsAnyFlagEnabled(dumInfo.messageSeverity, messageSeverity) &&
+			mvkIsAnyFlagEnabled(dumInfo.messageType, messageTypes) &&
+			(mvkDUM->_isCreationCallback == _useCreationCallbacks)) {
+
+			dumInfo.pfnUserCallback(messageSeverity, messageTypes, pCallbackData, dumInfo.pUserData);
+		}
+	}
+}
+
 void MVKInstance::debugReportMessage(MVKVulkanAPIObject* mvkAPIObj, int aslLvl, const char* pMessage) {
 
-	// Fail fast to avoid further unnecessary processing and locking.
-	if ( !(_hasDebugReportCallbacks) ) { return; }
+	if (_hasDebugReportCallbacks) {
+		VkDebugReportFlagsEXT flags = getVkDebugReportFlagsFromASLLevel(aslLvl);
+		uint64_t object = (uint64_t)(mvkAPIObj ? mvkAPIObj->getVkHandle() : nullptr);
+		VkDebugReportObjectTypeEXT objectType = mvkAPIObj ? mvkAPIObj->getVkDebugReportObjectType() : VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;
+		debugReportMessage(flags, objectType, object, 0, 0, _debugReportCallbackLayerPrefix, pMessage);
+	}
 
-	VkDebugReportFlagsEXT flags = getVkDebugReportFlagsFromASLLevel(aslLvl);
-	uint64_t object = (uint64_t)(mvkAPIObj ? mvkAPIObj->getVkHandle() : nullptr);
-	VkDebugReportObjectTypeEXT objectType = mvkAPIObj ? mvkAPIObj->getVkDebugReportObjectType() : VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;
-	debugReportMessage(flags, objectType, object, 0, 0, _debugReportCallbackLayerPrefix, pMessage);
+	if (_hasDebugUtilsMessengers) {
+		VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity = getVkDebugUtilsMessageSeverityFlagBitsFromASLLevel(aslLvl);
+		uint64_t objectHandle = (uint64_t)(mvkAPIObj ? mvkAPIObj->getVkHandle() : nullptr);
+		VkObjectType objectType = mvkAPIObj ? mvkAPIObj->getVkObjectType() : VK_OBJECT_TYPE_UNKNOWN;
+
+		VkDebugUtilsObjectNameInfoEXT duObjName = {
+			.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
+			.pNext = nullptr,
+			.objectType = objectType,
+			.objectHandle = objectHandle,
+			.pObjectName = mvkAPIObj ? mvkAPIObj->getDebugName().UTF8String : nullptr
+		};
+		VkDebugUtilsMessengerCallbackDataEXT dumcbd = {
+			.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT,
+			.pNext = nullptr,
+			.flags = 0,
+			.pMessageIdName = nullptr,
+			.messageIdNumber = 0,
+			.pMessage = pMessage,
+			.queueLabelCount = 0,
+			.pQueueLabels = nullptr,
+			.cmdBufLabelCount = 0,
+			.pCmdBufLabels = nullptr,
+			.objectCount = 1,
+			.pObjects = &duObjName
+		};
+		debugUtilsMessage(messageSeverity, VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT, &dumcbd);
+	}
 }
 
 VkDebugReportFlagsEXT MVKInstance::getVkDebugReportFlagsFromASLLevel(int aslLvl) {
@@ -152,6 +223,27 @@
 	}
 }
 
+VkDebugUtilsMessageSeverityFlagBitsEXT MVKInstance::getVkDebugUtilsMessageSeverityFlagBitsFromASLLevel(int aslLvl) {
+	switch (aslLvl) {
+		case ASL_LEVEL_DEBUG:
+			return VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
+
+		case ASL_LEVEL_INFO:
+		case ASL_LEVEL_NOTICE:
+			return VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
+
+		case ASL_LEVEL_WARNING:
+			return VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
+
+		case ASL_LEVEL_ERR:
+		case ASL_LEVEL_CRIT:
+		case ASL_LEVEL_ALERT:
+		case ASL_LEVEL_EMERG:
+		default:
+			return VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
+	}
+}
+
 
 #pragma mark Object Creation
 
@@ -205,7 +297,7 @@
 
 MVKInstance::MVKInstance(const VkInstanceCreateInfo* pCreateInfo) : _enabledExtensions(this) {
 
-	initCreationDebugReportCallbacks(pCreateInfo);	// Do before any creation activities
+	initDebugCallbacks(pCreateInfo);	// Do before any creation activities
 
 	_appInfo.apiVersion = MVK_VULKAN_API_VERSION;	// Default
 	mvkSetOrClear(&_appInfo, pCreateInfo->pApplicationInfo);
@@ -245,9 +337,10 @@
 	_useCreationCallbacks = false;
 }
 
-void MVKInstance::initCreationDebugReportCallbacks(const VkInstanceCreateInfo* pCreateInfo) {
+void MVKInstance::initDebugCallbacks(const VkInstanceCreateInfo* pCreateInfo) {
 	_useCreationCallbacks = true;
 	_hasDebugReportCallbacks = false;
+	_hasDebugUtilsMessengers = false;
 	_debugReportCallbackLayerPrefix = getDriverLayer()->getName();
 
 	MVKVkAPIStructHeader* next = (MVKVkAPIStructHeader*)pCreateInfo->pNext;
@@ -256,6 +349,9 @@
 			case VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT:
 				createDebugReportCallback((VkDebugReportCallbackCreateInfoEXT*)next, nullptr);
 				break;
+			case VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT:
+				createDebugUtilsMessenger((VkDebugUtilsMessengerCreateInfoEXT*)next, nullptr);
+				break;
 			default:
 				break;
 		}
@@ -433,6 +529,17 @@
 	ADD_INST_EXT_ENTRY_POINT(vkCreateDebugReportCallbackEXT, EXT_DEBUG_REPORT);
 	ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, EXT_DEBUG_REPORT);
 	ADD_INST_EXT_ENTRY_POINT(vkDebugReportMessageEXT, EXT_DEBUG_REPORT);
+	ADD_INST_EXT_ENTRY_POINT(vkSetDebugUtilsObjectNameEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkSetDebugUtilsObjectTagEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkQueueBeginDebugUtilsLabelEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkQueueEndDebugUtilsLabelEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkQueueInsertDebugUtilsLabelEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkCmdBeginDebugUtilsLabelEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkCmdEndDebugUtilsLabelEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkCmdInsertDebugUtilsLabelEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkCreateDebugUtilsMessengerEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkDestroyDebugUtilsMessengerEXT, EXT_DEBUG_UTILS);
+	ADD_INST_EXT_ENTRY_POINT(vkSubmitDebugUtilsMessageEXT, EXT_DEBUG_UTILS);
 
 #ifdef VK_USE_PLATFORM_IOS_MVK
 	ADD_INST_EXT_ENTRY_POINT(vkCreateIOSSurfaceMVK, MVK_IOS_SURFACE);
@@ -527,7 +634,7 @@
 	_useCreationCallbacks = true;
 	mvkDestroyContainerContents(_physicalDevices);
 
-	lock_guard<mutex> lock(_drcbLock);
+	lock_guard<mutex> lock(_dcbLock);
 	mvkDestroyContainerContents(_debugReportCallbacks);
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index f84d7b7..9e68050 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -46,6 +46,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_PIPELINE_LAYOUT; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT; }
 
@@ -129,6 +132,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_PIPELINE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT; }
 
@@ -332,6 +338,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_PIPELINE_CACHE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h
index 72f955b..23bdb68 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueryPool.h
@@ -44,6 +44,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_QUERY_POOL; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
index 2a365bd..b661f64 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
@@ -78,6 +78,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_QUEUE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
index 56209da..a93dba6 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
@@ -147,6 +147,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_RENDER_PASS; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
index b01add4..1fd1670 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
@@ -169,6 +169,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_SHADER_MODULE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
index bac1e64..59f8b73 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.h
@@ -47,6 +47,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_SURFACE_KHR; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
index affd7a3..20e3dbe 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h
@@ -34,6 +34,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_SWAPCHAIN_KHR; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
index 6904fa8..1fa52cf 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h
@@ -112,6 +112,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_SEMAPHORE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT; }
 
@@ -158,6 +161,9 @@
 
 public:
 
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_FENCE; }
+
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT; }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
index 2f20ad9..0a326dd 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
@@ -50,6 +50,9 @@
 	/** Returns a reference to this object suitable for use as a Vulkan API handle. */
 	virtual void* getVkHandle() { return this; }
 
+	/** Returns the Vulkan type of this object. */
+	virtual VkObjectType getVkObjectType() = 0;
+
 	/** Returns the debug report object type of this object. */
 	virtual VkDebugReportObjectTypeEXT getVkDebugReportObjectType() = 0;
 
@@ -86,6 +89,9 @@
 	/** Returns the MVKVulkanAPIObject instance referenced by the object of the given type. */
 	static MVKVulkanAPIObject* getMVKVulkanAPIObject(VkDebugReportObjectTypeEXT objType, uint64_t object);
 
+	/** Returns the MVKVulkanAPIObject instance referenced by the object of the given type. */
+	static MVKVulkanAPIObject* getMVKVulkanAPIObject(VkObjectType objType, uint64_t objectHandle);
+
 	/** Construct an empty instance. Declared here to support copy constructor. */
 	MVKVulkanAPIObject() {}
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
index 0c5c10e..6fd294d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
@@ -77,6 +77,20 @@
 	}
 }
 
+MVKVulkanAPIObject* MVKVulkanAPIObject::getMVKVulkanAPIObject(VkObjectType objType, uint64_t objectHandle) {
+	void* pVkObj = (void*)objectHandle;
+	switch (objType) {
+		case VK_OBJECT_TYPE_INSTANCE:
+		case VK_OBJECT_TYPE_PHYSICAL_DEVICE:
+		case VK_OBJECT_TYPE_DEVICE:
+		case VK_OBJECT_TYPE_QUEUE:
+		case VK_OBJECT_TYPE_COMMAND_BUFFER:
+			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 fc42c73..5e64a66 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -54,6 +54,7 @@
 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_debug_utils, EXT_DEBUG_UTILS)
 MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET)
 MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET)
 MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER)
diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
index 39f497e..2e76c54 100644
--- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
+++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.mm
@@ -98,11 +98,11 @@
 
 	MVKVulkanAPIObject* mvkAPIObj = mvkObj ? mvkObj->getVulkanAPIObject() : nullptr;
 	MVKInstance* mvkInst = mvkAPIObj ? mvkAPIObj->getInstance() : nullptr;
-	bool hasDebugReportCallbacks = mvkInst && mvkInst->hasDebugReportCallbacks();
+	bool hasDebugCallbacks = mvkInst && mvkInst->hasDebugCallbacks();
 	bool shouldLog = (aslLvl < (_mvkLogLevel << 2));
 
 	// Fail fast to avoid further unnecessary processing.
-	if ( !(shouldLog || hasDebugReportCallbacks) ) { return; }
+	if ( !(shouldLog || hasDebugCallbacks) ) { return; }
 
 	va_list origArgs, redoArgs;
 	va_copy(origArgs, args);
@@ -131,7 +131,7 @@
 	if (shouldLog) { fprintf(stderr, "[%s] %s\n", getReportingLevelString(aslLvl), pMessage); }
 
 	// Broadcast the message to any Vulkan debug report callbacks
-	if (hasDebugReportCallbacks) { mvkInst->debugReportMessage(mvkAPIObj, aslLvl, pMessage); }
+	if (hasDebugCallbacks) { mvkInst->debugReportMessage(mvkAPIObj, aslLvl, pMessage); }
 }
 
 VkResult MVKBaseObject::reportError(VkResult vkErr, const char* format, ...) {
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index ed6f65b..fb5ffac 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -2181,6 +2181,108 @@
 
 
 #pragma mark -
+#pragma mark VK_EXT_debug_utils extension
+
+MVK_PUBLIC_SYMBOL VkResult vkSetDebugUtilsObjectNameEXT(
+	VkDevice                                    device,
+	const VkDebugUtilsObjectNameInfoEXT*        pNameInfo) {
+
+	MVKTraceVulkanCall();
+	MVKVulkanAPIObject* mvkObj = MVKVulkanAPIObject::getMVKVulkanAPIObject(pNameInfo->objectType, pNameInfo->objectHandle);
+	return mvkObj ? mvkObj->setDebugName(pNameInfo->pObjectName) : VK_SUCCESS;
+}
+
+MVK_PUBLIC_SYMBOL VkResult vkSetDebugUtilsObjectTagEXT(
+	VkDevice                                    device,
+	const VkDebugUtilsObjectTagInfoEXT*         pTagInfo) {
+
+	MVKTraceVulkanCall();
+	return VK_SUCCESS;
+}
+
+MVK_PUBLIC_SYMBOL void vkQueueBeginDebugUtilsLabelEXT(
+	VkQueue                                     queue,
+	const VkDebugUtilsLabelEXT*                 pLabelInfo) {
+
+	MVKTraceVulkanCall();
+}
+
+MVK_PUBLIC_SYMBOL void vkQueueEndDebugUtilsLabelEXT(
+	VkQueue                                     queue) {
+
+	MVKTraceVulkanCall();
+}
+
+MVK_PUBLIC_SYMBOL void vkQueueInsertDebugUtilsLabelEXT(
+	VkQueue                                     queue,
+	const VkDebugUtilsLabelEXT*                 pLabelInfo) {
+
+	MVKTraceVulkanCall();
+}
+
+MVK_PUBLIC_SYMBOL void vkCmdBeginDebugUtilsLabelEXT(
+	VkCommandBuffer                             commandBuffer,
+	const VkDebugUtilsLabelEXT*                 pLabelInfo) {
+
+	MVKTraceVulkanCall();
+	MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
+	mvkCmdBeginDebugUtilsLabel(cmdBuff, pLabelInfo);
+}
+
+MVK_PUBLIC_SYMBOL void vkCmdEndDebugUtilsLabelEXT(
+	VkCommandBuffer                             commandBuffer) {
+
+	MVKTraceVulkanCall();
+	MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
+	mvkCmdEndDebugUtilsLabel(cmdBuff);
+}
+
+MVK_PUBLIC_SYMBOL void vkCmdInsertDebugUtilsLabelEXT(
+	VkCommandBuffer                             commandBuffer,
+	const VkDebugUtilsLabelEXT*                 pLabelInfo) {
+
+	MVKTraceVulkanCall();
+	MVKCommandBuffer* cmdBuff = MVKCommandBuffer::getMVKCommandBuffer(commandBuffer);
+	mvkCmdInsertDebugUtilsLabel(cmdBuff, pLabelInfo);
+}
+
+MVK_PUBLIC_SYMBOL VkResult vkCreateDebugUtilsMessengerEXT(
+	VkInstance                                  instance,
+	const VkDebugUtilsMessengerCreateInfoEXT*   pCreateInfo,
+	const VkAllocationCallbacks*                pAllocator,
+	VkDebugUtilsMessengerEXT*                   pMessenger) {
+
+	MVKTraceVulkanCall();
+	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
+	MVKDebugUtilsMessenger* mvkDUM = mvkInst->createDebugUtilsMessenger(pCreateInfo, pAllocator);
+	*pMessenger = (VkDebugUtilsMessengerEXT)mvkDUM;
+	return mvkDUM->getConfigurationResult();
+}
+
+MVK_PUBLIC_SYMBOL void vkDestroyDebugUtilsMessengerEXT(
+	VkInstance                                  instance,
+	VkDebugUtilsMessengerEXT                    messenger,
+	const VkAllocationCallbacks*                pAllocator) {
+
+	MVKTraceVulkanCall();
+	if ( !messenger ) { return; }
+	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
+	mvkInst->destroyDebugUtilsMessenger((MVKDebugUtilsMessenger*)messenger, pAllocator);
+}
+
+MVK_PUBLIC_SYMBOL void vkSubmitDebugUtilsMessageEXT(
+	VkInstance                                  instance,
+	VkDebugUtilsMessageSeverityFlagBitsEXT      messageSeverity,
+	VkDebugUtilsMessageTypeFlagsEXT             messageTypes,
+	const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) {
+
+	MVKTraceVulkanCall();
+	MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
+	mvkInst->debugUtilsMessage(messageSeverity, messageTypes, pCallbackData);
+}
+
+
+#pragma mark -
 #pragma mark iOS & macOS surface extensions
 
 MVK_PUBLIC_SYMBOL VkResult vkCreate_PLATFORM_SurfaceMVK(