Merge pull request #652 from billhollings/master
Fix race condition between swapchain image destruction and presentation completion callback
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 96ab18e..27cc42c 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -27,7 +27,8 @@
- Skip `SPIRV-Tools` build in Travis because Travis does not support the required Python 3.
- Separate `SPIRVToMSLConverterContext` into input config and output results.
- Fix pipeline cache lookups.
-- Doument that the functions in `vk_mvk_moltenvk.h` cannot be used with objects
+- Fix race condition between swapchain image destruction and presentation completion callback.
+- Document that the functions in `vk_mvk_moltenvk.h` cannot be used with objects
retrieved through the *Vulkan SDK Loader and Layers* framework.
- Update `VK_MVK_MOLTENVK_SPEC_VERSION` to 21.
- Update to latest SPIRV-Cross version:
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 96ebc14..cc977a5 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -1323,7 +1323,12 @@
// Signal the semaphore device-side.
_availabilitySignalers.front().first->encodeSignal(mtlCmdBuff);
}
- [mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) { makeAvailable(); }];
+
+ retain(); // Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
+ [mtlCmdBuff addCompletedHandler: ^(id<MTLCommandBuffer> mcb) {
+ makeAvailable();
+ release();
+ }];
} else {
[mtlDrawable present];
resetMetalSurface();
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
index 0a326dd..abf7863 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.h
@@ -21,7 +21,7 @@
#include "MVKBaseObject.h"
#include <vulkan/vk_icd.h>
#include <string>
-#include <mutex>
+#include <atomic>
#import <Foundation/NSString.h>
@@ -93,25 +93,18 @@
static MVKVulkanAPIObject* getMVKVulkanAPIObject(VkObjectType objType, uint64_t objectHandle);
/** Construct an empty instance. Declared here to support copy constructor. */
- MVKVulkanAPIObject() {}
+ MVKVulkanAPIObject() : _refCount(1) {}
- /**
- * Construct an instance from a copy. Default copy constructor disallowed due to mutex.
- * Copies start with fresh reference counts.
- */
- MVKVulkanAPIObject(const MVKVulkanAPIObject& other) {}
+ /** Default copy constructor disallowed due to mutex. Copy starts with fresh reference counts. */
+ MVKVulkanAPIObject(const MVKVulkanAPIObject& other) : _refCount(1) {}
~MVKVulkanAPIObject() override;
protected:
- bool decrementRetainCount();
- bool markDestroyed();
virtual void propogateDebugName() = 0;
+ std::atomic<uint32_t> _refCount;
NSString* _debugName = nil;
- std::mutex _refLock;
- unsigned _refCount = 0;
- bool _isDestroyed = false;
};
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
index 6fd294d..6a390b1 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKVulkanAPIObject.mm
@@ -25,33 +25,15 @@
#pragma mark MVKVulkanAPIObject
void MVKVulkanAPIObject::retain() {
- lock_guard<mutex> lock(_refLock);
-
_refCount++;
}
void MVKVulkanAPIObject::release() {
- if (decrementRetainCount()) { destroy(); }
+ if (--_refCount == 0) { MVKConfigurableObject::destroy(); }
}
void MVKVulkanAPIObject::destroy() {
- if (markDestroyed()) { MVKConfigurableObject::destroy(); }
-}
-
-// Decrements the reference count, and returns whether it's time to destroy this object.
-bool MVKVulkanAPIObject::decrementRetainCount() {
- lock_guard<mutex> lock(_refLock);
-
- if (_refCount > 0) { _refCount--; }
- return (_isDestroyed && _refCount == 0);
-}
-
-// Marks this object as destroyed, and returns whether no references are left outstanding.
-bool MVKVulkanAPIObject::markDestroyed() {
- lock_guard<mutex> lock(_refLock);
-
- _isDestroyed = true;
- return _refCount == 0;
+ release();
}
VkResult MVKVulkanAPIObject::setDebugName(const char* pObjectName) {