diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 745fe079..36fe3877 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -21,6 +21,8 @@ Released TBD - Use `VK_KHR_image_format_list` to disable `MTLTextureUsagePixelFormatView` if only swizzles or `sRGB` conversion will be used for image views, improving performance on *iOS* by allowing Metal to use lossless texture compression. +- Move *Metal* drawable presentation from `MTLCommandBuffer` to `MTLDrawable` + to improve performance and reduce blocking. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h index 8097a6ef..39863204 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h @@ -429,6 +429,14 @@ typedef struct MVKSwapchainImageAvailability { bool operator< (const MVKSwapchainImageAvailability& rhs) const; } MVKSwapchainImageAvailability; +/** VK_GOOGLE_display_timing extension info */ +typedef struct { + MVKPresentableSwapchainImage* presentableImage; + bool hasPresentTime; // Keep track of whether presentation included VK_GOOGLE_display_timing + uint32_t presentID; // VK_GOOGLE_display_timing presentID + uint64_t desiredPresentTime; // VK_GOOGLE_display_timing desired presentation time in nanoseconds +} MVKPresentTimingInfo; + /** Tracks a semaphore and fence for later signaling. */ typedef std::pair MVKSwapchainSignaler; @@ -440,15 +448,9 @@ public: #pragma mark Metal - /** - * Presents the contained drawable to the OS, releases the Metal drawable and its - * texture back to the Metal layer's pool, and makes the image memory available for new use. - * - * If mtlCmdBuff is not nil, the contained drawable is scheduled for presentation using - * the presentDrawable: method of the command buffer. If mtlCmdBuff is nil, the contained - * drawable is presented immediately using the present method of the drawable. - */ - void presentCAMetalDrawable(id mtlCmdBuff, bool hasPresentTime, uint32_t presentID, uint64_t desiredPresentTime); + /** Presents the contained drawable to the OS. */ + void presentCAMetalDrawable(id mtlCmdBuff, + MVKPresentTimingInfo presentTimingInfo); #pragma mark Construction @@ -465,6 +467,7 @@ protected: friend MVKSwapchain; id getCAMetalDrawable() override; + void presentCAMetalDrawable(MVKPresentTimingInfo presentTimingInfo); void releaseMetalDrawable(); MVKSwapchainImageAvailability getAvailability(); void makeAvailable(); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 4c5c35ea..c7c15b76 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -1287,52 +1287,53 @@ id MVKPresentableSwapchainImage::getCAMetalDrawable() { } // Present the drawable and make myself available only once the command buffer has completed. -void MVKPresentableSwapchainImage::presentCAMetalDrawable(id mtlCmdBuff, bool hasPresentTime, uint32_t presentID, uint64_t desiredPresentTime) { +void MVKPresentableSwapchainImage::presentCAMetalDrawable(id mtlCmdBuff, + MVKPresentTimingInfo presentTimingInfo) { + _swapchain->willPresentSurface(getMTLTexture(0), mtlCmdBuff); - NSString* scName = _swapchain->getDebugName(); - if (scName) { mvkPushDebugGroup(mtlCmdBuff, scName); } - if (!hasPresentTime) { - [mtlCmdBuff presentDrawable: getCAMetalDrawable()]; - } - else { - // Convert from nsecs to seconds - CFTimeInterval presentTimeSeconds = ( double ) desiredPresentTime * 1.0e-9; - [mtlCmdBuff presentDrawable: getCAMetalDrawable() atTime:(CFTimeInterval)presentTimeSeconds]; - } - if (scName) { mvkPopDebugGroup(mtlCmdBuff); } + [mtlCmdBuff addScheduledHandler: ^(id mcb) { + presentCAMetalDrawable(presentTimingInfo); + }]; - signalPresentationSemaphore(mtlCmdBuff); - - retain(); // Ensure this image is not destroyed while awaiting MTLCommandBuffer completion + // Ensure this image is not destroyed while awaiting MTLCommandBuffer completion + retain(); [mtlCmdBuff addCompletedHandler: ^(id mcb) { makeAvailable(); release(); }]; - - if (hasPresentTime) { + + signalPresentationSemaphore(mtlCmdBuff); +} + +// If MTLDrawable.presentedTime/addPresentedHandler isn't supported. +// Treat it as if the present happened when requested. +void MVKPresentableSwapchainImage::presentCAMetalDrawable(MVKPresentTimingInfo presentTimingInfo) { + + id mtlDrwbl = getCAMetalDrawable(); + + if (presentTimingInfo.hasPresentTime) { + + // Convert from nsecs to seconds for Metal + [mtlDrwbl presentAtTime: (double)presentTimingInfo.desiredPresentTime * 1.0e-9]; + #if MVK_OS_SIMULATOR - // If MTLDrawable.presentedTime/addPresentedHandler isn't supported, just treat it as if the - // present happened when requested - _swapchain->recordPresentTime(presentID, desiredPresentTime, desiredPresentTime); + _swapchain->recordPresentTime(presentTimingInfo); #else - if ([_mtlDrawable respondsToSelector: @selector(addPresentedHandler:)]) { - retain(); // Ensure this image is not destroyed while awaiting presentation - [_mtlDrawable addPresentedHandler: ^(id drawable) { - // Record the presentation time - CFTimeInterval presentedTimeSeconds = drawable.presentedTime; - uint64_t presentedTimeNanoseconds = (uint64_t)(presentedTimeSeconds * 1.0e9); - _swapchain->recordPresentTime(presentID, desiredPresentTime, presentedTimeNanoseconds); + if ([mtlDrwbl respondsToSelector: @selector(addPresentedHandler:)]) { + // Ensure this image is not destroyed while awaiting presentation + retain(); + [mtlDrwbl addPresentedHandler: ^(id drawable) { + _swapchain->recordPresentTime(presentTimingInfo, drawable.presentedTime * 1.0e9); release(); }]; } else { - // If MTLDrawable.presentedTime/addPresentedHandler isn't supported, just treat it as if the - // present happened when requested - _swapchain->recordPresentTime(presentID, desiredPresentTime, desiredPresentTime); + _swapchain->recordPresentTime(presentTimingInfo); } #endif + } else { + [mtlDrwbl present]; } - } // Resets the MTLTexture and CAMetalDrawable underlying this image. diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h index 61ff8387..0f798fc6 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h @@ -255,13 +255,6 @@ public: protected: id getMTLCommandBuffer(); - typedef struct { - MVKPresentableSwapchainImage* presentableImage; - bool hasPresentTime; // Keep track of whether present included VK_GOOGLE_display_timing - uint32_t presentID; // VK_GOOGLE_display_timing presentID - uint64_t desiredPresentTime; // VK_GOOGLE_display_timing desired presentation time in nanoseconds - } PresentInfo; - - MVKSmallVector _presentInfo; + MVKSmallVector _presentInfo; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm index 8eba6d8a..a53754c1 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm @@ -353,7 +353,7 @@ void MVKQueuePresentSurfaceSubmission::execute() { for (auto& ws : _waitSemaphores) { ws->encodeWait(mtlCmdBuff); } for (int i = 0; i < _presentInfo.size(); i++ ) { MVKPresentableSwapchainImage *img = _presentInfo[i].presentableImage; - img->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i].hasPresentTime, _presentInfo[i].presentID, _presentInfo[i].desiredPresentTime); + img->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i]); } for (auto& ws : _waitSemaphores) { ws->encodeWait(nil); } [mtlCmdBuff commit]; @@ -401,7 +401,7 @@ MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* que _presentInfo.reserve(scCnt); for (uint32_t scIdx = 0; scIdx < scCnt; scIdx++) { MVKSwapchain* mvkSC = (MVKSwapchain*)pPresentInfo->pSwapchains[scIdx]; - PresentInfo presentInfo = {}; + MVKPresentTimingInfo presentInfo = {}; presentInfo.presentableImage = mvkSC->getPresentableImage(pPresentInfo->pImageIndices[scIdx]); if ( pPresentTimesGOOGLE ) { presentInfo.hasPresentTime = true; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h index a1fc360f..e6e5ff11 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.h @@ -113,7 +113,7 @@ protected: void willPresentSurface(id mtlTexture, id mtlCmdBuff); void renderWatermark(id mtlTexture, id mtlCmdBuff); void markFrameInterval(); - void recordPresentTime(uint32_t presentID, uint64_t desiredPresentTime, uint64_t actualPresentTime); + void recordPresentTime(MVKPresentTimingInfo presentTimingInfo, uint64_t actualPresentTime = 0); CAMetalLayer* _mtlLayer; MVKWatermark* _licenseWatermark; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm index c2cbd32b..8928d263 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSwapchain.mm @@ -434,7 +434,7 @@ VkResult MVKSwapchain::getPastPresentationTiming(uint32_t *pCount, VkPastPresent return VK_SUCCESS; } -void MVKSwapchain::recordPresentTime(uint32_t presentID, uint64_t desiredPresentTime, uint64_t actualPresentTime) { +void MVKSwapchain::recordPresentTime(MVKPresentTimingInfo presentTimingInfo, uint64_t actualPresentTime) { std::lock_guard lock(_presentHistoryLock); if (_presentHistoryCount < kMaxPresentationHistory) { _presentHistoryCount++; @@ -442,8 +442,12 @@ void MVKSwapchain::recordPresentTime(uint32_t presentID, uint64_t desiredPresent } else { _presentHistoryHeadIndex = (_presentHistoryHeadIndex + 1) % kMaxPresentationHistory; } - _presentTimingHistory[_presentHistoryIndex].presentID = presentID; - _presentTimingHistory[_presentHistoryIndex].desiredPresentTime = desiredPresentTime; + + // If actual time not supplied, use desired time instead + if (actualPresentTime == 0) { actualPresentTime = presentTimingInfo.desiredPresentTime; } + + _presentTimingHistory[_presentHistoryIndex].presentID = presentTimingInfo.presentID; + _presentTimingHistory[_presentHistoryIndex].desiredPresentTime = presentTimingInfo.desiredPresentTime; _presentTimingHistory[_presentHistoryIndex].actualPresentTime = actualPresentTime; // These details are not available in Metal _presentTimingHistory[_presentHistoryIndex].earliestPresentTime = actualPresentTime;