diff --git a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h index aa5a2077a..9a22a0ecc 100644 --- a/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h +++ b/MoltenVK/MoltenVK/API/vk_mvk_moltenvk.h @@ -48,7 +48,7 @@ extern "C" { */ #define MVK_VERSION_MAJOR 1 #define MVK_VERSION_MINOR 0 -#define MVK_VERSION_PATCH 8 +#define MVK_VERSION_PATCH 9 #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/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h index a7d856ad5..1cf4551dd 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h @@ -228,7 +228,7 @@ class MVKRenderPipelineCompiler : public MVKMetalCompiler { ~MVKRenderPipelineCompiler() override; protected: - void compileComplete(id pipelineState, NSError *error); + bool compileComplete(id pipelineState, NSError *error); id _mtlRenderPipelineState = nil; }; @@ -265,7 +265,7 @@ class MVKComputePipelineCompiler : public MVKMetalCompiler { ~MVKComputePipelineCompiler() override; protected: - void compileComplete(id pipelineState, NSError *error); + bool compileComplete(id pipelineState, NSError *error); id _mtlComputePipelineState = nil; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm index 1520f1013..e9c814a92 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm @@ -747,18 +747,19 @@ void serialize(Archive & archive, MVKShaderModuleKey& k) { compile(lock, ^{ [getMTLDevice() newRenderPipelineStateWithDescriptor: mtlRPLDesc completionHandler: ^(id ps, NSError* error) { - compileComplete(ps, error); + bool isLate = compileComplete(ps, error); + if (isLate) { destroy(); } }]; }); return [_mtlRenderPipelineState retain]; } -void MVKRenderPipelineCompiler::compileComplete(id mtlRenderPipelineState, NSError* compileError) { +bool MVKRenderPipelineCompiler::compileComplete(id mtlRenderPipelineState, NSError* compileError) { lock_guard lock(_completionLock); _mtlRenderPipelineState = [mtlRenderPipelineState retain]; // retained - endCompile(compileError); + return endCompile(compileError); } #pragma mark Construction @@ -777,18 +778,19 @@ void serialize(Archive & archive, MVKShaderModuleKey& k) { compile(lock, ^{ [getMTLDevice() newComputePipelineStateWithFunction: mtlFunction completionHandler: ^(id ps, NSError* error) { - compileComplete(ps, error); + bool isLate = compileComplete(ps, error); + if (isLate) { destroy(); } }]; }); return [_mtlComputePipelineState retain]; } -void MVKComputePipelineCompiler::compileComplete(id mtlComputePipelineState, NSError* compileError) { +bool MVKComputePipelineCompiler::compileComplete(id mtlComputePipelineState, NSError* compileError) { lock_guard lock(_completionLock); _mtlComputePipelineState = [mtlComputePipelineState retain]; // retained - endCompile(compileError); + return endCompile(compileError); } #pragma mark Construction diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h index 0d50e7e7d..66dbb51bc 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h @@ -212,7 +212,7 @@ class MVKShaderLibraryCompiler : public MVKMetalCompiler { ~MVKShaderLibraryCompiler() override; protected: - void compileComplete(id mtlLibrary, NSError *error); + bool compileComplete(id mtlLibrary, NSError *error); void handleError() override; id _mtlLibrary = nil; @@ -250,7 +250,7 @@ class MVKFunctionSpecializer : public MVKMetalCompiler { ~MVKFunctionSpecializer() override; protected: - void compileComplete(id mtlFunction, NSError *error); + bool compileComplete(id mtlFunction, NSError *error); id _mtlFunction = nil; }; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm index aa5f864eb..7bec627f4 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm @@ -350,7 +350,8 @@ static uint32_t getOffsetForConstantId(const VkSpecializationInfo* pSpecInfo, ui [getMTLDevice() newLibraryWithSource: mslSourceCode options: options completionHandler: ^(id mtlLib, NSError* error) { - compileComplete(mtlLib, error); + bool isLate = compileComplete(mtlLib, error); + if (isLate) { destroy(); } }]; }); @@ -366,11 +367,11 @@ static uint32_t getOffsetForConstantId(const VkSpecializationInfo* pSpecInfo, ui } } -void MVKShaderLibraryCompiler::compileComplete(id mtlLibrary, NSError* compileError) { +bool MVKShaderLibraryCompiler::compileComplete(id mtlLibrary, NSError* compileError) { lock_guard lock(_completionLock); _mtlLibrary = [mtlLibrary retain]; // retained - endCompile(compileError); + return endCompile(compileError); } #pragma mark Construction @@ -392,18 +393,19 @@ static uint32_t getOffsetForConstantId(const VkSpecializationInfo* pSpecInfo, ui [mtlLibrary newFunctionWithName: funcName constantValues: constantValues completionHandler: ^(id mtlFunc, NSError* error) { - compileComplete(mtlFunc, error); + bool isLate = compileComplete(mtlFunc, error); + if (isLate) { destroy(); } }]; }); return [_mtlFunction retain]; } -void MVKFunctionSpecializer::compileComplete(id mtlFunction, NSError* compileError) { +bool MVKFunctionSpecializer::compileComplete(id mtlFunction, NSError* compileError) { lock_guard lock(_completionLock); _mtlFunction = [mtlFunction retain]; // retained - endCompile(compileError); + return endCompile(compileError); } #pragma mark Construction diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm index 7e11c689b..8a33789e4 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSurface.mm @@ -19,6 +19,7 @@ #include "MVKSurface.h" #include "MVKInstance.h" #include "MVKFoundation.h" +#include "MVKOSExtensions.h" #pragma mark MVKSurface @@ -29,7 +30,8 @@ const Vk_PLATFORM_SurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator) { - CALayer* viewLayer = ((PLATFORM_VIEW_CLASS*)pCreateInfo->pView).layer; + __block CALayer* viewLayer = nil; + mvkDispatchToMainAndWait(^{ viewLayer = ((PLATFORM_VIEW_CLASS*)pCreateInfo->pView).layer; }); if ( [viewLayer isKindOfClass: [CAMetalLayer class]] ) { _mtlCAMetalLayer = (CAMetalLayer*)[viewLayer retain]; // retained } else { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h index a7b36eaac..9fc8b1300 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.h @@ -22,6 +22,7 @@ #include #include #include +#include class MVKFenceSitter; @@ -122,7 +123,8 @@ class MVKSignalable : public MVKBaseDeviceObject { MVKSignalable(MVKDevice* device) : MVKBaseDeviceObject(device) {} protected: - void maybeDestroy(); + bool decrementSignalerCount(); + bool markDestroyed(); std::mutex _signalerLock; uint32_t _signalerCount = 0; @@ -246,6 +248,7 @@ class MVKFenceSitter : public MVKBaseObject { void addUnsignaledFence(MVKFence* fence); void fenceSignaled(MVKFence* fence); + void getUnsignaledFences(std::vector& fences); std::mutex _lock; std::unordered_set _unsignaledFences; @@ -295,8 +298,8 @@ class MVKMetalCompiler : public MVKBaseDeviceObject { protected: void compile(std::unique_lock& lock, dispatch_block_t block); virtual void handleError(); - void endCompile(NSError* compileError); - void maybeDestroy(); + bool endCompile(NSError* compileError); + bool markDestroyed(); NSError* _compileError = nil; uint64_t _startTime = 0; diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm index 07321017f..7ed78dde9 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKSync.mm @@ -75,23 +75,27 @@ } void MVKSignalable::wasRemovedFromSignaler() { + if (decrementSignalerCount()) { destroy(); } +} + +// Decrements the signaler count, and returns whether it's time to destroy this object. +bool MVKSignalable::decrementSignalerCount() { lock_guard lock(_signalerLock); if (_signalerCount > 0) { _signalerCount--; } - maybeDestroy(); + return (_isDestroyed && _signalerCount == 0); } void MVKSignalable::destroy() { + if (markDestroyed()) { MVKBaseDeviceObject::destroy(); } +} + +// Marks this object as destroyed, and returns whether the compilation is complete. +bool MVKSignalable::markDestroyed() { lock_guard lock(_signalerLock); _isDestroyed = true; - maybeDestroy(); -} - -void MVKSignalable::maybeDestroy() { - if (_isDestroyed && _signalerCount == 0) { - MVKBaseDeviceObject::destroy(); - } + return _signalerCount == 0; } @@ -190,12 +194,25 @@ #pragma mark Construction MVKFenceSitter::~MVKFenceSitter() { - lock_guard lock(_lock); - for (auto& uf : _unsignaledFences) { + // Use copy of collection to avoid deadlocks with the fences if lock in place here when removing sitters + vector ufsCopy; + getUnsignaledFences(ufsCopy); + for (auto& uf : ufsCopy) { uf->removeSitter(this); } } +// Fills the vector with the collection of unsignaled fences +void MVKFenceSitter::getUnsignaledFences(vector& fences) { + fences.clear(); + fences.reserve(_unsignaledFences.size()); + + lock_guard lock(_lock); + for (auto& uf : _unsignaledFences) { + fences.push_back(uf); + } +} + #pragma mark - #pragma mark Support functions @@ -231,7 +248,7 @@ VkResult mvkWaitForFences(uint32_t fenceCount, // The thread dispatch is needed because even the sync portion of the async Metal compilation methods can take well // over a second to return when a compiler failure occurs! void MVKMetalCompiler::compile(unique_lock& lock, dispatch_block_t block) { - MVKAssert( _startTime == 0, "%s compile occurred already in this instance. Instances of %s should only be used for a single compile activity.", _compilerType.c_str(), className().c_str()); + MVKAssert( _startTime == 0, "%s compile occurred already in this instance. Instances of %s should only be used for a single compile activity.", _compilerType.c_str(), getClassName().c_str()); _startTime = _device->getPerformanceTimestamp(); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); @@ -254,25 +271,26 @@ VkResult mvkWaitForFences(uint32_t fenceCount, setConfigurationResult(mvkNotifyErrorWithText(VK_ERROR_INITIALIZATION_FAILED, "%s compile failed (error code %li):\n%s.", _compilerType.c_str(), (long)_compileError.code, _compileError.localizedDescription.UTF8String)); } -void MVKMetalCompiler::endCompile(NSError* compileError) { +// Returns whether the compilation came in late, after the compiler was destroyed. +bool MVKMetalCompiler::endCompile(NSError* compileError) { _compileError = [compileError retain]; // retained _isCompileDone = true; _blocker.notify_all(); - maybeDestroy(); + return _isDestroyed; } void MVKMetalCompiler::destroy() { + if (markDestroyed()) { MVKBaseDeviceObject::destroy(); } +} + +// Marks this object as destroyed, and returns whether the compilation is complete. +bool MVKMetalCompiler::markDestroyed() { lock_guard lock(_completionLock); _isDestroyed = true; - maybeDestroy(); + return _isCompileDone; } -void MVKMetalCompiler::maybeDestroy() { - if (_isDestroyed && _isCompileDone) { - MVKBaseDeviceObject::destroy(); - } -} #pragma mark Construction diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp b/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp index b0ecf16c9..0275acd6e 100644 --- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp +++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.cpp @@ -26,7 +26,7 @@ using namespace std; #pragma mark - #pragma mark MVKBaseObject -string MVKBaseObject::className() { +string MVKBaseObject::getClassName() { int status; char* demangled = abi::__cxa_demangle(typeid(*this).name(), 0, 0, &status); string clzName = demangled; diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h index 82fe29a13..683ba4b50 100644 --- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h +++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h @@ -35,7 +35,7 @@ class MVKBaseObject { public: /** Returns the name of the class of which this object is an instance. */ - std::string className(); + std::string getClassName(); /** Destroys this object. Default behaviour simply deletes it. Subclasses may override to delay deletion. */ virtual void destroy() { delete this; } diff --git a/MoltenVK/MoltenVK/Utility/MVKOSExtensions.h b/MoltenVK/MoltenVK/Utility/MVKOSExtensions.h index 16217c107..75f7d5ae0 100644 --- a/MoltenVK/MoltenVK/Utility/MVKOSExtensions.h +++ b/MoltenVK/MoltenVK/Utility/MVKOSExtensions.h @@ -71,3 +71,11 @@ uint64_t mvkRecommendedMaxWorkingSetSize(id mtlDevice); /** Populate the propertes with info about the GPU represented by the MTLDevice. */ void mvkPopulateGPUInfo(VkPhysicalDeviceProperties& devProps, id mtlDevice); +/** Ensures the block is executed on the main thread. */ +inline void mvkDispatchToMainAndWait(dispatch_block_t block) { + if (NSThread.isMainThread) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +}