Merge pull request #1370 from f32by/imageless_framebuffer

Support the VK_KHR_imageless_framebuffer extension.
diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md
index 413436c..3dbdf9c 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -64,7 +64,7 @@
 *Vulkan*, which uses *SPIR-V*. **MoltenVK** automatically converts your *SPIR-V* shaders 
 to their *MSL* equivalents.
 
-To provide *Vulkan* capability to the*macOS*, *iOS*, and *tvOS* platforms, **MoltenVK** uses 
+To provide *Vulkan* capability to the *macOS*, *iOS*, and *tvOS* platforms, **MoltenVK** uses 
 *Apple's* publicly available API's, including *Metal*. **MoltenVK** does **_not_** use any 
 private or undocumented API calls or features, so your app will be compatible with all 
 standard distribution channels, including *Apple's App Store*.
@@ -450,7 +450,7 @@
 <a name="shader_load_time"></a>
 ### Shader Loading Time
 
-A number of steps is require to load and compile *SPIR-V* shaders into a form that *Metal* can use. 
+A number of steps is required to load and compile *SPIR-V* shaders into a form that *Metal* can use. 
 Although the overall process is fast, the slowest step involves converting shaders from *SPIR-V* to
 *MSL* source code format.
 
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 5dfa180..cd22261 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -4,7 +4,7 @@
 
 
 
-#What's New in MoltenVK
+# What's New in MoltenVK
 
 Copyright (c) 2015-2021 [The Brenwill Workshop Ltd.](http://www.brenwill.com)
 
@@ -166,7 +166,7 @@
 MoltenVK 1.1.1
 --------------
 
-Released 2010/12/09
+Released 2020/12/09
 
 - Add support for extensions:
 	- `VK_KHR_sampler_mirror_clamp_to_edge` (iOS)
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 88d7813..952c7ec 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -517,6 +517,9 @@
 #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 125e122..801b3f7 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -807,47 +807,60 @@
 #pragma mark -
 #pragma mark Support functions
 
+NSString* mvkMTLCommandBufferLabel(MVKCommandUse cmdUse) {
+	switch (cmdUse) {
+		case kMVKCommandUseEndCommandBuffer:                return @"vkEndCommandBuffer (Prefilled) CommandBuffer";
+		case kMVKCommandUseQueueSubmit:                     return @"vkQueueSubmit CommandBuffer";
+		case kMVKCommandUseQueuePresent:                    return @"vkQueuePresentKHR CommandBuffer";
+		case kMVKCommandUseQueueWaitIdle:                   return @"vkQueueWaitIdle CommandBuffer";
+		case kMVKCommandUseDeviceWaitIdle:                  return @"vkDeviceWaitIdle CommandBuffer";
+		case kMVKCommandUseAcquireNextImage:                return @"vkAcquireNextImageKHR CommandBuffer";
+		case kMVKCommandUseInvalidateMappedMemoryRanges:    return @"vkInvalidateMappedMemoryRanges CommandBuffer";
+		default:                                            return @"Unknown Use CommandBuffer";
+	}
+}
+
 NSString* mvkMTLRenderCommandEncoderLabel(MVKCommandUse cmdUse) {
     switch (cmdUse) {
-        case kMVKCommandUseBeginRenderPass:         return @"vkCmdBeginRenderPass RenderEncoder";
-        case kMVKCommandUseNextSubpass:             return @"vkCmdNextSubpass RenderEncoder";
-        case kMVKCommandUseBlitImage:               return @"vkCmdBlitImage RenderEncoder";
-        case kMVKCommandUseResolveImage:            return @"vkCmdResolveImage (resolve stage) RenderEncoder";
-        case kMVKCommandUseResolveExpandImage:      return @"vkCmdResolveImage (expand stage) RenderEncoder";
-        case kMVKCommandUseClearColorImage:         return @"vkCmdClearColorImage RenderEncoder";
-        case kMVKCommandUseClearDepthStencilImage:  return @"vkCmdClearDepthStencilImage RenderEncoder";
-        default:                                    return @"Unknown Use RenderEncoder";
+        case kMVKCommandUseBeginRenderPass:                 return @"vkCmdBeginRenderPass RenderEncoder";
+        case kMVKCommandUseNextSubpass:                     return @"vkCmdNextSubpass RenderEncoder";
+        case kMVKCommandUseBlitImage:                       return @"vkCmdBlitImage RenderEncoder";
+        case kMVKCommandUseResolveImage:                    return @"vkCmdResolveImage (resolve stage) RenderEncoder";
+        case kMVKCommandUseResolveExpandImage:              return @"vkCmdResolveImage (expand stage) RenderEncoder";
+        case kMVKCommandUseClearColorImage:                 return @"vkCmdClearColorImage RenderEncoder";
+        case kMVKCommandUseClearDepthStencilImage:          return @"vkCmdClearDepthStencilImage RenderEncoder";
+        default:                                            return @"Unknown Use RenderEncoder";
     }
 }
 
 NSString* mvkMTLBlitCommandEncoderLabel(MVKCommandUse cmdUse) {
     switch (cmdUse) {
-        case kMVKCommandUsePipelineBarrier:     return @"vkCmdPipelineBarrier BlitEncoder";
-        case kMVKCommandUseCopyImage:           return @"vkCmdCopyImage BlitEncoder";
-        case kMVKCommandUseResolveCopyImage:    return @"vkCmdResolveImage (copy stage) RenderEncoder";
-        case kMVKCommandUseCopyBuffer:          return @"vkCmdCopyBuffer BlitEncoder";
-        case kMVKCommandUseCopyBufferToImage:   return @"vkCmdCopyBufferToImage BlitEncoder";
-        case kMVKCommandUseCopyImageToBuffer:   return @"vkCmdCopyImageToBuffer BlitEncoder";
-        case kMVKCommandUseFillBuffer:          return @"vkCmdFillBuffer BlitEncoder";
-        case kMVKCommandUseUpdateBuffer:        return @"vkCmdUpdateBuffer BlitEncoder";
-        case kMVKCommandUseResetQueryPool:      return @"vkCmdResetQueryPool BlitEncoder";
-        case kMVKCommandUseCopyQueryPoolResults:return @"vkCmdCopyQueryPoolResults BlitEncoder";
-        default:                                return @"Unknown Use BlitEncoder";
+        case kMVKCommandUsePipelineBarrier:                 return @"vkCmdPipelineBarrier BlitEncoder";
+        case kMVKCommandUseCopyImage:                       return @"vkCmdCopyImage BlitEncoder";
+        case kMVKCommandUseResolveCopyImage:                return @"vkCmdResolveImage (copy stage) RenderEncoder";
+        case kMVKCommandUseCopyBuffer:                      return @"vkCmdCopyBuffer BlitEncoder";
+        case kMVKCommandUseCopyBufferToImage:               return @"vkCmdCopyBufferToImage BlitEncoder";
+        case kMVKCommandUseCopyImageToBuffer:               return @"vkCmdCopyImageToBuffer BlitEncoder";
+        case kMVKCommandUseFillBuffer:                      return @"vkCmdFillBuffer BlitEncoder";
+        case kMVKCommandUseUpdateBuffer:                    return @"vkCmdUpdateBuffer BlitEncoder";
+        case kMVKCommandUseResetQueryPool:                  return @"vkCmdResetQueryPool BlitEncoder";
+        case kMVKCommandUseCopyQueryPoolResults:            return @"vkCmdCopyQueryPoolResults BlitEncoder";
+        default:                                            return @"Unknown Use BlitEncoder";
     }
 }
 
 NSString* mvkMTLComputeCommandEncoderLabel(MVKCommandUse cmdUse) {
     switch (cmdUse) {
-        case kMVKCommandUseDispatch:            return @"vkCmdDispatch ComputeEncoder";
-        case kMVKCommandUseCopyBuffer:          return @"vkCmdCopyBuffer ComputeEncoder";
-        case kMVKCommandUseCopyBufferToImage:   return @"vkCmdCopyBufferToImage ComputeEncoder";
-        case kMVKCommandUseCopyImageToBuffer:   return @"vkCmdCopyImageToBuffer ComputeEncoder";
-        case kMVKCommandUseFillBuffer:          return @"vkCmdFillBuffer ComputeEncoder";
-        case kMVKCommandUseClearColorImage:     return @"vkCmdClearColorImage ComputeEncoder";
-        case kMVKCommandUseTessellationVertexTessCtl: return @"vkCmdDraw (vertex and tess control stages) ComputeEncoder";
-        case kMVKCommandUseMultiviewInstanceCountAdjust: return @"vkCmdDraw (multiview instance count adjustment) ComputeEncoder";
-        case kMVKCommandUseCopyQueryPoolResults:return @"vkCmdCopyQueryPoolResults ComputeEncoder";
-        case kMVKCommandUseAccumOcclusionQuery: return @"Post-render-pass occlusion query accumulation ComputeEncoder";
-        default:                                return @"Unknown Use ComputeEncoder";
+        case kMVKCommandUseDispatch:                        return @"vkCmdDispatch ComputeEncoder";
+        case kMVKCommandUseCopyBuffer:                      return @"vkCmdCopyBuffer ComputeEncoder";
+        case kMVKCommandUseCopyBufferToImage:               return @"vkCmdCopyBufferToImage ComputeEncoder";
+        case kMVKCommandUseCopyImageToBuffer:               return @"vkCmdCopyImageToBuffer ComputeEncoder";
+        case kMVKCommandUseFillBuffer:                      return @"vkCmdFillBuffer ComputeEncoder";
+        case kMVKCommandUseClearColorImage:                 return @"vkCmdClearColorImage ComputeEncoder";
+        case kMVKCommandUseTessellationVertexTessCtl:       return @"vkCmdDraw (vertex and tess control stages) ComputeEncoder";
+        case kMVKCommandUseMultiviewInstanceCountAdjust:    return @"vkCmdDraw (multiview instance count adjustment) ComputeEncoder";
+        case kMVKCommandUseCopyQueryPoolResults:            return @"vkCmdCopyQueryPoolResults ComputeEncoder";
+        case kMVKCommandUseAccumOcclusionQuery:             return @"Post-render-pass occlusion query accumulation ComputeEncoder";
+        default:                                            return @"Unknown Use ComputeEncoder";
     }
 }
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
index 366ebdf..c219539 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)->getMTLCommandBuffer(true) retain];
+	return [_device->getQueue(_queueFamilyIndex, queueIndex)->getMTLCommandBuffer(kMVKCommandUseEndCommandBuffer, true) retain];
 }
 
 // Clear the command type pool member variables.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
index d5c3421..c192d5d 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
@@ -36,7 +36,7 @@
 	id<MTLFunction> vtxFunc = newFunctionNamed(isLayeredBlit ? "vtxCmdBlitImageLayered" : "vtxCmdBlitImage");	// temp retain
 	id<MTLFunction> fragFunc = newBlitFragFunction(blitKey);													// temp retain
     MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new];									// temp retain
-    plDesc.label = @"CmdBlitImage";
+    plDesc.label = @"vkCmdBlitImage";
 
 	plDesc.vertexFunction = vtxFunc;
 	plDesc.fragmentFunction = fragFunc;
@@ -116,7 +116,7 @@
 	id<MTLFunction> vtxFunc = newClearVertFunction(attKey);						// temp retain
 	id<MTLFunction> fragFunc = newClearFragFunction(attKey);					// temp retain
 	MTLRenderPipelineDescriptor* plDesc = [MTLRenderPipelineDescriptor new];	// temp retain
-    plDesc.label = @"CmdClearAttachments";
+    plDesc.label = @"vkCmdClearAttachments";
 	plDesc.vertexFunction = vtxFunc;
     plDesc.fragmentFunction = fragFunc;
 	plDesc.sampleCount = attKey.mtlSampleCount;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 013b251..6ca2971 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -2860,7 +2860,7 @@
 	VkResult rslt = VK_SUCCESS;
 	for (auto& queues : _queuesByQueueFamilyIndex) {
 		for (MVKQueue* q : queues) {
-			if ((rslt = q->waitIdle()) != VK_SUCCESS) { return rslt; }
+			if ((rslt = q->waitIdle(kMVKCommandUseDeviceWaitIdle)) != VK_SUCCESS) { return rslt; }
 		}
 	}
 	return VK_SUCCESS;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
