Merge pull request #1806 from billhollings/fix-swapchain-mem-issues

Fix small memory issues with MVKPresentableSwapchainImage.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 1f361df..95be3e7 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -13,6 +13,18 @@
 
 
 
+MoltenVK 1.2.2
+--------------
+
+Released TBD
+
+- Fix Metal validation error caused by `CAMetalDrawable` released before 
+  `MTLCommandBuffer` is finished using it.
+- Fix memory leak of `MVKFences` and `MVKSemaphores` when 
+  a swapchain image is acquired more than it is presented.
+
+
+
 MoltenVK 1.2.1
 --------------
 
diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
index 51a605f..6aef84a 100644
--- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
+++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h
@@ -51,7 +51,7 @@
  */
 #define MVK_VERSION_MAJOR   1
 #define MVK_VERSION_MINOR   2
-#define MVK_VERSION_PATCH   1
+#define MVK_VERSION_PATCH   2
 
 #define MVK_MAKE_VERSION(major, minor, patch)    (((major) * 10000) + ((minor) * 100) + (patch))
 #define MVK_VERSION     MVK_MAKE_VERSION(MVK_VERSION_MAJOR, MVK_VERSION_MINOR, MVK_VERSION_PATCH)
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index fc9e5fd..2c19af4 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -473,6 +473,7 @@
 	void signalPresentationSemaphore(const MVKSwapchainSignaler& signaler, id<MTLCommandBuffer> mtlCmdBuff);
 	static void markAsTracked(const MVKSwapchainSignaler& signaler);
 	static void unmarkAsTracked(const MVKSwapchainSignaler& signaler);
+	void untrackAllSignalers();
 	void renderWatermark(id<MTLCommandBuffer> mtlCmdBuff);
 
 	id<CAMetalDrawable> _mtlDrawable;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 2cbb203..8b05bbd 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -1287,6 +1287,18 @@
 	if (signaler.fence) { signaler.fence->release(); }
 }
 
+// Untrack any signalers that are still tracking, releasing the fences and semaphores.
+void MVKPresentableSwapchainImage::untrackAllSignalers() {
+	lock_guard<mutex> lock(_availabilityLock);
+
+	if ( !_availability.isAvailable ) {
+		unmarkAsTracked(_preSignaler);
+		for (auto& sig : _availabilitySignalers) {
+			unmarkAsTracked(sig);
+		}
+	}
+}
+
 
 #pragma mark Metal
 
@@ -1345,9 +1357,12 @@
 		_availabilitySignalers.erase(sigIter);
 	}
 
-	// Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
+	// Ensure this image and the drawable are not destroyed while awaiting MTLCommandBuffer completion.
+	// We retain the drawable separately because new drawable might be acquired by this image by then.
 	retain();
+	[mtlDrwbl retain];
 	[mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
+		[mtlDrwbl release];
 		makeAvailable(signaler);
 		release();
 	}];
@@ -1405,8 +1420,11 @@
 	_preSignaler = MVKSwapchainSignaler{nullptr, nullptr, 0};
 }
 
+// Unsignaled signalers will exist if this image is acquired more than it is presented.
+// Ensure they are untracked so the fences and semaphores will be released.
 MVKPresentableSwapchainImage::~MVKPresentableSwapchainImage() {
 	releaseMetalDrawable();
+	untrackAllSignalers();
 }