Merge pull request #810 from billhollings/master

Track performance of CAMetalLayer nextDrawable call.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 0cf6b29..0871d7e 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -22,6 +22,7 @@
 - Fix crash when app does not use queue family zero.
 - Fix buffer offset in `vkCmdPushDescriptorSet()` for non-dedicated buffer memory.
 - Fix Metal validation error on push constant sizing differences between C and MSL structs.
+- Track performance of `CAMetalLayer nextDrawable` call.
 - Update `VK_MVK_MOLTENVK_SPEC_VERSION` to `24`.
 
 
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 0e30b48..72226de 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -608,7 +608,6 @@
 	MVKPerformanceTracker glslToSPRIV;					/** Convert GLSL to SPIR-V code. */
 } MVKShaderCompilationPerformance;
 
-
 /** MoltenVK performance of pipeline cache activities. */
 typedef struct {
 	MVKPerformanceTracker sizePipelineCache;			/** Calculate the size of cache data required to write MSL to pipeline cache data stream. */
@@ -620,6 +619,7 @@
 typedef struct {
 	MVKPerformanceTracker mtlQueueAccess;               /** Create an MTLCommmandQueue or access an existing cached instance. */
 	MVKPerformanceTracker mtlCommandBufferCompletion;   /** Completion of a MTLCommandBuffer on the GPU, from commit to completion callback. */
+	MVKPerformanceTracker nextCAMetalDrawable;			/** Retrieve next CAMetalDrawable from CAMetalLayer during presentation. */
 } MVKQueuePerformance;
 
 /**
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 3d72136..b6ed1e8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -2493,6 +2493,7 @@
 	if (&activityTracker == &_performanceStatistics.pipelineCache.readPipelineCache) { return "read MSL from pipeline cache"; }
 	if (&activityTracker == &_performanceStatistics.queue.mtlQueueAccess) { return "access MTLCommandQueue"; }
 	if (&activityTracker == &_performanceStatistics.queue.mtlCommandBufferCompletion) { return "complete MTLCommandBuffer"; }
+	if (&activityTracker == &_performanceStatistics.queue.nextCAMetalDrawable) { return "retrieve a CAMetalDrawable from CAMetalLayer"; }
     return "Unknown performance activity";
 }
 
@@ -2638,6 +2639,7 @@
 	_performanceStatistics.pipelineCache.readPipelineCache = initPerf;
 	_performanceStatistics.queue.mtlQueueAccess = initPerf;
 	_performanceStatistics.queue.mtlCommandBufferCompletion = initPerf;
+	_performanceStatistics.queue.nextCAMetalDrawable = initPerf;
 }
 
 void MVKDevice::initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo) {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
index d2cd34b..6be77dd 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm
@@ -269,7 +269,7 @@
 	// otherwise if this instance has no content, it will not finish() and be destroyed.
 	if (signalCompletion || _trackPerformance) {
 		[getActiveMTLCommandBuffer() addCompletedHandler: ^(id<MTLCommandBuffer> mtlCmdBuff) {
-			_queue->_device->addActivityPerformance(mkvDev->_performanceStatistics.queue.mtlCommandBufferCompletion, startTime);
+			mkvDev->addActivityPerformance(mkvDev->_performanceStatistics.queue.mtlCommandBufferCompletion, startTime);
 			if (signalCompletion) { this->finish(); }
 		}];
 	}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
index 3d146fd..003db8f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm
@@ -346,15 +346,19 @@
 #pragma mark Metal
 
 id<CAMetalDrawable> MVKSwapchain::getCAMetalDrawable(uint32_t imageIndex) {
-    if ( _mtlDrawables[imageIndex] ) { return _mtlDrawables[imageIndex]; }
-    @autoreleasepool {      // Allow auto-released drawable object to be reclaimed before end of loop
-        id<CAMetalDrawable> nextDrwbl = nil;
-        while ( !(nextDrwbl = [_mtlLayer nextDrawable]) ) {
-            MVKLogError("Drawable could not be retrieved! Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds());
-        }
-        _mtlDrawables[imageIndex] = [nextDrwbl retain];
-    }
-    return _mtlDrawables[imageIndex];
+	id<CAMetalDrawable> nextDrwbl = _mtlDrawables[imageIndex];
+	while ( !nextDrwbl ) {
+		@autoreleasepool {      // Allow auto-released drawable object to be reclaimed before end of loop
+			uint64_t startTime = _device->getPerformanceTimestamp();
+
+			nextDrwbl = _mtlLayer.nextDrawable;
+			if ( !nextDrwbl ) { MVKLogError("Drawable could not be retrieved! Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds()); }
+			_mtlDrawables[imageIndex] = [nextDrwbl retain];
+
+			_device->addActivityPerformance(_device->_performanceStatistics.queue.nextCAMetalDrawable, startTime);
+		}
+	}
+	return nextDrwbl;
 }
 
 // Removes and releases a Metal drawable object, so that it can be lazily created by getCAMetalDrawable().