index 7f65a32..eac900f 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()->getMTLCommandBuffer(); }
+			if ( !pBlitEnc->mtlCmdBuffer) { pBlitEnc->mtlCmdBuffer = _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseInvalidateMappedMemoryRanges); }
 			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 094bda9..79e54df 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -1210,7 +1210,7 @@
 		@autoreleasepool {
 			MVKSemaphore* mvkSem = signaler.semaphore;
 			id<MTLCommandBuffer> mtlCmdBuff = (mvkSem && mvkSem->isUsingCommandEncoding()
-											   ? _device->getAnyQueue()->getMTLCommandBuffer()
+											   ? _device->getAnyQueue()->getMTLCommandBuffer(kMVKCommandUseAcquireNextImage)
 											   : nil);
 			signal(signaler, mtlCmdBuff);
 			[mtlCmdBuff commit];
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
index cc4d3c2..e9c92ee 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h
@@ -89,13 +89,13 @@
 #pragma mark Queue submissions
 
 	/** Submits the specified command buffers to the queue. */
-	VkResult submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence);
+	VkResult submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence, MVKCommandUse cmdUse);
 
 	/** Submits the specified presentation command to the queue. */
 	VkResult submit(const VkPresentInfoKHR* pPresentInfo);
 
 	/** Block the current thread until this queue is idle. */
-	VkResult waitIdle();
+	VkResult waitIdle(MVKCommandUse cmdUse);
 
 	/** Return the name of this queue. */
 	const std::string& getName() { return _name; }
@@ -107,7 +107,7 @@
 	id<MTLCommandQueue> getMTLCommandQueue() { return _mtlQueue; }
 
 	/** Returns a Metal command buffer from the Metal queue. */
-	id<MTLCommandBuffer> getMTLCommandBuffer(bool retainRefs = false);
+	id<MTLCommandBuffer> getMTLCommandBuffer(MVKCommandUse cmdUse, bool retainRefs = false);
 
 #pragma mark Construction
 	
@@ -193,7 +193,7 @@
 public:
 	void execute() override;
 
-	MVKQueueCommandBufferSubmission(MVKQueue* queue, const VkSubmitInfo* pSubmit, VkFence fence);
+	MVKQueueCommandBufferSubmission(MVKQueue* queue, const VkSubmitInfo* pSubmit, VkFence fence, MVKCommandUse cmdUse);
 
 	~MVKQueueCommandBufferSubmission() override;
 
@@ -209,6 +209,7 @@
 	MVKSmallVector<std::pair<MVKSemaphore*, uint64_t>> _signalSemaphores;
 	MVKFence* _fence;
 	id<MTLCommandBuffer> _activeMTLCommandBuffer;
+	MVKCommandUse _commandUse;
 };
 
 
@@ -221,7 +222,7 @@
 
 public:
 	MVKQueueFullCommandBufferSubmission(MVKQueue* queue, const VkSubmitInfo* pSubmit, VkFence fence) :
