Merge pull request #1092 from billhollings/drawable-release
Fix potential drawable present race conditions.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 3986320..aa1417a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -467,7 +467,7 @@
friend MVKSwapchain;
id<CAMetalDrawable> getCAMetalDrawable() override;
- void presentCAMetalDrawable(MVKPresentTimingInfo presentTimingInfo);
+ void presentCAMetalDrawable(id<CAMetalDrawable> mtlDrawable, MVKPresentTimingInfo presentTimingInfo);
void releaseMetalDrawable();
MVKSwapchainImageAvailability getAvailability();
void makeAvailable();
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index c7c15b7..d742c73 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -1292,8 +1292,10 @@
_swapchain->willPresentSurface(getMTLTexture(0), mtlCmdBuff);
+ // Get current drawable now. Don't retrieve in handler, because a new drawable might be acquired by then.
+ id<CAMetalDrawable> mtlDrwbl = getCAMetalDrawable();
[mtlCmdBuff addScheduledHandler: ^(id<MTLCommandBuffer> mcb) {
- presentCAMetalDrawable(presentTimingInfo);
+ presentCAMetalDrawable(mtlDrwbl, presentTimingInfo);
}];
// Ensure this image is not destroyed while awaiting MTLCommandBuffer completion
@@ -1306,24 +1308,21 @@
signalPresentationSemaphore(mtlCmdBuff);
}
-// If MTLDrawable.presentedTime/addPresentedHandler isn't supported.
-// Treat it as if the present happened when requested.
-void MVKPresentableSwapchainImage::presentCAMetalDrawable(MVKPresentTimingInfo presentTimingInfo) {
-
- id<CAMetalDrawable> mtlDrwbl = getCAMetalDrawable();
+void MVKPresentableSwapchainImage::presentCAMetalDrawable(id<CAMetalDrawable> mtlDrawable,
+ MVKPresentTimingInfo presentTimingInfo) {
if (presentTimingInfo.hasPresentTime) {
- // Convert from nsecs to seconds for Metal
- [mtlDrwbl presentAtTime: (double)presentTimingInfo.desiredPresentTime * 1.0e-9];
-
+ // Attach present handler before presenting to avoid race condition.
+ // If MTLDrawable.presentedTime/addPresentedHandler isn't supported,
+ // treat it as if the present happened when requested.
#if MVK_OS_SIMULATOR
_swapchain->recordPresentTime(presentTimingInfo);
#else
- if ([mtlDrwbl respondsToSelector: @selector(addPresentedHandler:)]) {
+ if ([mtlDrawable respondsToSelector: @selector(addPresentedHandler:)]) {
// Ensure this image is not destroyed while awaiting presentation
retain();
- [mtlDrwbl addPresentedHandler: ^(id<MTLDrawable> drawable) {
+ [mtlDrawable addPresentedHandler: ^(id<MTLDrawable> drawable) {
_swapchain->recordPresentTime(presentTimingInfo, drawable.presentedTime * 1.0e9);
release();
}];
@@ -1331,8 +1330,10 @@
_swapchain->recordPresentTime(presentTimingInfo);
}
#endif
+ // Convert from nsecs to seconds for Metal
+ [mtlDrawable presentAtTime: (double)presentTimingInfo.desiredPresentTime * 1.0e-9];
} else {
- [mtlDrwbl present];
+ [mtlDrawable present];
}
}