Merge pull request #1281 from cdavis5e/enhanced-cmdbuf-errors

Log enhanced command buffer errors in debug mode.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
index 1450ec7..366ebdf 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
@@ -78,7 +78,7 @@
 }
 
 id<MTLCommandBuffer> MVKCommandPool::newMTLCommandBuffer(uint32_t queueIndex) {
-	return [[_device->getQueue(_queueFamilyIndex, queueIndex)->getMTLCommandQueue() commandBuffer] retain];
+	return [_device->getQueue(_queueFamilyIndex, queueIndex)->getMTLCommandBuffer(true) retain];
 }
 
 // Clear the command type pool member variables.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
index b61fff1..7f65a32 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
@@ -109,7 +109,7 @@
 
 #if MVK_MACOS
 		if (pBlitEnc && _mtlBuffer && _mtlStorageMode == MTLStorageModeManaged) {
-			if ( !pBlitEnc->mtlCmdBuffer) { pBlitEnc->mtlCmdBuffer = [_device->getAnyQueue()->getMTLCommandQueue() commandBufferWithUnretainedReferences]; }
+			if ( !pBlitEnc->mtlCmdBuffer) { pBlitEnc->mtlCmdBuffer = _device->getAnyQueue()->getMTLCommandBuffer(); }
 			if ( !pBlitEnc->mtlBlitEncoder) { pBlitEnc->mtlBlitEncoder = [pBlitEnc->mtlCmdBuffer blitCommandEncoder]; }
 			[pBlitEnc->mtlBlitEncoder synchronizeResource: _mtlBuffer];
 		}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 65a9524..d72d8a8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -1209,7 +1209,7 @@
 		@autoreleasepool {
 			MVKSemaphore* mvkSem = signaler.semaphore;
 			id<MTLCommandBuffer> mtlCmdBuff = (mvkSem && mvkSem->isUsingCommandEncoding()
-											   ? [_device->getAnyQueue()->getMTLCommandQueue() commandBufferWithUnretainedReferences]
+											   ? _device->getAnyQueue()->getMTLCommandBuffer()
 											   : nil);
 			signal(signaler, mtlCmdBuff);
 			[mtlCmdBuff commit];
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
index a143819..b72edd4 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
@@ -106,6 +106,9 @@
 	/** Returns the Metal queue underlying this queue. */
 	inline id<MTLCommandQueue> getMTLCommandQueue() { return _mtlQueue; }
 
+	/** Returns a Metal command buffer from the Metal queue. */
+	id<MTLCommandBuffer> getMTLCommandBuffer(bool retainRefs = false);
+
 #pragma mark Construction
 	
 	/** Constructs an instance for the device and queue family. */
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
index 940b4a5..e80be13 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
@@ -145,6 +145,26 @@
 	return mvkWaitForFences(_device, 1, &fence, false);
 }
 
+id<MTLCommandBuffer> MVKQueue::getMTLCommandBuffer(bool retainRefs) {
+#if MVK_XCODE_12
+	if ([_mtlQueue respondsToSelector: @selector(commandBufferWithDescriptor:)]) {
+		MTLCommandBufferDescriptor* mtlCmdBuffDesc = [MTLCommandBufferDescriptor new];	// temp retain
+		mtlCmdBuffDesc.retainedReferences = retainRefs;
+		if (mvkGetMVKConfiguration()->debugMode) {
+			mtlCmdBuffDesc.errorOptions |= MTLCommandBufferErrorOptionEncoderExecutionStatus;
+		}
+		id<MTLCommandBuffer> cmdBuff = [_mtlQueue commandBufferWithDescriptor: mtlCmdBuffDesc];
+		[mtlCmdBuffDesc release];														// temp release
+		return cmdBuff;
+	} else
+#endif
+	if (retainRefs) {
+		return [_mtlQueue commandBuffer];
+	} else {
+		return [_mtlQueue commandBufferWithUnretainedReferences];
+	}
+}
+
 
 #pragma mark Construction
 
@@ -259,7 +279,7 @@
 // Returns the active MTLCommandBuffer, lazily retrieving it from the queue if needed.
 id<MTLCommandBuffer> MVKQueueCommandBufferSubmission::getActiveMTLCommandBuffer() {
 	if ( !_activeMTLCommandBuffer ) {
-		setActiveMTLCommandBuffer([_queue->_mtlQueue commandBufferWithUnretainedReferences]);
+		setActiveMTLCommandBuffer(_queue->getMTLCommandBuffer());
 	}
 	return _activeMTLCommandBuffer;
 }
@@ -273,6 +293,19 @@
 	[_activeMTLCommandBuffer enqueue];
 }
 
+#if MVK_XCODE_12
+static const char* mvkStringFromErrorState(MTLCommandEncoderErrorState errState) {
+	switch (errState) {
+		case MTLCommandEncoderErrorStateUnknown: return "unknown";
+		case MTLCommandEncoderErrorStateAffected: return "affected";
+		case MTLCommandEncoderErrorStateCompleted: return "completed";
+		case MTLCommandEncoderErrorStateFaulted: return "faulted";
+		case MTLCommandEncoderErrorStatePending: return "pending";
+	}
+	return "unknown";
+}
+#endif
+
 // Commits and releases the currently active MTLCommandBuffer, optionally signalling
 // when the MTLCommandBuffer is done. The first time this is called, it will wait on
 // any semaphores. We have delayed signalling the semaphores as long as possible to
@@ -312,7 +345,33 @@
 					device->getPhysicalDevice()->setConfigurationResult(VK_ERROR_DEVICE_LOST);
 					break;
 			}
+#if MVK_XCODE_12
+			if (mvkGetMVKConfiguration()->debugMode) {
+				if (&MTLCommandBufferEncoderInfoErrorKey != nullptr) {
+					if (NSArray<id<MTLCommandBufferEncoderInfo>>* mtlEncInfo = mtlCB.error.userInfo[MTLCommandBufferEncoderInfoErrorKey]) {
+						MVKLogInfo("Encoders for %p \"%s\":", mtlCB, mtlCB.label ? mtlCB.label.UTF8String : "");
+						for (id<MTLCommandBufferEncoderInfo> enc in mtlEncInfo) {
+							MVKLogInfo(" - %s: %s", enc.label.UTF8String, mvkStringFromErrorState(enc.errorState));
+							if (enc.debugSignposts.count > 0) {
+								MVKLogInfo("   Debug signposts:");
+								for (NSString* signpost in enc.debugSignposts) {
+									MVKLogInfo("    - %s", signpost.UTF8String);
+								}
+							}
+						}
+					}
+				}
+			}
+#endif
 		}
+#if MVK_XCODE_12
+		if (mvkGetMVKConfiguration()->debugMode) {
+			MVKLogInfo("Shader log messages:");
+			for (id<MTLFunctionLog> log in mtlCB.logs) {
+				MVKLogInfo("%s", log.description.UTF8String);
+			}
+		}
+#endif
 	}];
 
 	_activeMTLCommandBuffer = nil;
@@ -406,7 +465,7 @@
 }
 
 id<MTLCommandBuffer> MVKQueuePresentSurfaceSubmission::getMTLCommandBuffer() {
-	id<MTLCommandBuffer> mtlCmdBuff = [_queue->getMTLCommandQueue() commandBufferWithUnretainedReferences];
+	id<MTLCommandBuffer> mtlCmdBuff = _queue->getMTLCommandBuffer();
 	setLabelIfNotNil(mtlCmdBuff, @"vkQueuePresentKHR CommandBuffer");
 	[mtlCmdBuff enqueue];
 	return mtlCmdBuff;