From 3250569260fd3bf26de3d24e549e5813f5aa4287 Mon Sep 17 00:00:00 2001 From: Bill Hollings Date: Sat, 4 Feb 2023 11:27:16 -0500 Subject: [PATCH] Queue submissions retain wait semaphores until MTLCommandBuffer finishes. Add additional Vulkan error strings (unrelated). --- Docs/Whats_New.md | 1 + MoltenVK/MoltenVK/GPUObjects/MVKImage.mm | 12 ++- MoltenVK/MoltenVK/GPUObjects/MVKQueue.h | 6 +- MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm | 35 ++++++--- MoltenVK/MoltenVK/Utility/MVKFoundation.cpp | 84 +++++++++++---------- 5 files changed, 84 insertions(+), 54 deletions(-) diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md index 8ac6cf7b..19ec6ea7 100644 --- a/Docs/Whats_New.md +++ b/Docs/Whats_New.md @@ -25,6 +25,7 @@ Released TBA was sometimes incorrectly disabled due to a Metal driver bug. - Detect when size of surface has changed under the covers. - Change rounding of surface size provided by Metal from truncation to rounding-with-half-to-even. +- Queue submissions retain wait semaphores until `MTLCommandBuffer` finishes. MoltenVK 1.2.2 diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm index 7cded7b9..89631353 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm @@ -1338,15 +1338,21 @@ void MVKPresentableSwapchainImage::presentCAMetalDrawable(id m _availabilitySignalers.erase(sigIter); } - // 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. + // Ensure this image, the drawable, and the present fence are not destroyed while + // awaiting MTLCommandBuffer completion. We retain the drawable separately because + // a new drawable might be acquired by this image by then. retain(); [mtlDrwbl retain]; + auto* fence = presentInfo.fence; + if (fence) { fence->retain(); } [mtlCmdBuff addCompletedHandler: ^(id mcb) { [mtlDrwbl release]; makeAvailable(signaler); release(); - if (presentInfo.fence) { presentInfo.fence->signal(); } + if (fence) { + fence->signal(); + fence->release(); + } }]; signalPresentationSemaphore(signaler, mtlCmdBuff); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h index 54a68595..bad444f3 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.h @@ -189,6 +189,8 @@ public: protected: friend class MVKQueue; + virtual void finish() = 0; + MVKQueue* _queue; MVKSmallVector> _waitSemaphores; }; @@ -213,7 +215,7 @@ protected: id getActiveMTLCommandBuffer(); void setActiveMTLCommandBuffer(id mtlCmdBuff); void commitActiveMTLCommandBuffer(bool signalCompletion = false); - virtual void finish(); + void finish() override; virtual void submitCommandBuffers() {} MVKSmallVector> _signalSemaphores; @@ -270,7 +272,7 @@ public: const VkPresentInfoKHR* pPresentInfo); protected: - void stopAutoGPUCapture(); + void finish() override; MVKSmallVector _presentInfo; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm index 33c3246a..192de63c 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKQueue.mm @@ -290,11 +290,15 @@ MVKQueueSubmission::MVKQueueSubmission(MVKQueue* queue, _waitSemaphores.reserve(waitSemaphoreCount); for (uint32_t i = 0; i < waitSemaphoreCount; i++) { - _waitSemaphores.push_back(make_pair((MVKSemaphore*)pWaitSemaphores[i], (uint64_t)0)); + auto* sem4 = (MVKSemaphore*)pWaitSemaphores[i]; + sem4->retain(); + uint64_t sem4Val = 0; + _waitSemaphores.emplace_back(sem4, sem4Val); } } MVKQueueSubmission::~MVKQueueSubmission() { + for (auto s : _waitSemaphores) { s.first->release(); } _queue->release(); } @@ -530,33 +534,42 @@ void MVKQueueFullCommandBufferSubmission::finish() { #pragma mark - #pragma mark MVKQueuePresentSurfaceSubmission +// If the semaphores are encodable, wait on them by encoding them on the MTLCommandBuffer before presenting. +// If the semaphores are not encodable, wait on them inline after presenting. +// The semaphores know what to do. void MVKQueuePresentSurfaceSubmission::execute() { - // If the semaphores are encodable, wait on them by encoding them on the MTLCommandBuffer before presenting. - // If the semaphores are not encodable, wait on them inline after presenting. - // The semaphores know what to do. id mtlCmdBuff = _queue->getMTLCommandBuffer(kMVKCommandUseQueuePresent); [mtlCmdBuff enqueue]; for (auto& ws : _waitSemaphores) { ws.first->encodeWait(mtlCmdBuff, 0); } + + // Add completion handler that will destroy this submission only once the MTLCommandBuffer + // is finished with the resources retained here, including the wait semaphores. + // Completion handlers are also added in presentCAMetalDrawable() to retain the swapchain images. + [mtlCmdBuff addCompletedHandler: ^(id mcb) { + this->finish(); + }]; + for (int i = 0; i < _presentInfo.size(); i++ ) { _presentInfo[i].presentableImage->presentCAMetalDrawable(mtlCmdBuff, _presentInfo[i]); } + for (auto& ws : _waitSemaphores) { ws.first->encodeWait(nil, 0); } [mtlCmdBuff commit]; +} - // Let Xcode know the current frame is done, then start a new frame +void MVKQueuePresentSurfaceSubmission::finish() { + + // Let Xcode know the current frame is done, then start a new frame, + // and if auto GPU capture is active, and it's time to stop it, do so. auto cs = _queue->_submissionCaptureScope; cs->endScope(); cs->beginScope(); - stopAutoGPUCapture(); - - this->destroy(); -} - -void MVKQueuePresentSurfaceSubmission::stopAutoGPUCapture() { if (_queue->_queueFamily->getIndex() == mvkConfig().defaultGPUCaptureScopeQueueFamilyIndex && _queue->_index == mvkConfig().defaultGPUCaptureScopeQueueIndex) { _queue->getDevice()->stopAutoGPUCapture(MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_FRAME); } + + this->destroy(); } MVKQueuePresentSurfaceSubmission::MVKQueuePresentSurfaceSubmission(MVKQueue* queue, diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.cpp b/MoltenVK/MoltenVK/Utility/MVKFoundation.cpp index 0314f609..29ee115f 100644 --- a/MoltenVK/MoltenVK/Utility/MVKFoundation.cpp +++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.cpp @@ -24,49 +24,57 @@ const char* mvkVkResultName(VkResult vkResult) { switch (vkResult) { - CASE_STRINGIFY(VK_SUCCESS); - CASE_STRINGIFY(VK_NOT_READY); - CASE_STRINGIFY(VK_TIMEOUT); - CASE_STRINGIFY(VK_EVENT_SET); - CASE_STRINGIFY(VK_EVENT_RESET); - CASE_STRINGIFY(VK_INCOMPLETE); + CASE_STRINGIFY(VK_SUCCESS); + CASE_STRINGIFY(VK_NOT_READY); + CASE_STRINGIFY(VK_TIMEOUT); + CASE_STRINGIFY(VK_EVENT_SET); + CASE_STRINGIFY(VK_EVENT_RESET); + CASE_STRINGIFY(VK_INCOMPLETE); - CASE_STRINGIFY(VK_ERROR_OUT_OF_HOST_MEMORY); - CASE_STRINGIFY(VK_ERROR_OUT_OF_DEVICE_MEMORY); - CASE_STRINGIFY(VK_ERROR_INITIALIZATION_FAILED); - CASE_STRINGIFY(VK_ERROR_DEVICE_LOST); - CASE_STRINGIFY(VK_ERROR_MEMORY_MAP_FAILED); - CASE_STRINGIFY(VK_ERROR_LAYER_NOT_PRESENT); - CASE_STRINGIFY(VK_ERROR_EXTENSION_NOT_PRESENT); - CASE_STRINGIFY(VK_ERROR_FEATURE_NOT_PRESENT); - CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DRIVER); - CASE_STRINGIFY(VK_ERROR_TOO_MANY_OBJECTS); - CASE_STRINGIFY(VK_ERROR_FORMAT_NOT_SUPPORTED); - CASE_STRINGIFY(VK_ERROR_FRAGMENTED_POOL); + CASE_STRINGIFY(VK_ERROR_OUT_OF_HOST_MEMORY); + CASE_STRINGIFY(VK_ERROR_OUT_OF_DEVICE_MEMORY); + CASE_STRINGIFY(VK_ERROR_INITIALIZATION_FAILED); + CASE_STRINGIFY(VK_ERROR_DEVICE_LOST); + CASE_STRINGIFY(VK_ERROR_MEMORY_MAP_FAILED); + CASE_STRINGIFY(VK_ERROR_LAYER_NOT_PRESENT); + CASE_STRINGIFY(VK_ERROR_EXTENSION_NOT_PRESENT); + CASE_STRINGIFY(VK_ERROR_FEATURE_NOT_PRESENT); + CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DRIVER); + CASE_STRINGIFY(VK_ERROR_TOO_MANY_OBJECTS); + CASE_STRINGIFY(VK_ERROR_FORMAT_NOT_SUPPORTED); + CASE_STRINGIFY(VK_ERROR_FRAGMENTED_POOL); - CASE_STRINGIFY(VK_ERROR_UNKNOWN); - CASE_STRINGIFY(VK_ERROR_OUT_OF_POOL_MEMORY); - CASE_STRINGIFY(VK_ERROR_INVALID_EXTERNAL_HANDLE); - CASE_STRINGIFY(VK_ERROR_FRAGMENTATION); - CASE_STRINGIFY(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); + CASE_STRINGIFY(VK_ERROR_UNKNOWN); + CASE_STRINGIFY(VK_ERROR_OUT_OF_POOL_MEMORY); + CASE_STRINGIFY(VK_ERROR_INVALID_EXTERNAL_HANDLE); + CASE_STRINGIFY(VK_ERROR_FRAGMENTATION); + CASE_STRINGIFY(VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS); + CASE_STRINGIFY(VK_PIPELINE_COMPILE_REQUIRED); - CASE_STRINGIFY(VK_ERROR_SURFACE_LOST_KHR); - CASE_STRINGIFY(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); - CASE_STRINGIFY(VK_SUBOPTIMAL_KHR); - CASE_STRINGIFY(VK_ERROR_OUT_OF_DATE_KHR); - CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); + CASE_STRINGIFY(VK_ERROR_SURFACE_LOST_KHR); + CASE_STRINGIFY(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); + CASE_STRINGIFY(VK_SUBOPTIMAL_KHR); + CASE_STRINGIFY(VK_ERROR_OUT_OF_DATE_KHR); + CASE_STRINGIFY(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); - CASE_STRINGIFY(VK_ERROR_VALIDATION_FAILED_EXT); - CASE_STRINGIFY(VK_ERROR_INVALID_SHADER_NV); + CASE_STRINGIFY(VK_ERROR_VALIDATION_FAILED_EXT); + CASE_STRINGIFY(VK_ERROR_INVALID_SHADER_NV); + CASE_STRINGIFY(VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR); - CASE_STRINGIFY(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); - CASE_STRINGIFY(VK_ERROR_NOT_PERMITTED_EXT); - CASE_STRINGIFY(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); - CASE_STRINGIFY(VK_THREAD_IDLE_KHR); - CASE_STRINGIFY(VK_THREAD_DONE_KHR); - CASE_STRINGIFY(VK_OPERATION_DEFERRED_KHR); - CASE_STRINGIFY(VK_OPERATION_NOT_DEFERRED_KHR); - CASE_STRINGIFY(VK_PIPELINE_COMPILE_REQUIRED_EXT); + CASE_STRINGIFY(VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR); + CASE_STRINGIFY(VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR); + CASE_STRINGIFY(VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR); + CASE_STRINGIFY(VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR); + CASE_STRINGIFY(VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR); + + CASE_STRINGIFY(VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT); + CASE_STRINGIFY(VK_ERROR_NOT_PERMITTED_KHR); + CASE_STRINGIFY(VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT); + CASE_STRINGIFY(VK_THREAD_IDLE_KHR); + CASE_STRINGIFY(VK_THREAD_DONE_KHR); + CASE_STRINGIFY(VK_OPERATION_DEFERRED_KHR); + CASE_STRINGIFY(VK_OPERATION_NOT_DEFERRED_KHR); + CASE_STRINGIFY(VK_ERROR_COMPRESSION_EXHAUSTED_EXT); default: return "VK_UNKNOWN_VK_Result"; }