-		MVKQueueCommandBufferSubmission(queue, pSubmit, fence) {
+		MVKQueueCommandBufferSubmission(queue, pSubmit, fence, kMVKCommandUseQueueSubmit) {
 
 			// pSubmit can be null if just tracking the fence alone
 			if (pSubmit) {
@@ -255,7 +256,6 @@
 									 const VkPresentInfoKHR* pPresentInfo);
 
 protected:
-	id<MTLCommandBuffer> getMTLCommandBuffer();
 	void stopAutoGPUCapture();
 
 	MVKSmallVector<MVKPresentTimingInfo, 4> _presentInfo;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
index f9556af..22bd1b2 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
@@ -88,11 +88,11 @@
 	return rslt;
 }
 
-VkResult MVKQueue::submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence) {
+VkResult MVKQueue::submit(uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence, MVKCommandUse cmdUse) {
 
     // Fence-only submission
     if (submitCount == 0 && fence) {
-        return submit(new MVKQueueCommandBufferSubmission(this, nullptr, fence));
+        return submit(new MVKQueueCommandBufferSubmission(this, nullptr, fence, cmdUse));
     }
 
     VkResult rslt = VK_SUCCESS;
@@ -129,7 +129,7 @@
 }
 
 // Create an empty submit struct and fence, submit to queue and wait on fence.
-VkResult MVKQueue::waitIdle() {
+VkResult MVKQueue::waitIdle(MVKCommandUse cmdUse) {
 
 	if (_device->getConfigurationResult() != VK_SUCCESS) { return _device->getConfigurationResult(); }
 
@@ -143,13 +143,14 @@
 	// the command submission finishes, so we can't allocate MVKFence locally on the stack.
 	MVKFence* mvkFence = new MVKFence(_device, &vkFenceInfo);
 	VkFence vkFence = (VkFence)mvkFence;
-	submit(0, nullptr, vkFence);
+	submit(0, nullptr, vkFence, cmdUse);
 	VkResult rslt = mvkWaitForFences(_device, 1, &vkFence, false);
 	mvkFence->destroy();
 	return rslt;
 }
 
-id<MTLCommandBuffer> MVKQueue::getMTLCommandBuffer(bool retainRefs) {
+id<MTLCommandBuffer> MVKQueue::getMTLCommandBuffer(MVKCommandUse cmdUse, bool retainRefs) {
+	id<MTLCommandBuffer> mtlCmdBuff = nil;
 #if MVK_XCODE_12
 	if ([_mtlQueue respondsToSelector: @selector(commandBufferWithDescriptor:)]) {
 		MTLCommandBufferDescriptor* mtlCmdBuffDesc = [MTLCommandBufferDescriptor new];	// temp retain
@@ -157,16 +158,17 @@
 		if (mvkConfig().debugMode) {
 			mtlCmdBuffDesc.errorOptions |= MTLCommandBufferErrorOptionEncoderExecutionStatus;
 		}
-		id<MTLCommandBuffer> cmdBuff = [_mtlQueue commandBufferWithDescriptor: mtlCmdBuffDesc];
+		mtlCmdBuff = [_mtlQueue commandBufferWithDescriptor: mtlCmdBuffDesc];
 		[mtlCmdBuffDesc release];														// temp release
-		return cmdBuff;
 	} else
 #endif
 	if (retainRefs) {
-		return [_mtlQueue commandBuffer];
+		mtlCmdBuff = [_mtlQueue commandBuffer];
 	} else {
-		return [_mtlQueue commandBufferWithUnretainedReferences];
+		mtlCmdBuff = [_mtlQueue commandBufferWithUnretainedReferences];
 	}
+	setLabelIfNotNil(mtlCmdBuff, mvkMTLCommandBufferLabel(cmdUse));
+	return mtlCmdBuff;
 }
 
 
@@ -282,7 +284,7 @@
 // Returns the active MTLCommandBuffer, lazily retrieving it from the queue if needed.
 id<MTLCommandBuffer> MVKQueueCommandBufferSubmission::getActiveMTLCommandBuffer() {
 	if ( !_activeMTLCommandBuffer ) {
-		setActiveMTLCommandBuffer(_queue->getMTLCommandBuffer());
+		setActiveMTLCommandBuffer(_queue->getMTLCommandBuffer(_commandUse));
 	}
 	return _activeMTLCommandBuffer;
 }
@@ -410,10 +412,12 @@
 // retain() each here to ensure they live long enough for this submission to finish using them.
 MVKQueueCommandBufferSubmission::MVKQueueCommandBufferSubmission(MVKQueue* queue,
 																 const VkSubmitInfo* pSubmit,
-																 VkFence fence)
-        : MVKQueueSubmission(queue,
-							 (pSubmit ? pSubmit->waitSemaphoreCount : 0),
-							 (pSubmit ? pSubmit->pWaitSemaphores : nullptr)) {
+																 VkFence fence,
+																 MVKCommandUse cmdUse) :
+	MVKQueueSubmission(queue,
+					   (pSubmit ? pSubmit->waitSemaphoreCount : 0),
+					   (pSubmit ? pSubmit->pWaitSemaphores : nullptr)),
+	_commandUse(cmdUse) {
 
     // pSubmit can be null if just tracking the fence alone
     if (pSubmit) {
@@ -466,7 +470,8 @@
 	// If the semaphores are encodable, wait on them by encoding them on the MTLCommandBuffer before presenting.
 	// If the semaphores are not encodable, wait on them inline after presenting.
 	// The semaphores know what to do.
-	id<MTLCommandBuffer> mtlCmdBuff = getMTLCommandBuffer();
+	id<MTLCommandBuffer> mtlCmdBuff = _queue->getMTLCommandBuffer(kMVKCommandUseQueuePresent);
+	[mtlCmdBuff enqueue];
 	for (auto& ws : _waitSemaphores) { ws.first->encodeWait(mtlCmdBuff, 0); }
 	for (int i = 0; i < _presentInfo.size(); i++ ) {
 		MVKPresentableSwapchainImage *img = _presentInfo[i].presentableImage;
@@ -484,13 +489,6 @@
 	this->destroy();
 }
 
-id<MTLCommandBuffer> MVKQueuePresentSurfaceSubmission::getMTLCommandBuffer() {
-	id<MTLCommandBuffer> mtlCmdBuff = _queue->getMTLCommandBuffer();
-	setLabelIfNotNil(mtlCmdBuff, @"vkQueuePresentKHR CommandBuffer");
-	[mtlCmdBuff enqueue];
-	return mtlCmdBuff;
-}
-
 void MVKQueuePresentSurfaceSubmission::stopAutoGPUCapture() {
 	if (_queue->_queueFamily->getIndex() == mvkConfig().defaultGPUCaptureScopeQueueFamilyIndex &&
 		_queue->_index == mvkConfig().defaultGPUCaptureScopeQueueIndex) {
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
index afbc1c6..e061142 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
@@ -63,32 +63,35 @@
 
 /** Tracks the Vulkan command currently being used. */
 typedef enum : uint8_t {
-    kMVKCommandUseNone,                     /**< No use defined. */
-    kMVKCommandUseQueueSubmit,              /**< vkQueueSubmit. */
-    kMVKCommandUseQueuePresent,             /**< vkQueuePresentKHR. */
-    kMVKCommandUseQueueWaitIdle,            /**< vkQueueWaitIdle. */
-    kMVKCommandUseDeviceWaitIdle,           /**< vkDeviceWaitIdle. */
-    kMVKCommandUseBeginRenderPass,          /**< vkCmdBeginRenderPass. */
-    kMVKCommandUseNextSubpass,              /**< vkCmdNextSubpass. */
-    kMVKCommandUsePipelineBarrier,          /**< vkCmdPipelineBarrier. */
-    kMVKCommandUseBlitImage,                /**< vkCmdBlitImage. */
-    kMVKCommandUseCopyImage,                /**< vkCmdCopyImage. */
-    kMVKCommandUseResolveImage,             /**< vkCmdResolveImage - resolve stage. */
-    kMVKCommandUseResolveExpandImage,       /**< vkCmdResolveImage - expand stage. */
-    kMVKCommandUseResolveCopyImage,         /**< vkCmdResolveImage - copy stage. */
-    kMVKCommandUseCopyBuffer,               /**< vkCmdCopyBuffer. */
-    kMVKCommandUseCopyBufferToImage,        /**< vkCmdCopyBufferToImage. */
-    kMVKCommandUseCopyImageToBuffer,        /**< vkCmdCopyImageToBuffer. */
-    kMVKCommandUseFillBuffer,               /**< vkCmdFillBuffer. */
-    kMVKCommandUseUpdateBuffer,             /**< vkCmdUpdateBuffer. */
-    kMVKCommandUseClearColorImage,          /**< vkCmdClearColorImage. */
-    kMVKCommandUseClearDepthStencilImage,   /**< vkCmdClearDepthStencilImage. */
-    kMVKCommandUseResetQueryPool,           /**< vkCmdResetQueryPool. */
-    kMVKCommandUseDispatch,                 /**< vkCmdDispatch. */
-    kMVKCommandUseTessellationVertexTessCtl,/**< vkCmdDraw* - vertex and tessellation control stages. */
-	kMVKCommandUseMultiviewInstanceCountAdjust,/**< vkCmdDrawIndirect* - adjust instance count for multiview. */
-    kMVKCommandUseCopyQueryPoolResults,     /**< vkCmdCopyQueryPoolResults. */
-    kMVKCommandUseAccumOcclusionQuery       /**< Any command terminating a Metal render pass with active visibility buffer. */
+    kMVKCommandUseNone,                         /**< No use defined. */
+	kMVKCommandUseEndCommandBuffer,             /**< vkEndCommandBuffer (prefilled VkCommandBuffer). */
+    kMVKCommandUseQueueSubmit,                  /**< vkQueueSubmit. */
+	kMVKCommandUseAcquireNextImage,             /**< vkAcquireNextImageKHR. */
+    kMVKCommandUseQueuePresent,                 /**< vkQueuePresentKHR. */
+    kMVKCommandUseQueueWaitIdle,                /**< vkQueueWaitIdle. */
+    kMVKCommandUseDeviceWaitIdle,               /**< vkDeviceWaitIdle. */
+	kMVKCommandUseInvalidateMappedMemoryRanges, /**< vkInvalidateMappedMemoryRanges. */
+    kMVKCommandUseBeginRenderPass,              /**< vkCmdBeginRenderPass. */
+    kMVKCommandUseNextSubpass,                  /**< vkCmdNextSubpass. */
+    kMVKCommandUsePipelineBarrier,              /**< vkCmdPipelineBarrier. */
+    kMVKCommandUseBlitImage,                    /**< vkCmdBlitImage. */
+    kMVKCommandUseCopyImage,                    /**< vkCmdCopyImage. */
+    kMVKCommandUseResolveImage,                 /**< vkCmdResolveImage - resolve stage. */
+    kMVKCommandUseResolveExpandImage,           /**< vkCmdResolveImage - expand stage. */
+    kMVKCommandUseResolveCopyImage,             /**< vkCmdResolveImage - copy stage. */
+    kMVKCommandUseCopyBuffer,                   /**< vkCmdCopyBuffer. */
+    kMVKCommandUseCopyBufferToImage,            /**< vkCmdCopyBufferToImage. */
+    kMVKCommandUseCopyImageToBuffer,            /**< vkCmdCopyImageToBuffer. */
+    kMVKCommandUseFillBuffer,                   /**< vkCmdFillBuffer. */
+    kMVKCommandUseUpdateBuffer,                 /**< vkCmdUpdateBuffer. */
+    kMVKCommandUseClearColorImage,              /**< vkCmdClearColorImage. */
+    kMVKCommandUseClearDepthStencilImage,       /**< vkCmdClearDepthStencilImage. */
+    kMVKCommandUseResetQueryPool,               /**< vkCmdResetQueryPool. */
+    kMVKCommandUseDispatch,                     /**< vkCmdDispatch. */
+    kMVKCommandUseTessellationVertexTessCtl,    /**< vkCmdDraw* - vertex and tessellation control stages. */
+	kMVKCommandUseMultiviewInstanceCountAdjust, /**< vkCmdDrawIndirect* - adjust instance count for multiview. */
+    kMVKCommandUseCopyQueryPoolResults,         /**< vkCmdCopyQueryPoolResults. */
+    kMVKCommandUseAccumOcclusionQuery           /**< Any command terminating a Metal render pass with active visibility buffer. */
 } MVKCommandUse;
 
 /** Represents a given stage of a graphics pipeline. */
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 60fc694..e595e7e 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -397,7 +397,7 @@
 
 	MVKTraceVulkanCallStart();
 	MVKQueue* mvkQ = MVKQueue::getMVKQueue(queue);
-	VkResult rslt = mvkQ->submit(submitCount, pSubmits, fence);
+	VkResult rslt = mvkQ->submit(submitCount, pSubmits, fence, kMVKCommandUseQueueSubmit);
 	MVKTraceVulkanCallEnd();
 	return rslt;
 }
@@ -407,7 +407,7 @@
 	
 	MVKTraceVulkanCallStart();
 	MVKQueue* mvkQ = MVKQueue::getMVKQueue(queue);
-	VkResult rslt = mvkQ->waitIdle();
+	VkResult rslt = mvkQ->waitIdle(kMVKCommandUseQueueWaitIdle);
 	MVKTraceVulkanCallEnd();
 	return rslt;
 }